Преобразование различных классов в void * и обратно безопасно в C ++ - PullRequest
1 голос
/ 25 июня 2019

У меня есть карта обратных вызовов, которые передают информацию и выполняют различные функции в коде, очень похоже на события в 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 ]

3 голосов
/ 25 июня 2019

Обычно вы предпочитаете использовать лямбду вместо 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...
}

Кроме того, вы можете переместить приведение типов в саму лямбду, поэтому 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 голос
/ 26 июня 2019

Вот как я это сделал

...

//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;
    };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...