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