我有一幅回调图,它们在整个代码中传递信息并执行各种功能,非常类似于C#中的事件,但在C ++中。
该地图定义为
std::map<std::string, std::function<void(uint8_t*)>> mCallbacks
通过引用传递给所有子程序
然后每个类都这样绑定其回调
mCallbacks["Status_Label"] = std::bind(&MenuHandler::LabelEditCallback, this, std::placeholders::_1);
哪里
bool MenuHandler::LabelEditCallback(uint8_t * m_label_data)
{
int text_size = ((int*)m_label_text)[0];
char* v_text = (char*)&m_label_text[1];
}
每个事件都从这样的不同子程序中调用:
if (mCallbacks.find("Status_Label") != mCallbacks.end())
mCallbacks.at("Status_Label")((uint8_t*)selected_text);
这使得在程序周围传递数据和事件变得容易,而不会造成对象和引用的混乱
如您所见,这是非常不安全的,从uint8_t指针转换为各种数据格式很容易导致堆栈损坏。
问题是,我没有用于回调参数的特定结构,其中一些可能正在发送文本数据,另一些可能正在发送数字。
我的解决方案是定义在调用事件时将强制转换为void *并返回回调函数的结构
类似的东西(未经测试):
struct Label_Callback_Data
{
Label_Callback_Data(std::string v_name, std::string v_text)
{
labelName = v_name;
labelText = v_text;
size_of = sizeof(this);
}
int size_of;
std::string labelName;
std::string labelText;
};
我会这样称呼它:
if (mCallbacks.find("Status_Label") != mCallbacks.end())
mCallbacks.at("Status_Label")((uint8_t*)Label_Callback_Data("Status_Label_name", "TEXT"))
但是那我怎么在这里恢复呢?如果我不知道物体的确切尺寸?
bool MenuHandler::LabelEditCallback(uint8_t * m_label_data)
{
//?? Label_Callback_Data text_size = (Label_Callback_Data*)m_label_text
}
一种解决方案是使用具有固定大小数组的对象,但是必须有一个可以安全使用的C ++ 11解决方案,也许是使用dynamic_pointer_casts的解决方案?
另外,作为一个奖励问题,我怎么知道传递给回调函数的对象的大小是否小于预期的大小?是否可以检查一下并仅从回调函数返回false,以使程序不会崩溃?
谢谢你, 该代码未经测试,因此我愿意根据每个响应更正一些逻辑错误。
答案 0 :(得分:3)
通常,您应该更喜欢使用lambda而不是std::bind()
。
尝试更多类似的方法:
std::map<std::string, std::function<void(void*)>> mCallbacks;
struct Label_Callback_Data
{
std::string labelName;
std::string labelText;
Label_Callback_Data(std::string v_name, std::string v_text)
: labelName(v_name), labelText(v_text) { }
};
...
mCallbacks["Status_Label"] = [this](void *data){ this->LabelEditCallback(data); };
...
auto iter = mCallbacks.find("Status_Label");
if (iter != mCallbacks.end())
{
Label_Callback_Data data("Status_Label_name", "TEXT");
iter->second(&data);
}
...
bool MenuHandler::LabelEditCallback(void *m_label_data)
{
Label_Callback_Data *data = static_cast<Label_Callback_Data*>(m_label_text);
// use data->labelName and data->labelText as needed...
}
或者,您可以将类型转换移到lambda本身,因此LabelEditCallback()
根本不需要处理void*
:
std::map<std::string, std::function<void(void*)>> mCallbacks;
struct Label_Callback_Data
{
std::string labelName;
std::string labelText;
Label_Callback_Data(std::string v_name, std::string v_text)
: labelName(v_name), labelText(v_text) { }
};
...
mCallbacks["Status_Label"] = [this](void *data){ this->LabelEditCallback(static_cast<Label_Callback_Data*>(data)); };
...
auto iter = mCallbacks.find("Status_Label");
if (iter != mCallbacks.end())
{
Label_Callback_Data data("Status_Label_name", "TEXT");
iter->second(&data);
}
...
bool MenuHandler::LabelEditCallback(Label_Callback_Data *m_label_data)
{
// use m_label_data->labelName and m_label_data->labelText as needed...
}
答案 1 :(得分:1)
这就是我的做法
...
add_header X-Frame-Options "SAMEORIGIN";
//The container
...
std::map<std::string, std::function<void(std::shared_ptr<CallbackData::BlankData>)>> mCallbacks
...
//CALLBACK FUNCTION
bool InputManager::KeyboardCallback(std::shared_ptr<CallbackData::BlankData> callback_data)
{
std::shared_ptr<CallbackData::Keyboard> keyboard_data = std::dynamic_pointer_cast<CallbackData::Keyboard>(callback_data);
if (keyboard_data == nullptr)
return false;
///...
}
...
//CALLBACK EVENT
if (mCallbacks.find("Keyboard") != mCallbacks.end())
{
std::shared_ptr<CallbackData::Keyboard> keyboard_data = std::make_shared<CallbackData::Keyboard>(m_keyboardState);
mCallbacks.at("Keyboard")(std::dynamic_pointer_cast<CallbackData::BlankData>(keyboard_data));
}