LINUX.ORG.RU

маппинг енумов в С++

 , ,


0

2

Имеется некоторый класс работяга, который о своей работе сообщает через константы

enum class SomeWorkingStatus: int {
    INITIAL = 0,
    ITS_OK = 1, 
    ERROR = 3, 
    STATE_1 = 4, 
    // ...
    STATE_N = 121,
    // ... 
};

Отдавать эти состояния нужно через протобуфные соообщения

enum SomeWokringStatus {
    INIT = 0;
    ITS_OK = 1; 
    ERROR = 3; 
    STATE_1 = 4; 
    // ...
    STATE_N = 121;
    // ... 
};

Для этого приходится писать мэппер ... , который можно сделать со switch или std::map взять, или еще что-то. Но как-то написание мэппера не красиво выглядит, т.к. константы одни и те же. Можно как-то эти константы из enum class SomeWorkingStatus запихнуть в протобуферные перечисления без написания всяких преобразователей (мэпперов)?

int main() {
    auto status = SomeWorkingStatus::ITS_OK;
    auto protoStatus = static_cast<ProtoSomeWokringStatus>(status);
    status = static_cast<SomeWorkingStatus>(protoStatus);

    std::cout << protoStatus << "\n";
    std::cout << (int) status << "\n";

    return 0;
}
Int64 ★★★ ()
Последнее исправление: Int64 (всего исправлений: 3)

В первом приближении один из таких вариантов:

  1. Используй внутри программы сразу протобуфный енум.
  2. Если значения енумов совпадают, банально кастуй. Тут не будет проверок и чтобы уменьшить вероятность проблем, добавь в оба протобуфа последнее значение MAX_VALUE, и добавь static_assert на то что они совпадают.
  3. Бери/пиши генератор кода, навскидку не скажу, но видел когда-то именно для енумов генератор рефлексии. Для протобуфных енумов рефлексия есть из коробки. Либо без рефлексии просто из одного источника генерить и плюсовый файл, и протобуфный.

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

Возможно завтра множества значений разойдутся, тогда надо сразу писать код с учётом этого.

slovazap ★★★★★ ()
Ответ на: комментарий от Int64

кстати с такими кастами надо быть осторожнее. У enum class по умолчанию под капотом тип int, а у просто enum - «не фиксирован». И если кастуемое значение окажется за пределами ренджа этого не фиксированного типа, то получится UB. Поэтому крайне рекомендуется объявлять его явно:

enum SomeWorkingStatus: int {
    INIT = 0;
    // ... 
};
Lrrr ★★ ()
Ответ на: комментарий от slovazap

Возможно завтра множества значений разойдутся

да, тут я поддерживаю. обычно разне enumы потому и разные, что они когда-то разойдутся (то есть для раных целей созданы). тут имхо кодогенерация очень подходит.

имхо какой-нибудь макрос вполне подходит. например пишешь в нем значения, он генерит разу и enum и inline функцию, которая мапит все эти значения. принимает пары аргументов на вход, тогда можно не парится, что код повторяется в нескольких местах. вот тут https://en.cppreference.com/w/cpp/preprocessor/replace есть нужная для этого инфа. то есть если мапить все равно надо, то где-то все равно должен быть код, который соотносит эти значения. просто не хочется чтобы этот код повторял значеня из enum.

zerhud ()
Ответ на: комментарий от xaizek

так написано на cppreference:

If the underlying type is not fixed and the source value is out of range, the result is unspecified (until C++17) undefined (since C++17)

и кстати -fsanitize=undefined в clang это ловит.

Lrrr ★★ ()
Последнее исправление: Lrrr (всего исправлений: 1)
Ограничение на отправку комментариев: только для зарегистрированных пользователей