I have a nice library for managing files that needs to return specific lists of strings. Since the only code I\'m ever going to use it with is going to be C++ (and Java but
The vector there uses the default std::allocator, which uses ::operator new for its allocation.
The problem is, when the vector is used in the DLL's context, it is compiled with that DLL's vector code, which knows about the ::operator new provided by that DLL.
The code in the EXE will try to use the EXE's ::operator new.
I bet the reason this works on Mac/Linux and not on Windows is because Windows requires all symbols be resolved at compile time.
For example, you may have seen Visual Studio give an error saying something like "Unresolved external symbol." It means "You told me this function named foo() exists, but I can't find it anywhere."
This is not the same as what Mac/Linux does. It requires all symbols be resolved at load time. What this means is you can compile a .so with a missing ::operator new. And your program can load in your .so and provide its ::operator new to the .so, allowing it to be resolved. By default, all symbols are exported in GCC, and so ::operator new will be exported by the program and potentially loaded in by your .so.
There is an interesting thing here, where Mac/Linux allows circular dependencies. The program could rely on a symbol that is provided by the .so, and that same .so might rely on a symbol provided by the program. Circular dependencies are a terrible thing and so I really like that the Windows method forces you to not do this.
But, that said, the real problem is that you are trying to use C++ objects across boundaries. That is definitely a mistake. It will ONLY work if the compiler used in the DLL and the EXE is the same, with the same settings. The 'extern "C"' may attempt to prevent name mangling (not sure what it does for non-C-types like std::vector). But it doesn't change the fact that the other side may have a totally different implementation of std::vector.
Generally speaking, if it is passed across boundaries like that, you want it to be in a plain old C type. If it is things like ints and simple types, things aren't so difficult. In your case, you probably want to pass an array of char*. Which means you still need to be careful about memory management.
The DLL/.so should manage its own memory. So the function might be like this:
Foo *bar = nullptr;
int barCount = 0;
getFoos( bar, &barCount );
// use your foos
releaseFoos(bar);
The drawback is that you will have extra code to convert things to C-sharable types at the boundaries. And sometimes this leaks into your implementation in order to speed up the implementation.
But the benefit is now people can use any language and any compiler version and any settings to write a DLL for you. And you are more careful about proper memory management and dependencies.
I know it is extra work. But that is the proper way to do things across boundaries.