Reputation: 23
As far as I know it is safe to pass a struct across libraries, if the padding is compatible. So I wrote a test with a struct containing a single member, yet I still get a runtime error when reading the returned struct.
PluginInterface.h:
#ifndef PLUGIN_INTERFACE_H
#define PLUGIN_INTERFACE_H
typedef struct {
const char *name;
} DataStruct;
class PluginInterface {
public:
virtual DataStruct __cdecl getData() = 0;
};
#endif // PLUGIN_INTERFACE_H
Plugin.h:
#ifndef PLUGIN_H
#define PLUGIN_H
#include "PluginInterface.h"
class Plugin : public PluginInterface {
public:
DataStruct __cdecl getData();
};
#endif // PLUGIN_H
Plugin.cpp:
#include "Plugin.h"
DataStruct Plugin::getData() {
DataStruct data;
data.name = "name of plugin";
return data;
}
extern "C" __declspec(dllexport) PluginInterface* getInstance() {
return new Plugin;
}
main.cpp:
#include <iostream>
#include "windows.h"
#include "PluginInterface.h"
typedef PluginInterface* (*PluginCreator) ();
int main()
{
HINSTANCE handle = LoadLibrary("Plugin.dll");
if (handle == nullptr) {
std::cout << "Unable to open file!" << std::endl; return 0;
}
PluginCreator creator = (PluginCreator)GetProcAddress(handle, "getInstance");
if (creator == nullptr) {
std::cout << "Unable to load file!" << std::endl; return 0;
}
PluginInterface* plugin = creator();
if (plugin == nullptr) {
std::cout << "Unable to create plugin!" << std::endl; return 0;
}
DataStruct data = plugin->getData();
std::cout << "so far so good" << std::endl;
std::cout << data.name << std::endl; // Access violation
return 0;
}
I compiled the plugin with mingw, the executable with VS2012. I also tried to replace the const char* with an int, in which case I get a random integer. I know passing a struct with a single element doesn't make much sense, but I still wonder what the problem is.
Upvotes: 2
Views: 1168
Reputation: 2999
The problem is not with passing the struct by value, as this would work if the function returning the struct was a non-member declared extern "C"
. The problem is with the call to the virtual function getData()
. In your example, the VS2012 compiler generates the code to call the virtual function via a pointer to an object, but the object was created by code generated by a different compiler. This fails because the C++ ABI differs between the two compilers - which means that the underlying implementation of virtual functions is different.
The call to creator
succeeds because both compilers have the same underlying implemention of the C ABI. If you want to use C++ across library boundaries, you need to ensure that the libraries are compiled with the same C++ ABI version. Note that this can differ between different versions of the same compiler.
Upvotes: 1