2014-02-27 35 views
1

我目前正在將遊戲引擎作爲大學模塊的一部分,其中一部分是消息處理系統。我希望改進講師的實施,所以對我的可能改變提出任何建議或批評是非常受歡迎的。關於消息處理系統的建議

當前消息結構

struct Message 
{ 
    Entity* entity; 
    std::string message; 
    void* data; 

    Message(Entity* entity, std::string message, void* data): 
     entity(entity), message(message), data(data) {} 
}; 

最初的實現不僅具有消息發送到實體,但我打算使用的「接口」,以允許遊戲引擎的任何部件,以便能夠接收消息。

class IMessageReceiver 
{ 
public: 
    virtual void handleMessage(const Message& message) {} 
}; 

struct Message 
{ 
    IMessageReceiver* receiver; 
    std::string message; 
    void* data; 
}; 
//examples 
class Entity : public IMessageReceiver { }; 
class Game : public IMessageReceiver { }; 

我對當前系統的一個問題是數據的void *(我只是不喜歡使用void *)。然後將它轉換爲它需要在handleMessage函數內部的類型,因爲我知道它應該接收哪些數據(取決於消息字符串) - 通常,它一直是3D矢量或實體,直到此時爲止,但如果我把它發送給實體以外的東西,那麼這可能會改變。

我想改變它以使用模板,而不是;然而,我不確定我會如何去做這件事。

template <typename T> 
struct Message 
{ 
    IMessageReceiver* receiver; 
    std::string message; 
    T* data; 
}; 

我對基本層面的模板有點熟悉,但我可能缺乏對模板技巧的一些更深入的瞭解。我知道我可以在創建新消息時將它傳遞給數據類型。

//example 
Entity* entity; 
vec3 someVec; 
Message<vec3> message(entity, "Fire", someVec); 
MessageHandler::sendMessage(message); 

但我怎麼會那麼寫起來IMessageReceiver中的handleMessage函數聲明,因爲不同的實現類將在發送給他們的消息不同的數據?

virtual void handleMessage(const Message<?>& message) {} 

我有做IMessageReceiver模板類,以及思想,所以當一個類從它繼承他們設定數據,他們收到的信息將是類型。

template <typename T> 
class IMessageReceiver 
{ 
public: 
    virtual void handleMessage(const Message<T>& message) {} 
}; 

class Entity : IMessageReceiver<vec3> {}; 

然而,這意味着,一個消息接收機只能接收一種類型的信息,但也可能有一種情況,其中我有可能的不同類型的數據發送到同一個接收機 - 例如,一個派生類實體通常收到一個vec3作爲消息的數據部分,但是新的場景要求它接收另一個實體。 我發現的另一個問題是,只有抽象基類Entity從IMessageReceiver繼承(不是每個實體的派生類,它們都接收不同類型的不同信息 - 或者可能),這就意味着使Entity成爲模板類。這可以通過傳遞來設置。但是我仍然會有這種僵化的系統,一些東西只能接收一種數據類型的消息。

也許void *是'最好'的方法,也許使用接口類不是。我不知道。

請隨時提出你可以想到的任何建設性的批評。我並不是要求某人爲我寫信,我只是尋求建議來實現我正在嘗試做的事情。基本上,我希望能夠使引擎的任何組件都能夠接收消息;在消息中發送的數據不是預定義的;並且最好不使用void *。

感謝您花時間閱讀本文。如果您需要任何其他信息,請讓我知道。

加文

+0

你不能只使用一個只有目標實體和消息的基類嗎?你可以從它得到你的特定消息?我可能會使用一個枚舉的消息ID,而不是一個字符串,但也許這只是我:) –

+0

@MartinJames那麼,你是否建議有一個基本的消息類,有接收消息和消息本身的對象(無論是枚舉,字符串還是別的東西 - string被用作演講者的實現,但它可能是另一件事我會改變),然後如果我想發送一個帶'vec3'的消息作爲數據,我會派生一個'Vec3Message '班?然後,對於我將作爲消息的數據部分發送的每種可能類型執行此操作? – LoneJock

+0

這就是我在想的,是的:) –

回答

0

虛擬功能需要在編譯時知道確切的簽名,所以你不能用「模板掛羊頭賣狗肉」,直接達到你想要的東西。

看看Boost.Any作爲void* Message::data的替代品。它仍然需要將其轉換爲正確的類型,但它是以類型安全的方式完成的。此外,這種方式的消息「擁有」的數據,所以你不必擔心有生之年的問題。

另外,代替使用IMessageReceiver,可以考慮使用std::function<void(const Message&)>。這樣,您可以將消息傳遞給具有void operator()(const Message&)的任何對象,並且您不必擔心層次結構的複雜性。

兩個boost::anystd::function內部使用稱爲類型擦除這需要繼承了接口,並允許你對待事物的價值觀,這往往使代碼更簡單推理的技術。

+0

謝謝你的回覆,我會給這兩個看看。我試圖給你的答案'upvote',但顯然這需要至少15個代表做這個。我不會將其標記爲現在的公認答案(如果您不介意),因爲我希望看看其他人是否有針對不同方法的建議,但我認爲這可能是公認的答案。再次感謝。 – LoneJock

0

你的模式表明發送者知道接收者並將消息直接發送給單個接收者,所以你可以讓發送者直接調用接收者的方法,並且該方法可能被「消息」特定的參數重載(這也解決了哪個對象「擁有」動態分配的消息數據的問題)。

製作消息傳遞系統而不是直接調用目標方法的一個常見原因是爲了減少對象之間的耦合。觀察者或發佈/訂閱模式通過使消息的發送者(大部分)不知道消息的接收者來減少耦合。在這種情況下,您需要一種抽象消息細節的方式,這似乎是問題所在。

您可以擁有消息的虛擬基類,並從中派生特定於消息的類型。然後handleMessage方法可以獲取指向基類的指針,然後使用dynamic_cast將其向下轉換爲具體的消息類型。這是略微比傳遞void*更好,並進行任意強制轉換,因爲您至少會得到一些運行時類型檢查。

另一種選擇是在你的IMessageReceiver接口(它們都沒有操作實現)中有多個重載,並且讓消息發送者直接調用具體的方法。這給你編譯時類型檢查,但是當你發明一個新的「消息」類型時需要重新編譯。 (我把消息用引號引起來,因爲在這個方案中,你不再需要消息對象。)

+0

嗯..我懷疑消息是生產者 - 消費者排隊到接收器,並且直接調用是不可能的。 –

+0

@MartinJames:公平點。我的第三段仍然適用。但是,如果現在這是一場火災,交付後期系統,那麼就需要對終身管理給予一些考慮(例如,如果接收器在消息傳遞之前被銷燬?誰清理消息數據?)。 –

+0

感謝您的回答,@Adrian。我在一行中引用它,但沒有直接指定它,但有一個'MessageHandler'類。當發送一個'Message'時,一個新的消息被初始化,然後傳遞給MessageHandler :: sendMessage(const Message&m)函數,該函數將它添加到它的消息隊列中。然後在更新期間它彈出所有的消息並將它們發送到相關的接收器。這是一個基於觀察者模式的系統,是我的講師如何說明的。 – LoneJock