2014-11-21 79 views
0

所以我寫了一個遊戲引擎,利用數據驅動設計從xml文件實例化各種角色。我最近完成了對事件管理系統的編碼,並且所有事情都會觸發事件,並且處理程序正確地捕獲事件。考慮到我使用了成員函數指針和模板,這並非易事。我現在遇到的問題是,我希望能夠做這樣的事在xml文件:本機C++的數據驅動設計

<InputComponent> 
    <Event EventName="GainedFocus" OnEvent="Initialize"/> 
</InputComponent> 

的問題應該是很明顯的。 C++沒有反射形式,因此就我所知,不能從字符串「Initialize」中獲得Initialize函數的地址。這意味着對於稍微不同的輸入成分,我從類中派生出來並註冊特定於該實例的事件並添加函數。此外,應當知道,這是比我處理典型的輸入方式不同:

<InputComponent> 
    <Mapping Type="MousePress" Button=1 Command="RotateCamera"/> 
</InputComponent> 

在這種情況下的映射是特定於輸入部件的元件和命令本身是與工廠創建的對象。雖然我不能通過通用事件來做到這一點。命令是對不同對象執行完全相同操作的特定項目,而對象或組件本身通常需要以不同方式處理各個事件。有沒有人做過這樣的事情?真正好奇的是,某人可以如何去做這樣的事情,以便需要註冊到對象的事件不必被硬編碼到類中。

編輯:讓我這樣說吧。相同類型組件的不同實例需要對事件做出不同的反應。這將是特定於應用程序的,因此應該與引擎代碼分開(我不應該修改組件)。用戶應該能夠提供函數,然後可以在響應事件時調用函數。函數和事件都應該能夠綁定到XML中。我開始認爲這在非​​託管C++中可能是不可能的,因爲沒有任何形式的元數據可以根據同名字符串查找用戶提供的函數。

回答

0

您可以使用將字符串與函數指針關聯的映射。
或者如果函數簽名不同,可以使用帶有if-else-if梯子的工廠模式。

編輯1:例

// Typedef for event functions 
typedef void (*Event_Function_Pointer)(const Event& e); 

typedef std::map<std::string, Event_Function_Pointer> Event_Function_Container; 

//... 
Event_Function_Container events; 
events["Gained Focus"] = Initialize; 

還有有因爲文本和函數指針查找表的選項是不變的數據:

struct Event_Function_Entry 
{ 
    char * const * event_name; 
    Event_Function_Pointer event_function; 
}; 

Event_Function_Entry events[] = 
{ 
    {"Gained Focus", Initialize}, 
}; 
+0

是的,我認爲的全部問題來自於這樣一個事實,即我的委託是通過使用成員函數指針的模板完成的,這意味着實際的組件類本身必須更改爲添加事件處理程序。否則,我可以使用泛型函數指針,並像你說的那樣將它們添加到樹中。我認爲用成員函數指針來做這件事的唯一方法是讓組件包含通用的事件處理程序類。這樣我就不必更改組件本身,但可以編寫這些類的版本並將它們插入需要的地方。 – user3355098 2014-11-21 22:34:44

+0

另外問題是,我無法從沒有某種反射的xml中的字符串表示中找到Initialize函數的實際入口點。如果我能做到這一點,那麼使用動態數據結構來存儲和執行基於事件ID將很簡單。我想知道如何在XML中使用「Initialize」字符串,並讓我的XML解析代碼創建一個實際上可以找到具有相同名稱的函數的入口點的委託。 – user3355098 2014-11-21 23:09:50

0

你的部件均能從繼承相同的基地,這與您的命令將invoque的基本操作接口不相上下:

class Component { 
public: 
    virtual void initialize(); 
    virtual void rotate(int x, int y); 
    ... 
}; 

class Monster : public Component { 
    virtual void initialize(); // concrete implementation for a Monster 
    virtual void rotate(int x, int y); 
}; 

然後您可以考慮"command" design pattern。總體思路是:

class Command { // generic interface for commands 
    Component *receiver; 
public: 
    Command(Component *receiver); 
    virtual ~Command(); 
    virtual void execute();  

}; 

class InitializeCommand : Command { // a specific command 
public: 
    InitializeCommand (Component *receiver /* + additional command specific parameters */); // specific constructor with all needed parameters 
    void execute() { 
     // use the parameters and do the common operations 
     receiver->initialize(); // polymorphically call the object operations 
     // ... 
     } 
}; 

其餘取決於您的全球設計。

例如,你可以設計一個工廠,將創建基於被處理的事件的命令和執行這些:

if (event==...) { 
    // todo: find object pointed to by the object 
    Command c = myfactory_for_event (object, parameters); 
    c.execute(); 
} 

如果您的XML文件是爲了配置對象,那麼你讀文件,創建特定的命令,並將其存儲在一個事件的名稱到具體的命令相關聯的事件映射:

map<string,Command*> commands; 

在這種情況下,事件處理會是這樣的:

myobject["initialize"]->execute(); 
+0

我有命令和命令工廠都想通了。問題在於,例如輸入組件可能需要針對相同的事件做出不同的反應。例如,我可能想要在失去焦點時禁用某些功能,而其他人則需要在失去焦點時保持啓用狀態。爲了促進這一點,我不希望每次需要改變InputComponent類對事件的不同反應。我只想在xml中提供一個字符串,並在C++中運行,並將其綁定到該函數。有點像WPF那樣做。點擊=「OnCick」,然後你只需編寫OnClick函數和中提琴。 – user3355098 2014-11-21 23:39:37

0

所以沒有人知道是否有辦法做到這一點?顯然有黑客的方式。多個集成,多個工廠等。我只能響應Command對象完成的事件,但這似乎是一個奇怪的解決方案...因爲沒有可用的運行時信息,我必須將每個INDIVIDUAL函數封裝在它自己的類中。記憶,如果你問我......只是意識到聽起來有多怪異。基本上我的意思是每個功能我想映射到一個事件,我將不得不創建一個全新的類(與我已經用來映射鍵的命令模式相同)。如果我只是提供一個函數地址,而不是爲任何單獨的動作分配和釋放內存,但似乎沒有人能夠回答,會容易得多。