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