43

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
  • 1,026
  • 3
  • 10
  • 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

8 Answers8

70

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
  • 333,147
  • 50
  • 533
  • 760
  • 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
  • 4,473
  • 1
  • 44
  • 33
4

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
  • 30,949
  • 17
  • 114
  • 217
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
  • 150,114
  • 66
  • 286
  • 303
  • 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
1

If you want an actual function pointer representing the constructor, not a functor or closure, you can create a universal factory template:

#include <iostream>

namespace {
    template <typename Type, typename... Args>
    Type create(Args... args)
    {
        return Type{std::forward<Args>(args)...};
    }
}

template <typename Type, typename... Args>
static constexpr auto constructor = &create<Type, Args...>;

struct Foo {
    Foo(double v){ std::cout << "Foo ctor " << v << std::endl; }
};

int main()
{
    auto z = constructor<Foo, double>(0.33);

    return 0;
}
Foo ctor 0.33

https://godbolt.org/z/qKGGGzjTs

  • This is similar to Michael Burr's answer, except that you don't have a static factory method per object and it allows any kind of constructor (not just default)
  • This is similar to bradgonesurfing's answer, except that you create one function pointer per constructor and not one functor per class representing all constructors for that class.

Even though this question is old and already has several very helpful answers, I am adding this additional suggestion because having one function pointer for every constructor of any class is useful when implementing a runtime type reflection system.

joergbrech
  • 2,056
  • 1
  • 5
  • 17
  • You'll probably want `std::forward` in `create`. This will break a few things, especially moves. On an unrelated note, that `double const&` should be just a `double`. – MSalters Nov 21 '22 at 10:52
  • Actually, I just removed perfect forwarding, because it isn't really compatible with function pointers: https://godbolt.org/z/Toq9Goah7, The type of the function pointer will be resolved to `Foo (* const&)(double&&)` which is not a universal reference anymore, but a plain rvalue reference. Definitely not what I want. See also https://stackoverflow.com/questions/72174838/indirect-perfect-forwarding-via-function-pointer. Can you give an example that breks without perfect forwading? – joergbrech Nov 21 '22 at 11:08
  • You are right about changing `double const&` to `double` though, I will edit the anwer accordingly. – joergbrech Nov 21 '22 at 11:37
  • Ok, so it breaks if the constructor actually expects an rvalue reference. I noticed that the code works as expected, if we `std::forward` the arguments, but don't use a univeral reference for the argument: https://godbolt.org/z/6a96brGfW. I will edit the answer accordingly. – joergbrech Nov 21 '22 at 12:03
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
  • 16,883
  • 1
  • 35
  • 44
  • 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
  • 639
  • 4
  • 7