1

I have a very basic implementation of reflection that includes a Type class which does object instantiation for the class it describes. Stripped down to the relevant parts, it looks like this:

Type.h:

class Plugin; // forward declaration

typedef std::unique_ptr<Plugin> PluginPtr;

namespace Reflection {

    class Type {
    public:
        explicit Type(PluginPtr(*)());
        PluginPtr CreateInstance();
    private:
        PluginPtr(*_createInstance_Handler)();
    };

}

Type.cpp:

Type::Type(PluginPtr(*createInstance_Handler)()) :
    _createInstance_Handler(createInstance_Handler) {}

PluginPtr CreateInstance() { return (*_createInstance_Handler)(); }

The actual instantiation logic is housed in the Plugin class (and also in each of its descendants):

Plugin.h:

class Plugin {
public:
    virtual ~Plugin();
    static const Reflection::Type Type;
private:
    static PluginPtr CreateInstance();

Plugin.cpp

Plugin::~Plugin() {}

const Reflection::Type Plugin::Type(CreateInstance);

PluginPtr Plugin::CreateInstance() { return PluginPtr(new Plugin); }

When I attempt to compile this, I get these errors (in Visual Studio 2013):

error C2027: use of undefined type 'Plugin'
error C2338: can't delete an incomplete type
warning C4150: deletion of pointer to incomplete type 'Plugin'; no destructor called

I dug around a bit, and apparently this is caused by the deleter of the std::unique_ptr (finding itself inside the class definition of the class it is operating on). I read somewhere that if I supply my own deleter, this problem goes away. So I redefined PluginPtr to this:

typedef std::unique_ptr<Plugin, PluginDeleter> PluginPtr

The (compilation) problem does indeed go away, but the question then, is can/should this PluginDeleter call ~Plugin() manually (to ensure that the plugin (and any derived object that the PluginPtr might be pointing to!) is properly destructed)? And where/how should I best declare/define it, so that I don't get the same problem with incomplete types?

(or is there a better way altogether?)

PS. Working on my source code now, I realize that there's an error in the above code. The last line in Type.cpp should read

    PluginPtr CreateInstance() { return (_createInstance_Handler)(); }
d7samurai
  • 3,086
  • 2
  • 30
  • 43
  • `use of undefined type 'Plugin'`: have you tried to use a complete type before? i.e. declare the whole class before you use an unique_ptr of it ? – Jaffa Jan 14 '14 at 10:38
  • Do you use `delete` in your PluginDeleter? – Jaffa Jan 14 '14 at 10:43
  • A custom deleter should not be necessary and wouldn't really help. You just need to make sure you have the full definition of `Plugin` available (`Plugin.h` included) wherever you destroy a PluginPtr. As a side note, I don't see how the static `Plugin::CreateInstance` makes any sense. – Sebastian Redl Jan 14 '14 at 10:46
  • I don't really do anything in it yet, because I'm unsure of what to do. I have sketched up one that looks like this: `struct PluginDeleter { void operator()(Plugin*); }`, with a definition somewhere after the `Plugin` class is defined that does this: `plugin->~Plugin();`.. but this is what I am asking about, really.. – d7samurai Jan 14 '14 at 10:46
  • @SebastianRedl the `Plugin::CreateInstance` is the only place that knows what class to instantiate. `Plugin` is a base class for a whole hierarchy of classes that all have a corresponding `Type`. So if you have a `Type` object and call `CreateInstance()` on it, the static `CreateInstance` of the corresponding class is called to instantiate the correct type. – d7samurai Jan 14 '14 at 10:49
  • But why does the base class, which presumably does nothing useful (and should be abstract) have its own `CreateInstance`? – Sebastian Redl Jan 14 '14 at 10:51
  • @SebastianRedl The custom deleter allows the deleter to be defined outside of the class - and it helps. Anyway, that workaround is what I ended up with after digging around for a solution. This is what I am asking about here - how to best solve it. – d7samurai Jan 14 '14 at 10:51
  • @SebastianRedl The base class must have a `Type` object associated with it, so that the reflection implementation can use that to assess inheritance relationships (like "IsAssignableFrom()") with the other classes. Therefore it cannot be made abstract. But you are correct in that in the actual implementation, it would not return an instance of itself). Look here for context: [“Poor Man's Reflection” (AKA bottom-up reflection) in C++](http://stackoverflow.com/questions/21057938/poor-mans-reflection-aka-bottom-up-reflection-in-c) – d7samurai Jan 14 '14 at 10:54
  • @SebastianRedl as to your first comment - it actually does seem the custom deleter is necessary in this case (aside from a whole different approach), as the default deleter apparently is not allowed to access the class destructor since the unique_ptr is created within the scope of that class and somehow it's not complete at that point. At least that's how I understood it. But then again, that's why I asked this question - to find out ;) – d7samurai Jan 14 '14 at 11:03

1 Answers1

2

The deleter of std::unique_ptr should delete the object, that is to say destroy it (as you can assume it should be), then free the memory used if needed.

If your custom deleter uses the delete operator, then you don't have to manually call the destructor as:

delete is an operator with a very specific behavior: An expression with the delete operator, first calls the appropriate destructor (for class types), and then calls function operator delete (i.e., this function) to release the storage.

If you create the pointer using statically allocated memory or a placement-new in a statically allocated memory or a memory you won't free until the application exit (for example), then you should not call the delete operator but you still have to destroy the object, thus you have to call the destructor of the object.

Jaffa
  • 12,442
  • 4
  • 49
  • 101