C ++ реализация функции protobuf `oneof` для не-protobuf класса - PullRequest
0 голосов
/ 18 июня 2019

Функция protobuf oneof великолепна. Но его можно использовать только тогда, когда поля в oneof имеют тип примитива или сообщение protobuf. Что если у меня есть два класса A и B, которые определены кодом C ++ вместо сообщений protobuf, и я хочу реализовать класс AorB, который выглядит следующим образом:

message AorB {
    oneof oneof_name {
        A a = 1;
        B b = 2;
    }
}

Я попытался прочитать сгенерированный код C ++ поля oneof, чтобы увидеть, как это реализовано. Но это довольно сложно. Есть ли какой-нибудь краткий способ реализовать это? Или любой шаблон, который я могу использовать напрямую?

1 Ответ

1 голос
/ 19 июня 2019

В зависимости от того, какую версию C ++ вы можете использовать, вы можете выбрать std::variant, выбрать свой собственный вариант с помощью шаблонов с переменным числом аргументов или свой собственный с помощью union. std::variant был добавлен к языку в C ++ 17 и определенно будет самым простым в управлении. Вариативная версия шаблона хитрая.

union работает до начала языка и будет выглядеть примерно так.

struct MyAorB {
  union {
    A a;
    B b;
  };
  ~MyAorB() { destruct(); }
  MyAorB& operator=(const MyAorB&) = delete;
  MyAorB& operator=(MyAorB&&) = delete;
  MyAorB(const MyAorB&) = delete;
  MyAorB(const MyAorB&&) = delete;
  enum { HOLDS_NONE, HOLDS_A, HOLDS_B } which_one = HOLDS_NONE;
  A& get_A() { assert(which_one == HOLDS_A); return a; }
  B& get_B() { assert(which_one == HOLDS_B); return b; }
  void set_A(A new_a) { which_one = HOLDS_A; destruct(); a = std::move(new_a); }
  void set_B(B new_b) { which_one = HOLDS_B; destruct(); b = std::move(new_b); }
  void destruct() {
    switch (which_one) {
      case HOLDS_A: a.~A(); break;
      case HOLDS_B: b.~B(); break;
      default: break;
    }
  }
};

на базовом уровне, который вроде может работать. Хотя есть куча деталей, чтобы понять это правильно. Основой этого является то, что объединение помещает значения в перекрывающуюся память, только одно допустимо за один раз, и это неопределенное поведение для доступа к неправильному. Вам также необходимо вручную уничтожить перед повторным присвоением удерживаемого значения.

Я, вероятно, пропустил детали где-то там. Я бы предпочел оставить это на std::variant, но если вам нужно написать свой собственный дискриминируемый союз, он запустит что-то вроде кода выше.

Подробнее о варианте здесь: https://en.cppreference.com/w/cpp/utility/variant

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...