2011-07-08 11 views
5

Estoy diseñando un mecanismo que ejecutará un conjunto de objetos de funciones unarias en secuencia. Estos objetos de función se asignan durante el tiempo de ejecución, y el problema es: el tipo de parámetro de estos objetos de función es diferente.¿Cómo ejecutar objetos de funciones unarias de diferentes tipos de parámetros en secuencia?

Lo que quiero hacer es algo como esto:

class command_sequence { 
private: 

/* some kind of container */ 

public: 
    void add(FUNC_OBJ &func, PARAM val); 
    void run(void); 
}; 

class check_temperature { 
public: 
    void operator() (int celsius) { 
     if(celsius > 26) { 
      cooler.switch_on(); 
     } 
    } 
}; 

class log_usage { 
public: 
    void operator() (std::string username) { 
     username.append(" logged in"); 
     syslog(LOG_NOTICE,username.c_str()); 
    } 
}; 

command_sequence sequence; 
log_usage logger; 
check_temperature checker; 

sequence.add(logger, std::string("administrator")); 
sequence.add(checker, lobbyMeter.read_temperature()); 
sequence.add(logger, std::string("lecture")); 
sequence.add(checker, classroomMeter.read_temperature()); 
sequence.run(); 

Si estoy escribiendo código en C, no tengo más remedio devolución de llamada puntero de función que toma como parámetro void *. Pero ahora estoy trabajando con C++, debe haber una forma elegante de manejarlo.

La mejor manera que puedo pensar ahora está declarando una clase de plantilla que prácticamente heredan de una clase contenedora abstracta:

class command_sequence { 
private: 

    class runner { 
    public: 
     virtual void execute(void) = 0; 
    }; 

    template <class FUNC, typename T> class func_pair : public runner { 
    private: 
     FUNC &func; 
     T param; 
    public: 
     func_pair(FUNC &f, const T &t) : func(f),param(t) { } 
     void execute(void) { 
      func(param); 
     } 
    }; 

    std::vector<runner*> funcQueue; 

public: 

    template <class FUNC, typename T> void add(FUNC &obj, const T &t) { 
     funcQueue.push_back(new func_pair<FUNC,T>(obj,t)); 
    } 

    void run(void) { 
     std::vector<runner*>::iterator itr=funcQueue.begin(); 
     for(;itr!=funcQueue.end();++itr) { 
      (*itr)->execute(); 
      delete (*itr); 
     } 
    } 
}; 

Este enfoque puede adaptarse a mis necesidades, pero sería asignar y liberar template_pair para cada entrada. No tengo idea de si esto causaría fragmentos de memoria, ya que este procedimiento se llamará con bastante frecuencia durante el proceso.

¿Hay alguna forma mejor de hacerlo?

Respuesta

4

Dado que parece que el argumento de la función unaria se fija en el momento de añadirlo a la secuencia, usted podría hacer su secuencia de aceptar objetos de función sin argumentos usando boost::function, entonces boost::bind el parámetro requerido, por ejemplo,

class command_sequence { 
public: 
    void add(boost::function<void(void)> functor); 
}; 

/* ... as before ... */ 

log_usage logger; 
check_temperature checker; 

sequence.add(boost::bind<void>(logger, "administrator")); 
sequence.add(boost::bind<void>(checker, lobbymeter.read_temperature())); 

Tenga en cuenta que usted tiene que especificar <void> como un parámetro de plantilla a la llamada boost::bind ya que no puede deducir el tipo de retorno de la función del objeto de forma automática. Alternativamente, se puede exponer a un typedef público llamado result_type en la definición de clase que evita esto, es decir

class log_usage 
{ 
public: 
    typedef void result_type; 
    void operator() (const std::string& message) 
    { 
     // do stuff ... 
    } 
}; 

/* ... */ 

sequence.add(boost::bind(logger, "blah")); // will now compile 
+0

realmente impresionante. No tengo idea de que boost :: bind() puede aceptar objetos de función; pero tiene sentido. Esa parecía ser la respuesta que estoy buscando. Gracias. – RichardLiu

7

lo que realmente necesita para pasar un objeto de función y su argumento por separado? Que haría uso de boost::bind, en este caso podría ser similar al siguiente:

void check_temperature(int celsius) 
{ 
    if(celsius > 26) { 
     cooler.switch_on(); 
    } 
}; 

void log_usage(std::string username) 
{ 
    username.append(" logged in"); 
    syslog(LOG_NOTICE,username.c_str()); 
}; 

// keep actions 
typedef std::vector< boost::function<void()> > func_arr_t; 
func_arr_t actions; 
actions.push_back(boost::bind(&log_usage, "administrator")); 
actions.push_back(boost::bind(&check_temperature, lobbyMeter.read_temperature())); 
actions.push_back(boost::bind(&log_usage, "lecture")); 
actions.push_back(boost::bind(&check_temperature, classroomMeter.read_temperature())); 

// run all 
for (func_arr_t::const_iterator it = actions.begin(); it != actions.end(); ++it) 
    (*it)(); 

En este caso se command_sequence sólo mantener una matriz de los objetos de función.

+0

Gracias, esta solución es realmente un lenguaje de impulso :: función(), pero había necesidad de pasar un objeto instancia en mi caso. Un objeto de función se puede declarar como clase compuesta dentro de otra clase, y puede contener algún miembro privado de la clase de contenedor. Por ejemplo, en mis códigos reales, el objeto de función "check_temperature" también contiene un socket fileid al "cooler" de destino como variable miembro. – RichardLiu

Cuestiones relacionadas