2

C++ で簡単なイベント システムを実装しています。システムは、文字列 (イベントの名前) に基づいてイベントを識別し、イベントが発生したときにコールバック関数のリストを呼び出すように設計されています。ここに簡単な青写真があります:

class EventManager:
    public:
        register_event(string name) // creates a new entry in the event table
        register_listener(string name, callback) // adds the callback to name's entry in the event table
        fire_event(string name// executes all functions in the event table entry for name
    private:
        hashmap<string, vector<function>> //the event table

私が現在苦労しているのは、関数のベクトルへの文字列のハッシュマップを作成し、それらの関数をループして実行する方法です。すべてのコールバックがそれ自体の関数型を認識していると想定できるため、すべてのコールバックには引数が(void* userdata, ...)あり、コールバックで va_list を管理します。

誰かがハッシュマップの作成方法を示す簡単なスニペットと、関数の呼び出しをループする方法を示すスニペットを提供できれば、それは役に立ちます。

編集役に立たないの答えを使用すると、次のエラーが発生します。

EventManager.h

#include <string>
#include <unordered_map>
#include <vector>

using namespace std;

typedef unordered_map<string, vector<function<void()>>> CallbackMap;

class EventManager{
public:
    EventManager(){
        callbacks = CallbackMap();
    }

    void EventManager::RegisterEvent(string const& name);
    void EventManager::RegisterListener(string const &name, function<void()> callback);
    void EventManager::FireEvent(string name);
private:
    CallbackMap callbacks;
};

EventManager.cpp

#include "EventManager.h"
#include <string>

using namespace std;

void EventManager::RegisterEvent(string const& name){
    callbacks[name] = NULL;
}

void EventManager::RegisterListener(string const &name, function<void()> callback)
{
    callbacks[name].push_back(callback);
}

bool EventManager::FireEvent(string name){
    auto event_callbacks = callbacks.find(event_name);
    if (event_callbacks == callbacks.end()){
        return false; // ?
    }

    // or std::for_each
    for (auto cb = event_callbacks->second.begin();
         cb != event_callbacks->second.end(); ++cb)
    {
        (*cb)();
    }
    return true;
}

ターミナル

$ g++ EventManager.cpp -std=c++0x
In file included from EventManager.cpp:1:0:
EventManager.h:7:38: error: ‘function’ was not declared in this scope
EventManager.h:7:52: error: template argument 1 is invalid
EventManager.h:7:52: error: template argument 2 is invalid
EventManager.h:7:53: error: template argument 2 is invalid
EventManager.h:7:53: error: template argument 5 is invalid
EventManager.h:7:55: error: expected unqualified-id before ‘&gt;’ token
EventManager.h:11:5: error: ‘CallbackMap’ does not name a type
EventManager.h:18:47: error: ‘function’ has not been declared
EventManager.h:18:55: error: expected ‘,’ or ‘...’ before ‘&lt;’ token
EventManager.h: In constructor ‘EventManager::EventManager()’:
EventManager.h:14:9: error: ‘callbacks’ was not declared in this scope
EventManager.h:14:33: error: ‘CallbackMap’ was not declared in this scope
EventManager.cpp: In member function ‘void EventManager::RegisterEvent(const string&)’:
EventManager.cpp:7:5: error: ‘callbacks’ was not declared in this scope
EventManager.cpp: At global scope:
EventManager.cpp:10:57: error: ‘function’ has not been declared
EventManager.cpp:10:65: error: expected ‘,’ or ‘...’ before ‘&lt;’ token
EventManager.cpp: In member function ‘void EventManager::RegisterListener(const string&, int)’:
EventManager.cpp:12:5: error: ‘callbacks’ was not declared in this scope
EventManager.cpp:12:31: error: ‘callback’ was not declared in this scope
EventManager.cpp: At global scope:
EventManager.cpp:15:6: error: prototype for ‘bool EventManager::FireEvent(std::string)’ does not match any in class ‘EventManager’
EventManager.h:19:10: error: candidate is: void EventManager::FireEvent(std::string)
4

4 に答える 4

3

関数のベクトルへの文字列のハッシュマップ ...

typedef unordered_map<string, vector<function<void()>>> CallbackMap;

言葉はすべてそこにありました。unordered_multimapベクトルのハッシュマップの代わりに使用できることに注意functionしてください。タイプはコールバックインターフェイスを反映しています(function<void(Event*)>たとえば、必要な場合があります)。

mapRam がコメントで指摘したように、multimap特にハッシュが必要ない場合 (または C++11 を持っていない場合、boost::unordered_map を使用することもできます) は、同等のツリーベースの連想コンテナーです。

...そして、関数の呼び出しをループする方法について...

bool EventManager::fire_event(string const& event_name)
{
    auto event_callbacks = callbacks.find(event_name);
    if (event_callbacks == callbacks.end()) return false; // ?

    // or std::for_each
    for (auto cb = event_callbacks->second.begin();
         cb != event_callbacks->second.end(); ++cb)
    {
        (*cb)();
    }
    return true;
}

ああ、登録は次のように簡単です。

void EventManager::register_listener(string const &name,
                                     function<void()> callback)
{
    callbacks[name].push_back(callback);
}

(イベント エントリを遅延してオンデマンドで作成できるようにしています)。

于 2012-10-02T18:26:51.143 に答える
0

何かのようなもの:?

typedef void(*ExampleFunction) (std::string str, int bar);

void foo(std::string str, int bar){
}
void foo2(std::string str, int bar){
}

int main(){
    std::map<std::string,std::vector<ExampleFunction> > f_map;
    std::vector<ExampleFunction> v_func;
v_func.push_back(foo);
v_func.push_back(foo2);

f_map["tiny"] = v_func;
f_map["tiny"][0]("Woo",1);
return 1;
}
于 2012-10-02T18:31:54.373 に答える
0

C ++ 11がオプション(VS2012、gccの最近のバージョン)の場合、C++ライブラリを介してハッシュマップを取得できます。

#include <unordered_map>

std::unordered_map<KeyType, ValueType> map {{x,y},{n,m1},{a,b}};
map[x] = y;
ValueType q = map[y];

等々。std::unordered_mapとの違いはstd::mapstd::map並べ替えられているため、下に赤黒木を使用していることです。これは実際にはニーズに対して十分に効率的である可能性があります。その場合は、C++03でも問題ありません。

私の例が示すように、MSVC'12がマップのデフォルトの割り当てをサポートしていないことを発見したことに注意してください。それが必要な場合は、boost ::assign:を使用してください。

#include <boost/assign.hpp>

std::unordered_map<K,V> map = boost::assign::map_list_of<K,V> (x,y)(a,b)(n,q);
于 2012-10-02T18:32:19.223 に答える
0

まず、コールバックとは何かを定義する必要があります。オプションは基本的に関数ポインタに要約されます。

typedef void (* EventCallback)(void * userdata, ...);

またはインターフェース:

class IEventCallback
{
public:
    virtual void callback(void * userdata, ...) = 0;
};

インターフェイスバージョンの方がいいです。このインターフェイスを実装するクラスのオブジェクトで関数ポインタをラップできます。逆に行くのははるかに難しいです。

で、マップとタイプを定義しますEventManager(反復を容易にするため)。

private:
    typedef std::vector<IEventCallback &> EventCallbackList_t;
    typedef std::map<std::string, EventCallbackList_t> EventMap_t;

    EventMap_t m_eventMap;

次に、実装はかなり簡単です。

void EventManager::register_event(std::string const & name)
{
    m_eventMap.insert(std::make_pair(name, EventCallbackList_t()));
}

void EventManager::register_listener(std::string const & name, IEventCallback & callback)
{
    EventMap_t::iterator event = m_eventMap.find(name);

    if (event == m_eventMap.end()) {
        throw "No such event.";
    }

    event->second.push_back(callback);
}

void EventManager::fire_event(std::string const & name, void * userdata, ...)
{
    EventMap_t::iterator event = m_eventMap.find(name);

    if (event == m_eventMap.end()) {
        throw "No such event.";
    }

    for (EventCallbackList_t i = event->second.begin(); i != event->second.end(); ++i) {
        i->callback(userdata, ...);
    }
}

この実装にはまだいくつかの問題があり、解決するのはあなたに任せます。

  • 並行性。複数のスレッドから呼び出される場合は、登録/起動操作が同期されていることを確認してください。
  • コールバックオブジェクトの所有権。共有ポインター型を使用して、それらの存続期間を管理することを検討してください。
于 2012-10-02T18:35:22.243 に答える