From 29c10fbf6e51d2a204f7c24c90d7ccec86cc659a Mon Sep 17 00:00:00 2001 From: denchat <19730041+denchat@users.noreply.github.com> Date: Thu, 2 May 2019 21:49:01 +0700 Subject: [PATCH] Fix DLL visibility of explicit instantiation "declaration" of internal::basic_data in header format.h and the explicit instantiation "definition" in format.cc (#1134) * Update format.cc As the explicit instantiation *declaration* of `internal::basic_data` in format.h, this explicit instantiation *definition* should mirror FMT_API also. * Mirror visibility of explicit instantiation declaration explicit instantiation declaration of internal::basic_data should mirror visibility of FMT_API * Eliminate `__declspec(dllexport)` designation on extern template internal::basic_data<> when `extern` affected during exporting phase. * Add `FMT_EXTERN_TEMPLATE_API` for designate DLL export `extern template` When exporting DLL, do not designate `__declspec(dllexport)` any template that has any explicit class template declaration a.k.a. `extern template`. Instead, designate `__declspec(dllexport)` at single point where we have explicit class template definition a.k.a. normal instantiation without `extern` Note: this is a c++11 feature. * Delete whole `FMT_USE_EXTERN_TEMPLATES` block and its condition 1. Remove whole `FMT_USE_EXTERN_TEMPLATES` block (trailing `FMT_UDL_TEMPLATE` block) ```` #ifndef FMT_USE_EXTERN_TEMPLATES # ifndef FMT_HEADER_ONLY # define FMT_USE_EXTERN_TEMPLATES \ ((FMT_CLANG_VERSION >= 209 && __cplusplus >= 201103L) || \ (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) # else # define FMT_USE_EXTERN_TEMPLATES 0 # endif #endif ```` 2. Delete `FMT_USE_EXTERN_TEMPLATES` condition, only condition, that trailing basic_data class template definition. ```` #if FMT_USE_EXTERN_TEMPLATES extern template struct basic_data; #endif ```` 3. Replace `FMT_API` with new `FMT_EXTERN_TEMPLATE_API` added in `core.h` for sake of extern template of `basic_data` * Add `#define FMT_EXTERN extern` only when not `FMT_HEADER_ONLY` * Replace `extern` on basic_data with the `FMT_EXTERN` condition in core.h * replace misspelled if !define() with ifndef --- include/fmt/core.h | 10 ++++++++++ include/fmt/format.h | 16 ++-------------- src/format.cc | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 8dc68e40..731fe965 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -189,11 +189,21 @@ # define FMT_API __declspec(dllexport) # elif defined(FMT_SHARED) # define FMT_API __declspec(dllimport) +# define FMT_EXTERN_TEMPLATE_API FMT_API # endif #endif #ifndef FMT_API # define FMT_API #endif +#ifndef FMT_EXTERN_TEMPLATE_API +# define FMT_EXTERN_TEMPLATE_API +#endif + +#ifndef FMT_HEADER_ONLY +# define FMT_EXTERN extern +#else +# define FMT_EXTERN +#endif #ifndef FMT_ASSERT # define FMT_ASSERT(condition, message) assert((condition) && message) diff --git a/include/fmt/format.h b/include/fmt/format.h index 8e101c45..52caad57 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -159,16 +159,6 @@ FMT_END_NAMESPACE # define FMT_UDL_TEMPLATE 0 #endif -#ifndef FMT_USE_EXTERN_TEMPLATES -# ifndef FMT_HEADER_ONLY -# define FMT_USE_EXTERN_TEMPLATES \ - ((FMT_CLANG_VERSION >= 209 && __cplusplus >= 201103L) || \ - (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) -# else -# define FMT_USE_EXTERN_TEMPLATES 0 -# endif -#endif - #if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || \ FMT_MSC_VER >= 1600 # define FMT_USE_TRAILING_RETURN 1 @@ -712,7 +702,7 @@ template struct int_traits { // Static data is placed in this class template to allow header-only // configuration. -template struct FMT_API basic_data { +template struct FMT_EXTERN_TEMPLATE_API basic_data { static const uint64_t POWERS_OF_10_64[]; static const uint32_t ZERO_OR_POWERS_OF_10_32[]; static const uint64_t ZERO_OR_POWERS_OF_10_64[]; @@ -726,9 +716,7 @@ template struct FMT_API basic_data { static const wchar_t WRESET_COLOR[5]; }; -#if FMT_USE_EXTERN_TEMPLATES -extern template struct basic_data; -#endif +FMT_EXTERN template struct basic_data; // This is a struct rather than a typedef to avoid shadowing warnings in gcc. struct data : basic_data<> {}; diff --git a/src/format.cc b/src/format.cc index 6b268bbe..b60b7e4f 100644 --- a/src/format.cc +++ b/src/format.cc @@ -8,7 +8,7 @@ #include "fmt/format-inl.h" FMT_BEGIN_NAMESPACE -template struct internal::basic_data; +template struct FMT_API internal::basic_data; // Workaround a bug in MSVC2013 that prevents instantiation of grisu_format. bool (*instantiate_grisu_format)(double, internal::buffer&, int, unsigned,