Альтернатива проектированию многострочных макрофункций в C++

Вопрос или проблема

У меня есть проект дизайна, который я хотел бы обсудить. У меня есть следующая функция, где заглавные буквы обозначают макросы:

errorType someFunc(params) {
    
    INTEGRITY_CHECK(params)
    SET_DATA_1(params, 0, 1, 2)
    SET_DATA_2(params, 'a', 'b', 'c') 
}

Эти макросы определены в общем заголовочном файле, который подключается в другие файлы. Каждый из этих макросов по сути является вызовом функции, которая условно возвращает значение.


#define INTEGRITY_CHECK(val)   \
    if (is_empty_func(val))    \
    {                          \
        LOG_ERR("Пустые данные"); \
        return errorCode_1;    \
    }
    

#define SET_DATA_1(params,                                   \
                   v1,                                       \
                   v2,                                       \
                   v3)                                       \
    if (errorType retCode = some_function_set_data_1(params, \
                                                  v1,v2,v3); \
        retCode != errorCode_success)                        \
    {                                                        \
        return errorCode_2;                                  \
    }

#define SET_DATA_2(params,                                   \
                   v1,                                       \
                   v2,                                       \
                   v3)                                       \
    if (errorType retCode = some_function_set_data_2(params, \
                                                  v1,v2,v3); \
        retCode != errorCode_success)                        \
    {                                                        \
        return errorCode_3;                                  \
    }

Params – это указатель на структуру.

Извините за неоднозначность и неопределенность.

Я понимаю, что цель использования вышеописанного дизайна – избежать повторения кода. Мы можем условно возвращать значение из функции, так как макросы разворачиваются и по сути становятся функцией. Есть ли современный способ в C++ / использование функций для этой модели, который был бы сжатым? Или это именно тот случай для макросов?

Почему макросы полезны? Предположим, мы заменим макрос на функцию, она будет возвращать значение, и в функции, где вызываются макросы, нам придется повторно проверять возвращенное значение с помощью if.

Ответ или решение

Безусловно, макросы могут упростить код и избавиться от дублирования, но их использование также может привести к нечитаемости и сложности отладки. В современном C++ действительно есть более элегантные способы решения этой проблемы, без использования макросов.

Вот пример, как вы можете переписать вашу логику с помощью функций, которые могут исключить прямое использование макросов, сохраняя при этом читаемость и гибкость:

  1. Создание вспомогательных функций: Вместо макросов, создайте функции, которые будут реализовывать необходимую проверку и логику.
enum class errorType {
    success,
    errorCode_1,
    errorCode_2,
    errorCode_3
    // Добавьте другие коды ошибок по мере необходимости
};

errorType integrityCheck(const ParamsType* params) {
    if (is_empty_func(params)) {
        LOG_ERR("Empty Data");
        return errorType::errorCode_1;
    }
    return errorType::success;
}

errorType setData1(ParamsType* params, int v1, int v2, int v3) {
    return some_function_set_data_1(params, v1, v2, v3);
}

errorType setData2(ParamsType* params, char v1, char v2, char v3) {
    return some_function_set_data_2(params, v1, v2, v3);
}
  1. Основная функция: Теперь в вашей основной функции вы сможете использовать эти вспомогательные функции и обрабатывать их результаты.
errorType someFunc(ParamsType* params) {
    if (auto retCode = integrityCheck(params); retCode != errorType::success) {
        return retCode; // Возврат кода ошибки
    }

    if (auto retCode = setData1(params, 0, 1, 2); retCode != errorType::success) {
        return retCode; // Возврат кода ошибки
    }

    if (auto retCode = setData2(params, 'a', 'b', 'c'); retCode != errorType::success) {
        return retCode; // Возврат кода ошибки
    }

    return errorType::success; // Успешное завершение
}

Преимущества такого подхода:

  • Читаемость: Код становится более понятным и читаемым. Каждая функция выполняет свою задачу, и ее поведение легко понять.
  • Отладка: С отладкой будет проще, так как у вас есть четкое определение функций, которые можно тестировать по отдельности.
  • Типобезопасность: Функции могут иметь четко определенные типы параметров и возвращаемые значения, что снижает вероятность ошибок.

Заключение

Использование функций вместо макросов в этом случае дает вам больше контроля и предсказуемости в коде, что является важной частью практик современного программирования на C++. Хотя макросы имеют свое место, их использование следует ограничивать для внешнего видимости и обеспечения лучшего качества кода.

Оцените материал
Добавить комментарий

Капча загружается...