将不同的类转换为void *并在C ++中安全地返回

时间:2019-06-25 20:16:26

标签: c++ events callback

我有一幅回调图,它们在整个代码中传递信息并执行各种功能,非常类似于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,以使程序不会崩溃?

谢谢你, 该代码未经测试,因此我愿意根据每个响应更正一些逻辑错误。

2 个答案:

答案 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));
}