1// Copyright 2017 the V8 project authors. All rights reserved. 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 V8_BASE_EXPORT_TEMPLATE_H_ 6#define V8_BASE_EXPORT_TEMPLATE_H_ 7 8// Synopsis 9// 10// This header provides macros for using FOO_EXPORT macros with explicit 11// template instantiation declarations and definitions. 12// Generally, the FOO_EXPORT macros are used at declarations, 13// and GCC requires them to be used at explicit instantiation declarations, 14// but MSVC requires __declspec(dllexport) to be used at the explicit 15// instantiation definitions instead. 16 17// Usage 18// 19// In a header file, write: 20// 21// extern template class EXPORT_TEMPLATE_DECLARE(FOO_EXPORT) foo<bar>; 22// 23// In a source file, write: 24// 25// template class EXPORT_TEMPLATE_DEFINE(FOO_EXPORT) foo<bar>; 26 27// Implementation notes 28// 29// The implementation of this header uses some subtle macro semantics to 30// detect what the provided FOO_EXPORT value was defined as and then 31// to dispatch to appropriate macro definitions. Unfortunately, 32// MSVC's C preprocessor is rather non-compliant and requires special 33// care to make it work. 34// 35// Issue 1. 36// 37// #define F(x) 38// F() 39// 40// MSVC emits warning C4003 ("not enough actual parameters for macro 41// 'F'), even though it's a valid macro invocation. This affects the 42// macros below that take just an "export" parameter, because export 43// may be empty. 44// 45// As a workaround, we can add a dummy parameter and arguments: 46// 47// #define F(x,_) 48// F(,) 49// 50// Issue 2. 51// 52// #define F(x) G##x 53// #define Gj() ok 54// F(j()) 55// 56// The correct replacement for "F(j())" is "ok", but MSVC replaces it 57// with "Gj()". As a workaround, we can pass the result to an 58// identity macro to force MSVC to look for replacements again. (This 59// is why EXPORT_TEMPLATE_STYLE_3 exists.) 60 61#define EXPORT_TEMPLATE_DECLARE(export) \ 62 EXPORT_TEMPLATE_INVOKE(DECLARE, EXPORT_TEMPLATE_STYLE(export, ), export) 63#define EXPORT_TEMPLATE_DEFINE(export) \ 64 EXPORT_TEMPLATE_INVOKE(DEFINE, EXPORT_TEMPLATE_STYLE(export, ), export) 65 66// INVOKE is an internal helper macro to perform parameter replacements 67// and token pasting to chain invoke another macro. E.g., 68// EXPORT_TEMPLATE_INVOKE(DECLARE, DEFAULT, FOO_EXPORT) 69// will export to call 70// EXPORT_TEMPLATE_DECLARE_DEFAULT(FOO_EXPORT, ) 71// (but with FOO_EXPORT expanded too). 72#define EXPORT_TEMPLATE_INVOKE(which, style, export) \ 73 EXPORT_TEMPLATE_INVOKE_2(which, style, export) 74#define EXPORT_TEMPLATE_INVOKE_2(which, style, export) \ 75 EXPORT_TEMPLATE_##which##_##style(export, ) 76 77// Default style is to apply the FOO_EXPORT macro at declaration sites. 78#define EXPORT_TEMPLATE_DECLARE_DEFAULT(export, _) export 79#define EXPORT_TEMPLATE_DEFINE_DEFAULT(export, _) 80 81// The "MSVC hack" style is used when FOO_EXPORT is defined 82// as __declspec(dllexport), which MSVC requires to be used at 83// definition sites instead. 84#define EXPORT_TEMPLATE_DECLARE_MSVC_HACK(export, _) 85#define EXPORT_TEMPLATE_DEFINE_MSVC_HACK(export, _) export 86 87// EXPORT_TEMPLATE_STYLE is an internal helper macro that identifies which 88// export style needs to be used for the provided FOO_EXPORT macro definition. 89// "", "__attribute__(...)", and "__declspec(dllimport)" are mapped 90// to "DEFAULT"; while "__declspec(dllexport)" is mapped to "MSVC_HACK". 91// 92// It's implemented with token pasting to transform the __attribute__ and 93// __declspec annotations into macro invocations. E.g., if FOO_EXPORT is 94// defined as "__declspec(dllimport)", it undergoes the following sequence of 95// macro substitutions: 96// EXPORT_TEMPLATE_STYLE(FOO_EXPORT, ) 97// EXPORT_TEMPLATE_STYLE_2(__declspec(dllimport), ) 98// EXPORT_TEMPLATE_STYLE_3(EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport)) 99// EXPORT_TEMPLATE_STYLE_MATCH__declspec(dllimport) 100// EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport 101// DEFAULT 102#define EXPORT_TEMPLATE_STYLE(export, _) EXPORT_TEMPLATE_STYLE_2(export, ) 103#define EXPORT_TEMPLATE_STYLE_2(export, _) \ 104 EXPORT_TEMPLATE_STYLE_3( \ 105 EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA##export) 106#define EXPORT_TEMPLATE_STYLE_3(style) style 107 108// Internal helper macros for EXPORT_TEMPLATE_STYLE. 109// 110// XXX: C++ reserves all identifiers containing "__" for the implementation, 111// but "__attribute__" and "__declspec" already contain "__" and the token-paste 112// operator can only add characters; not remove them. To minimize the risk of 113// conflict with implementations, we include "foj3FJo5StF0OvIzl7oMxA" (a random 114// 128-bit string, encoded in Base64) in the macro name. 115#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA DEFAULT 116#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__attribute__(...) \ 117 DEFAULT 118#define EXPORT_TEMPLATE_STYLE_MATCH_foj3FJo5StF0OvIzl7oMxA__declspec(arg) \ 119 EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_##arg 120 121// Internal helper macros for EXPORT_TEMPLATE_STYLE. 122#define EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllexport MSVC_HACK 123#define EXPORT_TEMPLATE_STYLE_MATCH_DECLSPEC_dllimport DEFAULT 124 125// Sanity checks. 126// 127// EXPORT_TEMPLATE_TEST uses the same macro invocation pattern as 128// EXPORT_TEMPLATE_DECLARE and EXPORT_TEMPLATE_DEFINE do to check that they're 129// working correctly. When they're working correctly, the sequence of macro 130// replacements should go something like: 131// 132// EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport)); 133// 134// static_assert(EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT, 135// EXPORT_TEMPLATE_STYLE(__declspec(dllimport), ), 136// __declspec(dllimport)), "__declspec(dllimport)"); 137// 138// static_assert(EXPORT_TEMPLATE_INVOKE(TEST_DEFAULT, 139// DEFAULT, __declspec(dllimport)), "__declspec(dllimport)"); 140// 141// static_assert(EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT( 142// __declspec(dllimport)), "__declspec(dllimport)"); 143// 144// static_assert(true, "__declspec(dllimport)"); 145// 146// When they're not working correctly, a syntax error should occur instead. 147#define EXPORT_TEMPLATE_TEST(want, export) \ 148 static_assert(EXPORT_TEMPLATE_INVOKE( \ 149 TEST_##want, EXPORT_TEMPLATE_STYLE(export, ), export), \ 150 #export) 151#define EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT(...) true 152#define EXPORT_TEMPLATE_TEST_MSVC_HACK_MSVC_HACK(...) true 153 154EXPORT_TEMPLATE_TEST(DEFAULT, ); 155EXPORT_TEMPLATE_TEST(DEFAULT, __attribute__((visibility("default")))); 156EXPORT_TEMPLATE_TEST(MSVC_HACK, __declspec(dllexport)); 157EXPORT_TEMPLATE_TEST(DEFAULT, __declspec(dllimport)); 158 159#undef EXPORT_TEMPLATE_TEST 160#undef EXPORT_TEMPLATE_TEST_DEFAULT_DEFAULT 161#undef EXPORT_TEMPLATE_TEST_MSVC_HACK_MSVC_HACK 162 163#endif // V8_BASE_EXPORT_TEMPLATE_H_ 164