1 // Copyright 2023 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef PARTITION_ALLOC_PARTITION_ALLOC_BASE_EXPORT_TEMPLATE_H_ 6 #define PARTITION_ALLOC_PARTITION_ALLOC_BASE_EXPORT_TEMPLATE_H_ 7 8 // Synopsis 9 // 10 // This header provides macros for using 11 // PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) macros with explicit template 12 // instantiation declarations and definitions. Generally, the 13 // PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) macros are used at declarations, 14 // and GCC requires them to be used at explicit instantiation declarations, but 15 // MSVC requires __declspec(dllexport) to be used at the explicit instantiation 16 // definitions instead. 17 18 // Usage 19 // 20 // In a header file, write: 21 // 22 // extern template class 23 // PA_EXPORT_TEMPLATE_DECLARE(PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)) 24 // foo<bar>; 25 // 26 // In a source file, write: 27 // 28 // template class 29 // PA_EXPORT_TEMPLATE_DEFINE(PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)) 30 // foo<bar>; 31 32 // Implementation notes 33 // 34 // On Windows, when building when PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) 35 // expands to __declspec(dllexport)), we want the two lines to expand to: 36 // 37 // extern template class foo<bar>; 38 // template class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) foo<bar>; 39 // 40 // In all other cases (non-Windows, and Windows when 41 // PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) expands to 42 // __declspec(dllimport)), we want: 43 // 44 // extern template class PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) foo<bar>; 45 // template class foo<bar>; 46 // 47 // The implementation of this header uses some subtle macro semantics to 48 // detect what the provided PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) value was 49 // defined as and then to dispatch to appropriate macro definitions. 50 // Unfortunately, MSVC's C preprocessor is rather non-compliant and requires 51 // special care to make it work. 52 // 53 // Issue 1. 54 // 55 // #define F(x) 56 // F() 57 // 58 // MSVC emits warning C4003 ("not enough actual parameters for macro 59 // 'F'), even though it's a valid macro invocation. This affects the 60 // macros below that take just an "export" parameter, because export 61 // may be empty. 62 // 63 // As a workaround, we can add a dummy parameter and arguments: 64 // 65 // #define F(x,_) 66 // F(,) 67 // 68 // Issue 2. 69 // 70 // #define F(x) G##x 71 // #define Gj() ok 72 // F(j()) 73 // 74 // The correct replacement for "F(j())" is "ok", but MSVC replaces it 75 // with "Gj()". As a workaround, we can pass the result to an 76 // identity macro to force MSVC to look for replacements again. (This 77 // is why PA_EXPORT_TEMPLATE_STYLE_3 exists.) 78 79 #define PA_EXPORT_TEMPLATE_DECLARE(export) \ 80 PA_EXPORT_TEMPLATE_INVOKE(DECLARE, PA_EXPORT_TEMPLATE_STYLE(export, ), \ 81 export) // NOLINT 82 #define PA_EXPORT_TEMPLATE_DEFINE(export) \ 83 PA_EXPORT_TEMPLATE_INVOKE(DEFINE, PA_EXPORT_TEMPLATE_STYLE(export, ), \ 84 export) // NOLINT 85 86 // INVOKE is an internal helper macro to perform parameter replacements 87 // and token pasting to chain invoke another macro. E.g., 88 // PA_EXPORT_TEMPLATE_INVOKE(DECLARE, DEFAULT, PA_EXPORT) 89 // will export to call 90 // PA_EXPORT_TEMPLATE_DECLARE_DEFAULT(PA_EXPORT, ) 91 // (but with PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) expanded too). 92 #define PA_EXPORT_TEMPLATE_INVOKE(which, style, export) \ 93 PA_EXPORT_TEMPLATE_INVOKE_2(which, style, export) 94 #define PA_EXPORT_TEMPLATE_INVOKE_2(which, style, export) \ 95 PA_EXPORT_TEMPLATE_##which##_##style(export, ) 96 97 // Default style is to apply the PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) macro 98 // at declaration sites. 99 #define PA_EXPORT_TEMPLATE_DECLARE_DEFAULT(export, _) export 100 #define PA_EXPORT_TEMPLATE_DEFINE_DEFAULT(export, _) 101 102 // The "MSVC hack" style is used when PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) 103 // is defined as __declspec(dllexport), which MSVC requires to be used at 104 // definition sites instead. 105 #define PA_EXPORT_TEMPLATE_DECLARE_EXPORT_DLLEXPORT(export, _) 106 #define PA_EXPORT_TEMPLATE_DEFINE_EXPORT_DLLEXPORT(export, _) export 107 108 // PA_EXPORT_TEMPLATE_STYLE is an internal helper macro that identifies which 109 // export style needs to be used for the provided 110 // PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) macro definition. 111 // "", "__attribute__(...)", and "__declspec(dllimport)" are mapped 112 // to "DEFAULT"; while "__declspec(dllexport)" is mapped to "MSVC_HACK". 113 // 114 // It's implemented with token pasting to transform the __attribute__ and 115 // __declspec annotations into macro invocations. E.g., if 116 // PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE) is defined as 117 // "__declspec(dllimport)", it undergoes the following sequence of macro 118 // substitutions: 119 // PA_EXPORT_TEMPLATE_STYLE(PA_EXPORT,) 120 // PA_EXPORT_TEMPLATE_STYLE_2(__declspec(dllimport),) 121 // PA_EXPORT_TEMPLATE_STYLE_3( 122 // PA_EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport)) 123 // PA_EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport) 124 // PA_EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport 125 // DEFAULT 126 #define PA_EXPORT_TEMPLATE_STYLE(export, _) PA_EXPORT_TEMPLATE_STYLE_2(export, ) 127 #define PA_EXPORT_TEMPLATE_STYLE_2(export, _) \ 128 PA_EXPORT_TEMPLATE_STYLE_3( \ 129 PA_EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA##export) 130 #define PA_EXPORT_TEMPLATE_STYLE_3(style) style 131 132 // Internal helper macros for PA_EXPORT_TEMPLATE_STYLE. 133 // 134 // XXX: C++ reserves all identifiers containing "__" for the implementation, 135 // but "__attribute__" and "__declspec" already contain "__" and the token-paste 136 // operator can only add characters; not remove them. To minimize the risk of 137 // conflict with implementations, we include "foj3FJo5StF0OvIzl7oMxA" (a random 138 // 128-bit string, encoded in Base64) in the macro name. 139 #define PA_EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA DEFAULT 140 #define PA_EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__attribute__( \ 141 ...) \ 142 DEFAULT 143 #define PA_EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__declspec(arg) \ 144 PA_EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_##arg 145 146 // Internal helper macros for PA_EXPORT_TEMPLATE_STYLE. 147 #define PA_EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllexport EXPORT_DLLEXPORT 148 #define PA_EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport DEFAULT 149 150 // Sanity checks. 151 // 152 // PA_EXPORT_TEMPLATE_TEST uses the same macro invocation pattern as 153 // PA_EXPORT_TEMPLATE_DECLARE and PA_EXPORT_TEMPLATE_DEFINE do to check that 154 // they're working correctly. When they're working correctly, the sequence of 155 // macro replacements should go something like: 156 // 157 // PA_EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport)); 158 // 159 // static_assert(PA_EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT, 160 // PA_EXPORT_TEMPLATE_STYLE(__declspec(dllimport), ), 161 // __declspec(dllimport)), "__declspec(dllimport)"); 162 // 163 // static_assert(PA_EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT, 164 // DEFAULT, __declspec(dllimport)), "__declspec(dllimport)"); 165 // 166 // static_assert(PA_EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT( 167 // __declspec(dllimport)), "__declspec(dllimport)"); 168 // 169 // static_assert(true, "__declspec(dllimport)"); 170 // 171 // When they're not working correctly, a syntax error should occur instead. 172 #define PA_EXPORT_TEMPLATE_TEST(want, export) \ 173 static_assert(PA_EXPORT_TEMPLATE_INVOKE( \ 174 TEST_##want, PA_EXPORT_TEMPLATE_STYLE(export, ), export), \ 175 #export) // NOLINT 176 #define PA_EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(...) true 177 #define PA_EXPORT_TEMPLATE_TEST_EXPORT_DLLEXPORT_EXPORT_DLLEXPORT(...) true 178 179 PA_EXPORT_TEMPLATE_TEST(DEFAULT, ); // NOLINT 180 PA_EXPORT_TEMPLATE_TEST(DEFAULT, __attribute__((visibility("default")))); 181 PA_EXPORT_TEMPLATE_TEST(EXPORT_DLLEXPORT, __declspec(dllexport)); 182 PA_EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport)); 183 184 #undef PA_EXPORT_TEMPLATE_TEST 185 #undef PA_EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT 186 #undef PA_EXPORT_TEMPLATE_TEST_EXPORT_DLLEXPORT_EXPORT_DLLEXPORT 187 188 #endif // PARTITION_ALLOC_PARTITION_ALLOC_BASE_EXPORT_TEMPLATE_H_ 189