开发者

How to execute unary function objects of different parameter type in sequence?

I'm designing a mechanism that will execute a set of unary function objects in sequence. These function objects are assigned during runtime, and the problem is: the parameter type of these function objects are different.

What I want to do is something like this:

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();

If I'm writing C code, I have no choice but callback function pointer that takes void* as parameter. But I'm now working with C++, there should be an elegant way to deal with it.

The best way I can think now is declaring a template class that virtually inherit from a abstract wrapper class :

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 fun开发者_Go百科c_pair<FUNC,T>(obj,t) );
    }

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

This approach can fit my needs, but it would allocate and release template_pair for each entry. I've no idea whether this would cause memory fragment, since this procedure will be called quite frequently during the process.

Is there any better way to do this ?


Do you really need to pass a function object and its argument separately? I'd use boost::bind, in this case it could look like the following:

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)();

In this case command_sequence will just keep an array of the function objects.


Since it appears that the argument to the unary function is fixed at the time you add it to the sequence, you could make your sequence accept zero-argument function objects using boost::function, then boost::bind the required parameter, e.g.

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()) );

Note that you have to specify <void> as a template parameter to the boost::bind call since it can't deduce the return type of the function object automatically. Alternatively, you can expose a public typedef called result_type in the class definition which avoids this, i.e.

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜