2009-06-05 7 views
28

Estoy trabajando en la implementación de un mecanismo de reflexión en C++. Todos los objetos dentro de mi código son una subclase de Object (mi propio tipo genérico) que contiene un dato de miembro estático de tipo Class.¿Cómo pasar un puntero a la función que apunta al constructor?

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 
}; 

Para cualquier subclase de Object con un dato miembro de la clase estática, quiero ser capaz de inicializar 'crear' con un puntero al constructor de la subclase.

+0

Aunque esto es de 6 años después del hecho - usted debe dar una gran cantidad de pensamiento de si realmente desea implementar su propio mecanismo de reflexión. En primer lugar, considere la posibilidad de establecer una "reflexión" en tiempo de compilación utilizando plantillas, type_traits y el principio SFINAE; luego prueba una de las bibliotecas de reflexión C++ existentes; y solo entonces consideraría intentarlo yo mismo. – einpoklum

Respuesta

52

No se puede tomar la dirección de un constructor (C++ estándar 98 12.1/12 - Constructores "12,1-12 Constructores - 'No se tomará el domicilio de un constructor')

Su mejor apuesta es para tener una función de fábrica/método que crea el Object y pasar la dirección de la fábrica:.

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; 
} 
+5

Convertirlo en una plantilla hará que realmente devuelva "Clase": plantilla Objeto * ObjectFactory() {return new T; } .... Clase foo ("myFoo", y ObjectFactory ); –

2

Hmm, impar create es una variable miembro es decir, sólo está disponible en las instancias de clase, pero la intención de que parece estar creando una instancia en primer lugar.

No puede tomar la dirección de un constructor, pero puede crear sus propios métodos de fábrica estáticos y tomar la dirección de eso.

+1

laalto, Cuando construyo el objeto Clase, paso un nombre y una función de creación. Mi objetivo es tener una lista de clases a las que pueda hacer referencia en cualquier parte de mi proyecto. Un puntero a una función de miembro estático funcionaría. – Kareem

+0

Y agrega otro direccionamiento indirecto: malo para el rendimiento. – Lothar

0

No puede utilizar punteros a funciones regulares sobre métodos, usted tiene que utilizar punteros método, que tienen una sintaxis extraña:

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

Esto le da un puntero método para el método de MiClase - MiMetodo. Sin embargo, este no es un verdadero puntero ya que no es una dirección de memoria absoluta, es básicamente un desplazamiento (más complicado que eso debido a la herencia virtual, pero eso es específico de la implementación) en una clase. Así que para usar el puntero del método, usted tiene que suministrar con una clase, así:

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

o

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

Por supuesto, debería ser obvio en este punto que no se puede utilizar una puntero de método para apuntar a un constructor de objetos. Para usar un puntero de método, necesitas tener una instancia de la clase, ¡así que ya se ha llamado al constructor! Entonces, en tu caso, la sugerencia de Michael's Object Factory es probablemente la mejor manera de hacerlo.

+6

Eso no es correcto. & MyClass :: MyMethod te da la dirección en memoria del código ejecutable real de MyMethod. Los métodos tienen un primer argumento implícito, este puntero. No proporciona un método con una clase, le proporciona un objeto (una instancia de una clase, por ejemplo, un puntero). Ve a buscar la llamada a la convención de llamadas. –

+1

Debo agregar que hay más magia involucrada en obtener la ubicación del código para llamar si el método es virtual, pero la existencia de los métodos de ninguna manera depende de la existencia de cualquier instancia de la clase. –

+0

Ah, entonces tienes razón. Conceptualmente, hace poca diferencia para el OP, de cualquier forma un puntero de método no funcionará a menos que exista una instancia de la clase. He arreglado mi publicación para reflejar esto. –

0

Usando Qt, puede llamar a un constructor con mecanismos de reflexión Qt (QMetaObject) si declara el constructor como Q_INVOKABLE (nada más que hacer que eso).

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

no estoy seguro de que tendrá que incorporar Qt sólo para esa característica ;-), pero tal vez le gustaría tener una mirada en la forma en que lo hace.

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

1

estilo Lambda:

[](){return new YourClass();} 
+1

Considere agregar más explicación/contexto para esto ya que hace que la respuesta sea más útil para los lectores futuros. – EJoshuaS

+0

Definitivamente, mencione que esta es una característica C++ 11 y posterior. – Kareem

0

me encontré con este mismo problema. Mi solución fue una función de plantilla que llamó al constructor.

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

Para utilizar esto como un puntero de función es simple:

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

Y para obtener una instancia de MyClass:

MyClass* myClass = createMyClass(); 
Cuestiones relacionadas