1

Suppose I have some template class A with a static member A::i:

template<typename T>
class A {
    public:
        static int i;
}

template<typename T>
int A<T>::i = 0;

Now suppose I want to share A<T>::i across DLL boundaries for arbitrary T. I found various solutions to related problems (e.g., here, and here). However, as far as I can tell, these solutions require tight constraints on the entire build chain (e.g., the main executable and DLL must be built with the same version of the same compiler). Otherwise, name mangling could result in the main executable and the DLL having two different symbols for A<T>::i (among other problems).

The canonical, "portable" (compiler-independent) solution to sharing C++ objects / data across DLL boundaries is don't. Instead, the C++ data is usually unpacked into C-friendly primitives, passed to the DLL, and then re-packed. But in this case, I'm trying to share static data for arbitrary template classes, some of which may not have even been template-instantiated in the main executable. The data itself is templated, so I see no simple way of unpacking it into C-friendly primitives.

Basically, I want a mapping between types and data that's consistent (at a symbol level for linking) across compilers. I don't think I could use C++'s RTTI to build that mapping because it's implementation-dependent.

Is there any solution to this problem, besides a) rolling my own portable introspection system, or b) imposing tight constraints on the build chain?

EDIT: The goal is to pass class-wide data belonging to 0+ arbitrary classes across DLL boundaries. Static variables of generic classes are the intuitive implementation of this kind of data, but if I have to discard compile-time parametric polymorphism to achieve this broader goal, that's perfectly acceptable.

Alexander Guyer
  • 2,063
  • 1
  • 14
  • 20
  • 1
    What does "portable" mean to you? DLLs are Windows-specific. That all instances of the static variable are shared between all translation units and libraries is the default standard-conforming behavior. If a dynamic linking scheme does not by default behave that way, then you'll need to do _something_ specific to that scheme to make it behave that way (e.g. add annotations in code). Dynamic linking is not standardized. – user17732522 Apr 29 '23 at 23:11
  • 4
    Also, if you are concerned about ABI issues between libraries compiled with different compiler versions, then nothing can really save you except completely routing all data exchange through a common ABI such as C. Name mangling would not be the only ABI concern. But compilers do have some policies on ABI, e.g. GCC/Clang following the Itanium C++ ABI. Standard libraries also have policies on ABI compatibility (e.g. https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html). So aside from incompatible bug fixes, it is possible to mix libraries compiled with different compilers carefully. – user17732522 Apr 29 '23 at 23:23
  • @user17732522 Ideally, in this case I'd define "portable" to refer to a solution that works when the main executable and DLL / shared library are compiled with different compilers (e.g., not necessarily both following the Itanium C++ ABI). As you say, I'd have to route the data through a common ABI like C. Is there some sort of design pattern that could be applied to route "static" (class-wide) data of arbitrary C++ template classes through the C ABI? Or is this fundamentally impossible? – Alexander Guyer Apr 30 '23 at 00:28
  • I've edited my question for clarity – Alexander Guyer Apr 30 '23 at 00:35
  • 3
    You cannot export a variable for every possible `T` - there are infinitely many. You could export specific instantiations (e.g. `A::i`) by spelling each one out with explicit instantiation syntax. But you seem to be hoping that, when main executable uses `A::i` for some class `C`, the variable will magically materialize in the DLL; that of course can't happen. What is the actual underlying problem you are trying to solve, that you think this will get you closer to? Frankly, it sounds like an [XY problem](https://xyproblem.info/) – Igor Tandetnik Apr 30 '23 at 01:37
  • 3
    There are no "template classes" in C++. There are *class templates*. The difference is subtle but important. Class templates are not classes. They are recipes to make classes. DLLs know nothing about them or their variables. You cannot compile a class template into executable code or export anything about it in any way. You can only do that with classes. `A` is a class template, `A` is a class, and "`A` for every possible `T`" is an infinite set of classes that cannot exist in a finite binary. – n. m. could be an AI Apr 30 '23 at 04:28
  • @IgorTandetnik It's obviously not impossible to export infinitely many values. You can export an array, can't you? Suppose C++'s RTTI behavior (e.g., typeid()::name()) was specified in the standard to give cross-compiler-consistent type identifiers. I could build a global map from type identifiers to data. I could then pass the global container. That wouldn't be the same as passing static data of generic classes, but it would solve the same problem. The underlying problem I'm trying to solve is passing [type maps](https://gpfault.net/posts/mapping-types-to-values.txt.html) to DLLs. – Alexander Guyer Apr 30 '23 at 15:57
  • @n.m. I think I understand the issue with my question. I'm trying to achieve the *effect* of sharing static generic data across DLL boundaries. I've edited my question for clarity. – Alexander Guyer Apr 30 '23 at 16:02
  • 1
    what about a Singleton pattern here? Make a global registry that maps types to the corresponding data – Mayfair Apr 30 '23 at 16:11
  • 1
    "I could build a global map from type identifiers to data." Where would this map be located - the EXE or the DLL? If the DLL, how would you know, at the time the DLL is built, which names the EXE would eventually want data for? If the EXE, then in what sense is this data exported from the DLL? What is the role of the DLL in this scheme to begin with? – Igor Tandetnik Apr 30 '23 at 18:10
  • 1
    Let's put it this way. Imagine that you somehow managed to implement the facility exactly the way you envision it. Can you show an example of how you plan to use it? You seem to envision two modules built with two different compilers that both link to this DLL and then do ... what exactly? I utterly fail to grasp the point of the exercise. – Igor Tandetnik Apr 30 '23 at 18:17
  • @IgorTandetnik Suppose I have a [type map](https://gpfault.net/posts/mapping-types-to-values.txt.html), constructed in the EXE, that maps an event type to a list of listeners. The type map can be used to subscribe to and publish events of arbitrary types. Suppose the EXE opens N DLLs, each of which might build some event listeners and / or publish some events. Then they all need a reference to the type map. The issue is that the type map (as implemented in the linked article) requires a non-portable (static, templated) mapping from types to identifiers (incrementing integers). – Alexander Guyer Apr 30 '23 at 21:21
  • @Mayfair "Make a global registry that maps types to the corresponding data." That's the burning question---I'm asking how to do that in a way that can cross DLL boundaries, particularly when different compilers were used for the EXE vs the DLL. Mapping types to data requires type identifiers of some sort. C++ RTTI is implementation-specific, so `typeid()` wouldn't work. Just exporting the data as global symbols doesn't work because there's no defined C++ ABI (e.g., the DLL and EXE could use different symbols for the global data) – Alexander Guyer May 01 '23 at 02:56
  • "Suppose I have a type map, constructed in the EXE, that maps an event type to a list of listeners." This has nothing to do with the question as it currently stands. You cannot map types in a C++ program, but you can map runtime representations of types, for example std::type_info or std::type_index. "C++ RTTI is implementation-specific, so typeid() wouldn't work". Absolutely nothing whatsoever, except "extern C" C-compatible functions and types, will work if you mix implementations. – n. m. could be an AI May 01 '23 at 11:43
  • @n.m. "Absolutely nothing whatsoever, except extern C C-compatible funtions and types, will work if you mix implementations." Yes. `typeid()::name()` is a `std::string`. You can of course pack it into a C string. The problem is that *which* string is returned by `typeid()::name()` is implementation-dependent. Even if I built a map from C strings to the relevant data type using only C-compatible types wrapped in "extern C", I wouldn't be able to associate the keys with the corresponding types in the DLL; the DLL might return different values for `typeid()::name()`. So *how do I do it?* – Alexander Guyer May 01 '23 at 14:23
  • So your real problem has nothing to do with the question as it stands now. I recommend asking a different question and explicitly stating your problem in it. – n. m. could be an AI May 01 '23 at 15:17
  • As far as I understand your real problem, you want a component (DLL or whatever) to advertise itself as a producer of events of a certain type, and another component to advertise itself as a consumer of events of that same type, and yet another component to be an event dispatcher that ties producers and consumers together, and you want all of this to be compiled by incompatible C++ compilers and still work together seamlessly. You want to use this kind of language in your question. Not "how to export templates from DLLs". – n. m. could be an AI May 01 '23 at 15:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/253413/discussion-between-n-m-and-alexander-guyer). – n. m. could be an AI May 01 '23 at 15:24

1 Answers1

1

You can't do it with a DLL, because you can only embed explicit, concrete, specific template instantations in a DLL. But since there are infinitely many possible template instantations, you can not embed all of them.

I see two realistic options:

  1. Somehow you know perfectly all possible instantations. Than you can instantiate that limited subset in the DLL. Either manually, or possible with the help of some macro magic.
  2. Alternatively, create a "portable" header-only library which supports a singleton. This is for example how boost does it.

Note that both options are not ideal and your question sounds like an XY problem.

André
  • 18,348
  • 6
  • 60
  • 74
  • Thanks. I've mentioned [type maps](https://gpfault.net/posts/mapping-types-to-values.txt.html) in the question's comments. Suppose I want to pass one to a DLL that might've been compiled with a different compiler from the EXE (this is the problem I'm trying to solve). I can't figure out how to unpack it to a C-friendly structure given that it relies on static, templated data. Is that an XY problem? Is there another pattern that might solve the same problem as type maps, but in a "portable" way? – Alexander Guyer Apr 30 '23 at 21:28