将不同的类转换为void*,然后在C 中安全地返回

Converting different classes to void* and back safely in C++

本文关键字:然后 安全 返回 转换 void      更新时间:2023-10-16

我有一个回调的地图,可以传递信息并在整个代码中执行各种功能,就像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指针转换为各种数据格式很容易导致堆栈损坏。

问题是,我没有回调参数的特定结构,其中一些可能正在发送文本数据,其他可能是发送数字。

我的解决方案是定义将在调用事件时将被施放到空白的结构,然后返回回调函数

这样的东西(未经测试(:

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_cast的东西?

另外,作为一个奖励问题,我如何知道传递给回调功能的对象是否尺寸小于预期?是否可以检查此问题并只需从回调函数中返回一个false,以免程序崩溃?

谢谢,此代码未进行测试,因此我愿意根据响应有逻辑错误。

您通常应该更喜欢使用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...
}

这就是我做的

...

//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));
}

...

//Data structure    
namespace CallbackData
{
    struct BlankData
    {
        virtual ~BlankData() {};
    };
    struct Keyboard : public BlankData
    {
        Keyboard(uint8_t* kb_data)
        {
            kbData = kb_data;
        }
        uint8_t* kbData;
    };
}