Pregunta

typedef void (FunctionSet::* Function)();

class MyFunctionSet : public FunctionSet
{
protected:
    void addFunctions()
    {
        addFunction(Function(&MyFunctionSet::function1));
    }

    void function1()
    {
        // Do something.
    }
};

El método addFunction agrega la función a una lista en la clase base,
que luego se puede enumerar para llamar a todas las funciones.

¿Hay alguna forma de simplificar (menos trabajo de tipeo) la adición de funciones?

¿Fue útil?

Solución

Parece que asigna un puntero de función miembro a una función de la clase derivada a un puntero de función miembro a una función de la clase base. Bueno, eso está prohibido, porque abre un agujero en el sistema de tipos. Es una sorpresa (al menos para mí, la primera vez que escuché eso). Lea esta respuesta para por qué.

Para responder a su pregunta real, haría de addFunction una plantilla:

void addFunctions() {
    addFunction(&MyFunctionSet::function1);
}

Cambie addFunction en la clase base a esto:

template<typename Derived>
void addFunction(void(Derived::*f)()) {
    myFunctions.push_back(static_cast<Function>(f));
}

Mejor use static_cast , porque le dirá si Derivado en realidad no se deriva de FunctionSet .

Otros consejos

¿Puedes explicar lo que estás tratando de lograr haciendo esto?

Esto parece un diseño bastante malo, ¿no puede tener una clase abstracta (" interfaz de C ++ ") como " Computable " que tiene una función virtual pura1, subclase Computable para cada implementación, y luego MyFunctionSet mantiene un conjunto de Computable?

¿Hay alguna razón específica por la que está utilizando punteros de función?

Entonces, tiendo a estar en desacuerdo con Uri, algo.

Si está implementando el patrón Observador, lo que quiere es poder decir:

" Cuando el objeto X hace Y, quiero ejecutar el código Z " ;.

Seguir un enfoque basado puramente en una clase abstracta no es la mejor manera de hacerlo. Requerir una clase separada (especialmente en C ++) para cada controlador de eventos es excesivo. Si vienes de Java, así es como lo hacen todo. Sin embargo, Java tiene 2 características que hacen que esto sea un poco molesto: la clase anónima y la "instancia". clases de miembros Esto le permite definir múltiples controladores para múltiples eventos en la misma clase. Debe prefijar los métodos con " class {" ;, pero puede hacerlo.

C ++ no tiene clases anónimas ni '' instancia '' clases de miembros Esto hace que el uso de una clase abstracta para eventos sea mucho más engorroso si tiene varios controladores que necesitan compartir el estado. Debe hacer el equivalente a la generación de cierre a mano, lo que puede volverse bastante irritante con bastante rapidez.

.NET usa delegados para el manejo de eventos, que son básicamente punteros de función de tipo seguro. Hacen que el código para manejar eventos sea muy sencillo. Sin embargo, a diferencia de los punteros de función en C ++, un delegado unifica los punteros de función miembro y los punteros de función estática. Básicamente puede curry el " this " parámetro de cualquier función miembro, dejando un puntero de función que se parece a un puntero de función estática. Esto significa que el tipo de objeto que maneja el evento no forma parte de la interfaz de evento. Eso lo hace muy flexible.

No puede hacer eso directamente utilizando punteros de función miembro de C ++ porque el " this " type termina siendo parte del tipo de puntero de función, lo que limita sus controladores para que solo aparezcan dentro de la clase actual.

La mejor solución para C ++ es un híbrido entre los dos. Lo que quieres es una interfaz genérica como esta:

class EventHandler
{
public:
    virtual void Handle() = 0;
};

Y luego una implementación para funciones miembro como esta

template <class T>
class MemberFuncEventHandler : public EventHandler
{
public:


    MemberFuncEventHandler(T * pThis, void (T::*pFunc)()) : m_pThis(pThis), m_pFunc(pFunc)
    {
    }

    void Handle()
    {
        (m_pThis->*m_pFunc)();
    } 
private:
    T* m_pThis;
    void (T::*m_pFunc)();
};

template <class T>
EventHandler * Handler(T * pThis, void (T::*pFunc)())
{
    return new MemberFuncEventHandler<T>(pThis, pFunc);
}

También puede definir de manera similar una clase de controlador para métodos estáticos. Entonces puedes hacer cosas como esta:

Handlers += Handler(obj, &(c1::foo));
Handlers += Handler(obj, &(c2::bar));
Handlers += Handler(blaa); // for static methods...

Supongo que podría sobrecargar el operador de adición.

Si está intentando hacer un sistema de devolución de llamada o algo, recomendaría la biblioteca Boost.Signals. Básicamente, agrega funciones y llama al grupo de funciones por comando (como cualquier otra biblioteca de señales), pero también está diseñado para funcionar con Boost.Bind y Boost.Function, que son increíbles.

Ejemplo:

using boost::function
using boost::bind
using boost::signal

void some_other_func();    

Foo a;
Bar b;

signal<void()> sig;

sig.connect(bind(&Foo::foo,&a));
sig.connect(bind(&Bar::bar,&b));
sig.connect(some_other_func);

sig(); // calls -> a.foo(), b.bar(), some_other_func()

También admite bloqueo, gestión de conexión sofisticada y otras cosas interesantes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top