Pertanyaan Bagaimana cara mengeksekusi objek fungsi yang tidak beraturan dari tipe parameter yang berbeda secara berurutan?


Saya sedang merancang mekanisme yang akan mengeksekusi sekumpulan objek fungsi tak berurutan secara berurutan. Objek-objek fungsi ini ditugaskan selama waktu proses, dan masalahnya adalah: tipe parameter dari objek fungsi ini berbeda.

Yang ingin saya lakukan adalah sesuatu seperti ini:

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

Jika saya menulis kode C, saya tidak punya pilihan selain fungsi callback pointer yang mengambil void * sebagai parameter. Tapi sekarang saya bekerja dengan C ++, harus ada cara yang elegan untuk menghadapinya.

Cara terbaik yang dapat saya pikirkan sekarang adalah mendeklarasikan kelas template yang hampir mewarisi dari kelas pembungkus abstrak:

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

Pendekatan ini dapat sesuai dengan kebutuhan saya, tetapi akan mengalokasikan dan merilis template_pair untuk setiap entri. Saya tidak tahu apakah ini akan menyebabkan fragmen memori, karena prosedur ini akan disebut cukup sering selama proses.

Apakah ada cara yang lebih baik untuk melakukan ini?


5
2017-07-08 15:33


asal


Jawaban:


Karena tampaknya argumen untuk fungsi unary tetap pada saat Anda menambahkannya ke urutan, Anda bisa membuat urutan Anda menerima objek fungsi nol argumen menggunakan boost :: function, kemudian boost :: bind parameter yang diperlukan, mis.

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

Perhatikan bahwa Anda harus menentukan <void> sebagai parameter template ke boost::bind panggilan karena tidak dapat menyimpulkan jenis kembalinya objek fungsi secara otomatis. Atau, Anda dapat mengekspos typedef publik yang dipanggil result_type dalam definisi kelas yang menghindari ini, yaitu

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

4
2017-07-08 15:59



Apakah Anda benar-benar harus menyampaikan objek fungsi dan argumennya secara terpisah? Saya akan menggunakan boost::bind, dalam hal ini bisa terlihat seperti berikut:

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

Pada kasus ini command_sequence hanya akan menyimpan array objek fungsi.


7
2017-07-08 15:45