i have the problem in writing a C++ framework, that users should have less overhead than possible to use it. Users can publish their work to the frameworks by creating a shared library that contains a class, which is derived by a frameworks' BaseClass and implementing an extern "C" createInstance()-method in order to return an instance its' derived class. So the framework can access the user class by calling the createInstance-Method through the shared library with dlsym().
cl开发者_StackOverflow中文版ass BaseClass{}
class UserClass : public BaseClass{}
extern "C"{
BaseClass* UserXcreateInstance(){
return new UserClass();
}
}
In framework:
typedef BaseClass* (*CreateInstance) ();
void* handle;
CreateInstance createInstance;
handle = dlopen( "libUserLibrary.so", RTLD_LAZY | RTLD_GLOBAL );
createInstance = reinterpret_cast <CreateInstance*> dlsym( handle, "UserXcreateInstance" );
BaseClass* userX = createInstance();
My Question: Is it possible to generate the UserXcreateInstance()-method, which is redundant in each user library, so that the user don`t have to think about it?
I thought it would be possible with templates+macros, but I haven't yet found a way to do this...
Another approach, I was thinking is directly call the constructor of any user class via dlsym and appropiate name mangling. (I know any namespace + class name from a config file) But I don`t think this a proper solution, especially a constructor call is not the same as a regular function call... but very interesting...
I can't imagine a way to create this automatically without any coding per part of the user. I can imagine ways to simplify it, perhaps using a macro:
#define OBJECT_CREATOR(X) \
extern "C" { \
BaseClass *UserXCreateInstance() {\
return new X(); \
}\
}
And the user just need to put on his cpp file:
OBJECT_CREATOR(UserClass);
I'll assume calling the user function via dlsym
is not an absolute requirement.
What you want is easy to achieve by using CRTP. A constructor of a static helper object then registers relevant data in a central repository. It should go like this:
template <typename UserClass>
class BaseClass
{
private:
class UserObjectFactory
{
UserObjectFactory()
{
std::string myname = typeid(UserClass).name();
CentralObjectFactory::instance()->register(this, myname);
}
BaseClass* XUserCreateInstance()
{
return new UserClass;
}
};
static UserObjectFactory factory;
};
Users code is simple:
class MyClass : public BaseClass<MyClass>
{
// whatever
};
Presumably, the CentralObjectFactory
instance contains some kind of (multi)map from std::string
to UserObjectFactory
.
myname
could be initialized to some uniquely generated string instead of typeid(UserClass).name()
, in order to avoid collisions.
If all you need is a single object of each user's class, you can make UserObjectFactory
create an instance of UserClass
and register it instead.
std::string myname = typeid(UserClass).name();
CentralObjectRepository::instance()->register(XUserCreateInstance(), myname);
Does this design fulfil your needs?
精彩评论