Home | History | Annotate | Download | only in base
      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 
    154 EXPORT_TEMPLATE_TEST(DEFAULT, );
    155 EXPORT_TEMPLATE_TEST(DEFAULT, __attribute__((visibility("default"))));
    156 EXPORT_TEMPLATE_TEST(MSVC_HACK, __declspec(dllexport));
    157 EXPORT_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