I have a problem using cereal with dynamic libraries on Windows (DLLs). I have read the documentation on the cereal site. However, all documentation and examples implicitly link DLLs, while I need to be able to explicitly link my DLLs. I made an example project to show you my setup and where I get stuck.
The project is divided into three projects, 2 DLLs and an executable. The first DLL (Object.dll) contains a Base class called Object. This DLL is implicitly linked to the second DLL (Script.dll), which implements a Derived class called Script. This DLL is then explicitly linked into the executable (Main.exe).
Object.dll:
Object.h
#pragma once
class __declspec(dllexport) Object
{
public:
Object();
virtual ~Object();
int so;
virtual void foo() = 0;
template<class Archive>
void serialize(Archive& ar)
{
ar(so);
}
};
Object.cpp is empty, so I've omitted it.
Script.dll:
Script.h
#pragma once
#include "Object.h"
class Script : public Object
{
public:
Script();
~Script();
void foo() override;
template<class Archive>
void serialize(Archive& ar)
{
ar(cereal::base_class<Object>(this));
}
};
Script.cpp
#include "Script.h"
Script::Script()
{
}
Script::~Script()
{
}
void Script::foo()
{
}
extern "C" __declspec(dllexport) Object* CreateObject()
{
return new Script();
}
extern "C" __declspec(dllexport) void DeleteObject(Object* obj)
{
delete obj;
obj = nullptr;
}
Main.exe
#include "Object.h"
#include "Windows.h"
#include "cereal/archives/xml.hpp"
#include "cereal/types/memory.hpp"
typedef Object* (__cdecl *CREATEFUNCTION)();
typedef void(__cdecl *DELTEFUNCTION)(Object*);
using namespace std;
int main()
{
std::string dll("DLL.dll");
HINSTANCE dllHandle = LoadLibraryA(dll.c_str());
if (dllHandle == nullptr)
{
cout << "Couldn't load the DLL of the script " << endl;
return 1;
}
CREATEFUNCTION CreateObject = reinterpret_cast<CREATEFUNCTION>(GetProcAddress(dllHandle, "CreateObject"));
if (CreateObject == nullptr)
{
cout << "Couldn't load the CreateObject() function" << endl;
return 1;
}
DELTEFUNCTION DeleteObject = reinterpret_cast<DELTEFUNCTION>(GetProcAddress(dllHandle, "DeleteObject"));
if (DeleteObject == nullptr)
{
cout << "Couldn't load the DeleteObject() function" << endl;
return 1;
}
std::shared_ptr<Object> obj = std::shared_ptr<Object>(CreateObject(), DeleteObject);
cereal::XMLOutputArchive oAr(std::cout);
oAr(obj);
return 0;
}
Of course, at the moment it throws the exception UNREGISTERED_POLYMORPHIC_EXCEPTION, because I don't register my Script class. And this is also my issue, according to what I have read, I should add the macro CEREAL_REGISTER_DYNAMIC_INIT to the source file where I call CEREAL_REGISTER_TYPE, which would be the Script.h file because I can't add it to the source file according to the Polymorphism documentation. I should also add CEREAL_FORCE_DYNAMIC_INIT to the header that gets included in the main application, but I don't include the Script.h file anywhere.
I have tried to put each macro in various places (without including it in the main.exe), but I have not got it to work. Does anybody know where these two function should go so that I can register my Script class and call its serialize function?