在C++中转换不同的类,使其无效和安全返回 我有一个回调图,它通过代码传递信息,并执行各种函数,非常类似于C中的事件,但在C++中。

在C++中转换不同的类,使其无效和安全返回 我有一个回调图,它通过代码传递信息,并执行各种函数,非常类似于C中的事件,但在C++中。,c++,events,callback,C++,Events,Callback,地图被定义为 映射mCallbacks 它通过引用传递给所有子程序 然后每个类都将其回调绑定为这样 mCallbacks[Status_Label]=std::bind&MenuHandler::LabelEditCallback,this,std::placeholders::\u 1 在哪里 每个事件都从不同的子程序调用,如下所示: if (mCallbacks.find("Status_Label") != mCallbacks.end()) mCallbacks.at("Stat

地图被定义为

映射mCallbacks

它通过引用传递给所有子程序

然后每个类都将其回调绑定为这样

mCallbacks[Status_Label]=std::bind&MenuHandler::LabelEditCallback,this,std::placeholders::\u 1

在哪里

每个事件都从不同的子程序调用,如下所示:

if (mCallbacks.find("Status_Label") != mCallbacks.end())
    mCallbacks.at("Status_Label")((uint8_t*)selected_text);
if (mCallbacks.find("Status_Label") != mCallbacks.end())
    mCallbacks.at("Status_Label")((uint8_t*)Label_Callback_Data("Status_Label_name", "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*)selected_text);
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解决方案,可能是使用动态指针类型转换的解决方案

另外,作为一个额外的问题,我如何知道传递给回调函数的对象是否比预期的小?是否可以检查此项并从回调函数返回false,这样程序就不会崩溃

谢谢,,
此代码未经测试,因此可能存在逻辑错误,我愿意在每个响应中更正。

您通常应该更喜欢使用lambda而不是std::bind

尝试类似以下内容:

if (mCallbacks.find("Status_Label") != mCallbacks.end())
    mCallbacks.at("Status_Label")((uint8_t*)selected_text);
if (mCallbacks.find("Status_Label") != mCallbacks.end())
    mCallbacks.at("Status_Label")((uint8_t*)Label_Callback_Data("Status_Label_name", "TEXT"))
映射mCallbacks; 结构标签\回调\数据 { std::字符串标签名; 字符串标签文本; Label_Callback_Datastd::string v_name,std::string v_text :labelNamev_name,labelTextv_text{} }; ... mCallbacks[Status_Label]=[this]void*data{this->LabelEditCallbackdata;}; ... 自动iter=mCallbacks.findStatus_标签; 如果iter!=mCallbacks.end { 标签\回调\数据状态\标签\名称、文本; iter->second&data; } ... bool MenuHandler::LabelEditCallbackvoid*m_label_数据 { Label\u Callback\u Data*Data=static\u castm\u Label\u text; //根据需要使用data->labelName和data->labelText。。。 } 或者,您可以将类型转换移动到lambda本身中,这样LabelEditCallback根本不需要处理void*:

映射mCallbacks; 结构标签\回调\数据 { std::字符串标签名; 字符串标签文本; Label_Callback_Datastd::string v_name,std::string v_text :labelNamev_name,labelTextv_text{} }; ... mCallbacks[Status_Label]=[this]void*data{this->LabelEditCallbackstatic_castdata;}; ... 自动iter=mCallbacks.findStatus_标签; 如果iter!=mCallbacks.end { 标签\回调\数据状态\标签\名称、文本; iter->second&data; } ... bool MenuHandler::LabelEditCallbackLabel\u Callback\u数据*m\u label\u数据 { //根据需要使用m_label_data->labelName和m_label_data->labelText。。。 }
您通常应该更喜欢使用lambda而不是std::bind

尝试类似以下内容:

if (mCallbacks.find("Status_Label") != mCallbacks.end())
    mCallbacks.at("Status_Label")((uint8_t*)selected_text);
if (mCallbacks.find("Status_Label") != mCallbacks.end())
    mCallbacks.at("Status_Label")((uint8_t*)Label_Callback_Data("Status_Label_name", "TEXT"))
映射mCallbacks; 结构标签\回调\数据 { std::字符串标签名; 字符串标签文本; Label_Callback_Datastd::string v_name,std::string v_text :labelNamev_name,labelTextv_text{} }; ... mCallbacks[Status_Label]=[this]void*data{this->LabelEditCallbackdata;}; ... 自动iter=mCallbacks.findStatus_标签; 如果iter!=mCallbacks.end { 标签\回调\数据状态\标签\名称、文本; iter->second&data; } ... bool MenuHandler::LabelEditCallbackvoid*m_label_数据 { Label\u Callback\u Data*Data=static\u castm\u Label\u text; //根据需要使用data->labelName和data->labelText。。。 } 或者,您可以将类型转换移动到lambda本身中,这样LabelEditCallback根本不需要处理void*:

映射mCallbacks; 结构标签\回调\数据 { std::字符串标签名; 字符串标签文本; Label_Callback_Datastd::string v_name,std::string v_text :labelNamev_name,labelTextv_text{} }; ... mCallbacks[Status_Label]=[this]void*data{this->LabelEditCallbackstatic_castdata;}; ... 自动iter=mCallbacks.findStatus_标签; 如果iter!=mCallbacks.end { 标签\回调\数据状态\标签\名称、文本; iter->second&data; } ... bool MenuHandler::LabelEditCallbackLabel\u Callback\u数据*m\u label\u数据 { //根据需要使用m_label_data->labelName和m_label_data->labelText。。。 } 我就是这样做的

映射mCallbacks

我就是这样做的

映射mCallbacks

int*m_标签_文本[0]错误。如果sizeofint==4,这是所有现代32位和64位co上的值
计算机得到的是m_label_text[0]、m_label_text[1]、m_label_text[2]和m_label_text[3]的值。您还需要考虑这样的代码的问题。如果大小在m_label_text[0]中存储为无符号8位整数,则只需执行例如size_t text_size=m_label_text[0];您可以使用多态性。函数使用基类作为参数。在您的类中,您可以使用dynamic cast来验证类型转换始终是一种代码味道。相反,你应该试着使用void*来摆脱所有的强制转换。也许看看STD::任何或STD::VALANATION,除非这是一个作业或练习,我真的建议您看看已经存在的许多C++事件处理框架之一。其中一些将允许您处理信号处理程序的多个参数,并可以处理C++对象,因此可以让信号处理程序使用sisixt参数和STD::String对象或引用。请参阅,例如,@formerlyknownas_463035818所述,类型转换,尤其是C样式转换,是一个危险信号,表明您可能做错了什么。int*m_label_text[0]是错误的。如果sizeofint==4,这在所有现代32位和64位计算机上都存在,那么得到的是m_label_text[0]、m_label_text[1]、m_label_text[2]和m_label_text[3]的值。您还需要考虑这样的代码的问题。如果大小在m_label_text[0]中存储为无符号8位整数,则只需执行例如size_t text_size=m_label_text[0];您可以使用多态性。函数使用基类作为参数。在您的类中,您可以使用dynamic cast来验证类型转换始终是一种代码味道。相反,你应该试着使用void*来摆脱所有的强制转换。也许看看STD::任何或STD::VALANATION,除非这是一个作业或练习,我真的建议您看看已经存在的许多C++事件处理框架之一。其中一些将允许您处理信号处理程序的多个参数,并可以处理C++对象,因此可以让信号处理程序使用sisixt参数和STD::String对象或引用。如@formerlyknownas_463035818所述,强制类型转换,尤其是C风格的强制类型转换,是一个危险信号,表明您可能做错了什么。但您如何验证进行静态强制类型转换的安全性?如果无法执行静态\u转换,它是否将返回空指针?静态\u转换是在编译时计算的,而不是在运行时。只有动态_cast在转换失败时返回NULL。由于每个回调传递的每个结构只应有一个定义,因此,如果从ie开始使用正确的结构,则无需验证大小,状态标签始终使用标签回调数据等。否则,如果希望代码更具动态性,您必须将结构大小和/或类型ID作为额外参数传递给回调。为什么通常更喜欢lambda?我确实更喜欢lambdas而不是bind,因为imho-bind会导致糟糕的代码,伤害我的眼睛,使用起来比较困难,而lambdas更自然,但除此之外还有其他原因吗?@formerlyknownas_463035818看到并假设两者都可以使用,我在其中一个答案中发现的lambdas的唯一优点是,当使用绑定函数时,不太可能内联,这就是为什么我通常更喜欢lambdas的原因,我猜,但是如何验证进行静态转换的安全性呢?如果无法执行静态\u转换,它是否将返回空指针?静态\u转换是在编译时计算的,而不是在运行时。只有动态_cast在转换失败时返回NULL。由于每个回调传递的每个结构只应有一个定义,因此,如果从ie开始使用正确的结构,则无需验证大小,状态标签始终使用标签回调数据等。否则,如果希望代码更具动态性,您必须将结构大小和/或类型ID作为额外参数传递给回调。为什么通常更喜欢lambda?我确实更喜欢lambdas而不是bind,因为imho-bind会导致糟糕的代码,伤害我的眼睛,使用起来比较困难,而lambdas更自然,但除此之外还有其他原因吗?@formerlyknownas_463035818看到并假设两者都可以使用,我在其中一个答案中发现的lambdas的唯一优点是,使用绑定函数时不太可能内联,我想这就是为什么我通常更喜欢lambdas的原因