• Advertisement
Sign in to follow this  

C++ : Extend class in dll

This topic is 548 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello,

I have a program that defines a class

 

class GuiItem {

...

};

 

and then I extend this class for specific items

 

class TextBox : public GuiItem {

...

};

 

All works well, but I would like to implement a sort of extension system so that one can write it's own dll with the new specific GuiItem.

Then, just placing the dll in the program folder I would like to use this new item.

 

How can I do this?

 

Thank you.

Share this post


Link to post
Share on other sites
Advertisement

Typically this is achieved through some form of dynamic type information, be it using templates + typeid() or some other form of unique type identification

 

The basic idea consists of two classes. A "Manager" and a "Factory". The manager stores a collection of factories and associated type identification, and the factories know how to instantiate the type you need.

 

The following is a complete example using std::type_info. Note that you will need C++11 because of hash_code(). If you must use c++03 then you can replace hash_code() with name() and run that through your own hash function (or change the type in the container from size_t to std::string).

#include <typeinfo>
#include <map>
#include <string>
#include <iostream>


class Factory {
public:
    virtual void* Create() = 0;
};


template <class T>
class FactoryImpl : public Factory
{
    virtual void* Create() override
    {
        return new T();
    }
};


class Manager
{
public:
    template <class T>
    void RegisterObjectFactory()
    {
        factoryMap_[typeid(T).hash_code()] = new FactoryImpl<T>();
    }


    template <class T>
    T* CreateObject() const
    {
        auto factory = factoryMap_.find(typeid(T).hash_code());
        if(factory == factoryMap_.end())
            return NULL;
        return static_cast<T*>(factory->second->Create());
    }


    template <class T>
    void DestroyObject(T* object)
    {
        delete object;
    }


private:
    std::map<size_t, Factory*> factoryMap_;
};


// ----------------------------------------------------------------------------
// Example usage
// ----------------------------------------------------------------------------
class Test
{
public:
    void Greet() { std::cout << "it works!" << std::endl; }
};


int main()
{
    Manager manager;
    manager.RegisterObjectFactory<Test>();
    Test* test = manager.CreateObject<Test>();


    test->Greet();


    manager.DestroyObject(test);
}

One pitfall to look out for with factories is to make sure you delete the object in the same DLL you created it. This is why Manager::DestroyObject() is necessary. If you were to convert it from raw pointers to std::shared_ptr then this would no longer be necessary to do.

 

With this method you would need to pass the manager instance to your plugin DLLs when they start (typically achieved by defining your own start_plugin() function). Then your plugins can call Manager::RegisterObjectFactory<Whatever>() to register the types they require. This will enable the core application to start instantiating types that weren't originally there when compiling.

 

You might consider extending the example to make it possible to pass the actual class name to the manager, and not just the has code. For example:

manager.CreateObject("Test");

instead of

manager.CreateObject<Test>();

For this you would need to store typeid(T).name() in the corresponding factory class.

Edited by TheComet

Share this post


Link to post
Share on other sites

Look into virtual abstract base classes. The dll can return a pointer to a descendant in a factory function and the host can use it because it knows the base class. (EDIT: I see Wyrframe's link talks about them, so read that).

 

The main reason for this is to support dlls made in different languages and with different compilers than yours.

 

Do make sure you define calling conventions and data (struct) alignment. For calling conventions, cdecl and stdcall are good choices.

Edited by fredericvanmol

Share this post


Link to post
Share on other sites

in addition to already commented, you can use shared_ptr to wrap your types to avoid pitfalls between different runtimes in windows

 

Or implement your own wrapper and provide the type erasure yourself, I see this approach cleaner and less error-prone that relying on an API deletor

Share this post


Link to post
Share on other sites
Does this have to be C++?

I only ask because C++ does not have an ABI and inheriting across dlls can be a pain in the arse.

For something like this, C# would be much less painful.

Share this post


Link to post
Share on other sites

Look into virtual abstract base classes. The dll can return a pointer to a descendant in a factory function and the host can use it because it knows the base class. (EDIT: I see Wyrframe's link talks about them, so read that).
 
The main reason for this is to support dlls made in different languages and with different compilers than yours.
 
Do make sure you define calling conventions and data (struct) alignment. For calling conventions, cdecl and stdcall are good choices.


Is this, in fact, not what COM technology is all about? Although it includes additional features such as reference counting as well.

Share this post


Link to post
Share on other sites

 

Look into virtual abstract base classes. The dll can return a pointer to a descendant in a factory function and the host can use it because it knows the base class. (EDIT: I see Wyrframe's link talks about them, so read that).
 
The main reason for this is to support dlls made in different languages and with different compilers than yours.
 
Do make sure you define calling conventions and data (struct) alignment. For calling conventions, cdecl and stdcall are good choices.


Is this, in fact, not what COM technology is all about? Although it includes additional features such as reference counting as well.

 

 

The reason this works is in fact COM: the ABI for virtual abstract classes was standardized in Windows compilers to support COM interfaces. At least that's what I heard a number of years ago.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement