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