40

I'm working on implementing a reflection mechanism in C++. All objects within my code are a subclass of Object(my own generic type) that contain a static member datum of type Class.

class Class{
public:
   Class(const std::string &n, Object *(*c)());
protected:
   std::string name;     // Name for subclass
   Object *(*create)();  // Pointer to creation function for subclass
};

For any subclass of Object with a static Class member datum, I want to be able to initialize 'create' with a pointer to the constructor of that subclass.

Kareem
  • 986
  • 3
  • 9
  • 15
  • Although this is 6 years after the fact - you should give a lot of thought of whether you really want to implement your own reflection mechanisn. First consider settling for compile-time 'reflection' using templates, type_traits and the SFINAE principle; then try one of the existing C++ reflection libraries; and only then would I consider having a go at it myself. – einpoklum Dec 15 '15 at 21:41

7 Answers7

68

You cannot take the address of a constructor (C++98 Standard 12.1/12 Constructors - "12.1-12 Constructors - "The address of a constructor shall not be taken.")

Your best bet is to have a factory function/method that creates the Object and pass the address of the factory:

class Object;

class Class{
public:
   Class(const std::string &n, Object *(*c)()) : name(n), create(c) {};
protected:
   std::string name;     // Name for subclass
   Object *(*create)();  // Pointer to creation function for subclass
};

class Object {};

Object* ObjectFactory()
{
    return new Object;
}



int main(int argc, char**argv)
{
    Class foo( "myFoo", ObjectFactory);

    return 0;
}
Michael Burr
  • 321,763
  • 49
  • 514
  • 739
  • 7
    Making it a template will make it actually return "Class": template Object* ObjectFactory() { return new T; } .... Class foo("myFoo", &ObjectFactory); – Johannes Schaub - litb Jun 05 '09 at 14:40
  • Reason: https://stackoverflow.com/questions/7905272/why-is-taking-the-address-of-a-destructor-forbidden – Rufus Feb 18 '20 at 09:01
8

I encountered this same problem. My solution was a template function which called the constructor.

template<class T> MyClass* create()
{
    return new T;
}

To use this as a function pointer is simple:

MyClass* (*createMyClass)(void) = create<MyClass>;

And to get an instance of MyClass:

MyClass* myClass = createMyClass();
6

Lambda style:

[](){return new YourClass();}
kungfooman
  • 3,982
  • 1
  • 37
  • 28
3

Hmm, odd. create is a member variable i.e. only available in class instances but the intent of it seems to be creating an instance in the first place.

You cannot take the address of a constructor, but you can create static factory methods of your own and take the address of that.

laalto
  • 144,748
  • 64
  • 275
  • 293
  • 1
    laalto, When I construct the Class object I pass in a name and a creation function. My goal is to have a list of Classes to which I can refer to anywhere in my project. A pointer to a static member function would work. – Kareem Jun 05 '09 at 07:02
2

Using variadic templates you can create a wrapper which will turn the constructor into a functor.

#include <utility>

template <typename T>
struct BindConstructor{
    template<typename... Args>
    T operator()(Args&&...args)const{
        return T(std::forward<Args>(args)...);
    }
};


struct Foo {
    Foo(int a, int b):a(a),b(b){}
    int a;
    int b;
};

template <typename Fn>
auto Bar(Fn f){
    return f(10,20);
}

int main(){
    Foo foo = Bar(BindConstructor<Foo>());
}

https://godbolt.org/z/5W383McTc

bradgonesurfing
  • 29,669
  • 13
  • 106
  • 205
1

You can't use regular function pointers on methods, you have to use method pointers, which have bizarre syntax:

void (MyClass::*method_ptr)(int x, int y);
method_ptr = &MyClass::MyMethod;

This gives you a method pointer to MyClass's method - MyMethod. However this isn't a true pointer in that it's not an absolute memory address, it's basically an offset (more complicated than that due to virtual inheritance, but that stuff is implementation specific) into a class. So to use the method pointer, you have to supply it with a class, like this:

MyClass myclass;
myclass.*method_ptr(x, y);

or

MyClass *myclass = new MyClass;
myclass->*method_ptr(x, y);

Of course it should be obvious at this point that you can't use a method pointer to point to an objects constructor. In order to use a method pointer you need to have an instance of the class so it's constructor has already been called! So in your case Michael's Object Factory suggestion is probably the best way of doing it.

Niki Yoshiuchi
  • 15,975
  • 1
  • 33
  • 43
  • 9
    That's not correct. &MyClass::MyMethod gives you the address in memory of the actual executable code of MyMethod. Methods have an implicit first argument, the this pointer. You don't supply a method with a class, you supply it with an object (an instance of a class, e.g. a this pointer). Go look up the thiscall calling convention. – Rob K Jun 05 '09 at 19:35
  • 2
    I have to add that there is more magic involved in getting the location of the code to call if the method is virtual, but the existence of the methods in no way depends on the existence of any instance of the class. – Rob K Jun 05 '09 at 19:45
  • Ah, so you are right. Conceptually it makes little difference to the OP, either way a method pointer won't work unless an instance of the class exists. I have fixed my post to reflect this. – Niki Yoshiuchi Jun 05 '09 at 20:19
0

Using Qt, you can call a constructor with Qt reflection mechanisms (QMetaObject) if you declare the constructor as Q_INVOKABLE (nothing more to do than that).

class MyClass : public QObject {
   Q_OBJECT
public:
   Q_INVOKABLE MyClass(int foo);
   MyClass *cloningMySelf() {
     return metaObject()->newInstance(Q_ARG(int, 42));
   }
};

I'm not sure you will want to embed Qt just for that feature ;-) but maybe you would like to have a look on the way it does that.

http://doc.qt.io/qt-5/metaobjects.html#meta-object-system

Jim
  • 629
  • 4
  • 6