Skip to content

Commit cb0a856

Browse files
jll63claude
andcommitted
samples/windll: illustrate the dllvar technique with a plain int
shared.hpp shows the core trick: answer_dllvar has the same type name in every TU, but its base class varies (dllexport_tag in the owner, dllimport_tag in clients). A base class does not enter the mangled name, so static_answer<answer_dllvar, void>::value is ONE shared linker symbol while SFINAE selects the dllexport or dllimport specialization per TU. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d7dd003 commit cb0a856

3 files changed

Lines changed: 53 additions & 13 deletions

File tree

samples/windll/main.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@
33
// See accompanying file LICENSE_1_0.txt
44
// or copy at http://www.boost.org/LICENSE_1_0.txt)
55

6+
// Owner: answer_dllvar derives from dllexport_tag here.
67
#define WINDLL_OWNER
78
#include "shared.hpp"
89
#include <cassert>
910
#ifdef _WIN32
1011
#include <windows.h>
1112
#endif
1213

13-
int answer = 42;
14-
1514
int main() {
15+
answer() = 42;
1616
#ifdef _WIN32
1717
auto plugin = LoadLibraryA("windll_plugin.dll");
1818
assert(plugin);
19-
auto get_answer = (int (*)())GetProcAddress(plugin, "get_answer");
20-
assert(get_answer);
19+
auto get_answer = reinterpret_cast<int(*)()>(
20+
GetProcAddress(plugin, "get_answer"));
2121
assert(get_answer() == 42);
2222
FreeLibrary(plugin);
2323
#else

samples/windll/plugin.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See accompanying file LICENSE_1_0.txt
44
// or copy at http://www.boost.org/LICENSE_1_0.txt)
55

6+
// Client: answer_dllvar derives from dllimport_tag here (no WINDLL_OWNER).
67
#include "shared.hpp"
78

89
#ifdef _WIN32
@@ -11,6 +12,6 @@
1112
#define EXPORT
1213
#endif
1314

14-
extern "C" EXPORT int get_answer() {
15-
return answer;
16-
}
15+
// answer() returns a reference to the same static_answer<answer_dllvar, void>::value
16+
// that main.cpp owns — one shared linker symbol, bound via dllimport.
17+
extern "C" EXPORT int get_answer() { return answer(); }

samples/windll/shared.hpp

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,56 @@
44
// or copy at http://www.boost.org/LICENSE_1_0.txt)
55

66
#pragma once
7+
#include <type_traits>
78

8-
// The module that owns (exports) the variable defines WINDLL_OWNER before
9-
// including this header. All other modules import it.
9+
// Base classes used as SFINAE tags.
10+
struct dllexport_tag {};
11+
struct dllimport_tag {};
12+
13+
// answer_dllvar has the same *type name* in every translation unit, but its
14+
// *base class* varies: dllexport_tag in the owner, dllimport_tag in clients.
15+
// A base class does not appear in a class's mangled name, so
16+
// static_answer<answer_dllvar>::value resolves to the SAME linker symbol in
17+
// all modules — only the export/import attribute differs.
1018
#ifdef _WIN32
1119
# ifdef WINDLL_OWNER
12-
# define WINDLL_API __declspec(dllexport)
20+
struct answer_dllvar : dllexport_tag {};
1321
# else
14-
# define WINDLL_API __declspec(dllimport)
22+
struct answer_dllvar : dllimport_tag {};
1523
# endif
1624
#else
17-
# define WINDLL_API
25+
struct answer_dllvar {}; // no decoration needed on ELF platforms
26+
#endif
27+
28+
// Primary template: plain static variable (non-Windows and catch-all).
29+
template<class Tag, typename = void>
30+
struct static_answer {
31+
static int value;
32+
};
33+
template<class Tag, typename Enable>
34+
int static_answer<Tag, Enable>::value;
35+
36+
#ifdef _WIN32
37+
// Specialization for dllexport: selected when Tag derives from dllexport_tag.
38+
// enable_if_t<true> evaluates to void, so the instantiation is
39+
// static_answer<answer_dllvar, void> — same mangled name as the dllimport case.
40+
template<class Tag>
41+
struct static_answer<Tag,
42+
std::enable_if_t<std::is_base_of_v<dllexport_tag, Tag>>> {
43+
static __declspec(dllexport) int value;
44+
};
45+
template<class Tag>
46+
int static_answer<Tag,
47+
std::enable_if_t<std::is_base_of_v<dllexport_tag, Tag>>>::value;
48+
49+
// Specialization for dllimport: no definition — the symbol lives in the owner.
50+
template<class Tag>
51+
struct static_answer<Tag,
52+
std::enable_if_t<std::is_base_of_v<dllimport_tag, Tag>>> {
53+
static __declspec(dllimport) int value;
54+
};
1855
#endif
1956

20-
WINDLL_API extern int answer;
57+
// Accessor: always refers to static_answer<answer_dllvar, void>::value —
58+
// the one shared linker symbol — regardless of which specialization is active.
59+
inline int& answer() { return static_answer<answer_dllvar>::value; }

0 commit comments

Comments
 (0)