diff --git a/include/pybind11/chrono.h b/include/pybind11/chrono.h index d32b5b056..c4e81f572 100644 --- a/include/pybind11/chrono.h +++ b/include/pybind11/chrono.h @@ -11,9 +11,14 @@ #pragma once #include "pybind11.h" + +#include #include #include -#include +#include + +#include + #include // Backport the PyDateTime_DELTA functions from Python3.3 if required @@ -95,6 +100,22 @@ public: PYBIND11_TYPE_CASTER(type, _("datetime.timedelta")); }; +inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) { +#if defined(__STDC_WANT_LIB_EXT1__) || defined(_MSC_VER) + if (localtime_s(buf, time)) + return nullptr; + return buf; +#else + static std::mutex mtx; + std::lock_guard lock(mtx); + std::tm *tm_ptr = localtime(time); + if (tm_ptr != nullptr) { + *buf = *tm_ptr; + } + return tm_ptr; +#endif +} + // This is for casting times on the system clock into datetime.datetime instances template class type_caster> { public: @@ -162,16 +183,10 @@ public: // (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t) std::time_t tt = system_clock::to_time_t(time_point_cast(src - us)); - // std::localtime returns a pointer to a static internal std::tm object on success, - // or null pointer otherwise - std::tm *localtime_ptr = std::localtime(&tt); + std::tm localtime; + std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime); if (!localtime_ptr) throw cast_error("Unable to represent system_clock in local time"); - - // this function uses static memory so it's best to copy it out asap just in case - // otherwise other code that is using localtime may break this (not just python code) - std::tm localtime = *localtime_ptr; - return PyDateTime_FromDateAndTime(localtime.tm_year + 1900, localtime.tm_mon + 1, localtime.tm_mday, diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 52b019335..3faf3ea32 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -125,6 +125,11 @@ # endif #endif +// https://en.cppreference.com/w/c/chrono/localtime +#if defined(__STDC_LIB_EXT1__) && !defined(__STDC_WANT_LIB_EXT1__) +# define __STDC_WANT_LIB_EXT1__ +#endif + #include #include #include diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 0c25ca1a2..696e7cd37 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -19,7 +19,6 @@ # pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter # pragma warning(disable: 4127) // warning C4127: Conditional expression is constant # pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) -# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name # pragma warning(disable: 4522) // warning C4522: multiple assignment operators specified # pragma warning(disable: 4505) // warning C4505: 'PySlice_GetIndicesEx': unreferenced local function has been removed (PyPy only) #elif defined(__GNUG__) && !defined(__clang__) @@ -43,6 +42,8 @@ #include #include +#include + #if defined(__cpp_lib_launder) && !(defined(_MSC_VER) && (_MSC_VER < 1914)) # define PYBIND11_STD_LAUNDER std::launder # define PYBIND11_HAS_STD_LAUNDER 1 @@ -76,8 +77,13 @@ inline bool apply_exception_translators(std::forward_list& return false; } -PYBIND11_NAMESPACE_END(detail) +#if defined(_MSC_VER) +# define PYBIND11_COMPAT_STRDUP _strdup +#else +# define PYBIND11_COMPAT_STRDUP strdup +#endif +PYBIND11_NAMESPACE_END(detail) /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object class cpp_function : public function { @@ -276,7 +282,7 @@ protected: std::free(s); } char *operator()(const char *s) { - auto t = strdup(s); + auto t = PYBIND11_COMPAT_STRDUP(s); strings.push_back(t); return t; } @@ -520,7 +526,8 @@ protected: auto *func = (PyCFunctionObject *) m_ptr; std::free(const_cast(func->m_ml->ml_doc)); // Install docstring if it's non-empty (when at least one option is enabled) - func->m_ml->ml_doc = signatures.empty() ? nullptr : strdup(signatures.c_str()); + func->m_ml->ml_doc + = signatures.empty() ? nullptr : PYBIND11_COMPAT_STRDUP(signatures.c_str()); if (rec->is_method) { m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->scope.ptr()); @@ -1525,7 +1532,7 @@ public: detail::process_attributes::init(extra..., rec_fget); if (rec_fget->doc && rec_fget->doc != doc_prev) { free(doc_prev); - rec_fget->doc = strdup(rec_fget->doc); + rec_fget->doc = PYBIND11_COMPAT_STRDUP(rec_fget->doc); } } if (rec_fset) { @@ -1533,7 +1540,7 @@ public: detail::process_attributes::init(extra..., rec_fset); if (rec_fset->doc && rec_fset->doc != doc_prev) { free(doc_prev); - rec_fset->doc = strdup(rec_fset->doc); + rec_fset->doc = PYBIND11_COMPAT_STRDUP(rec_fset->doc); } if (! rec_active) rec_active = rec_fset; }