Вычислить биты для представления перечисления во время компиляции - PullRequest
0 голосов
/ 21 ноября 2019

Мне нужно сжать мою структуру для экономии памяти, и я получил такую ​​структуру данных:

enum class Foo {
  F1,
  F2,
  F3,
  FEnd,
};

struct Bar {
  bool b: 1;
  Foo foo : 2; // Foo has 3 elements, 2 bits can represent them
};

, и все еще есть вероятность, что элементы Foo увеличатся, поэтому я нехочу жестко кодировать Foo foo:2 и изменять его каждый раз, когда Foo добавляет элемент.

в настоящее время у меня есть решение: constexpr static int i = 32 - __builtin_clz(static_cast<int>(Foo::FEnd));, но это слишком уродливо и не переносимо.

Так какие-нибудь предложения?

1 Ответ

0 голосов
/ 21 ноября 2019

Поскольку стандарт C ++ не упоминает о математической библиотеке constexpr, решение:

constexpr uint64_t bits(const uint64_t x) {
  int i = 64;
  uint64_t mask = 0x8000000000000000;
  while (i > 0) {
    if ((mask & x) != 0) return i;
    i--;
    mask /= 2; // do not use >>, which is a non-constexpr in standard.
  }
  return 1;
}

, тогда пример кода будет выглядеть так:

struct Bar {
  bool b: 1;
  Foo foo : bits(static_cast<int>(Foo::FEnd) - 1);
};

Если выиспользуя GCC, есть встроенный constexpr log2 (x), тогда код будет:

struct Bar {
  bool b: 1;
  Foo foo : (int)std::log2(static_cast<int>(Foo::FEnd) - 1) + 1;
};

Edit : реализация версии GCC std::log2(x):

template<typename _Tp>
    constexpr typename __gnu_cxx::__enable_if<__is_integer<_Tp>::__value, 
                                              double>::__type
    log2(_Tp __x)
    { return __builtin_log2(__x); }

Edit2 : как уже упоминалось @uneven_mark, std::log2(x) возвращает double и может привести к ошибкам в зависимости от ввода, поэтому рекомендуется использовать пользовательские функции constexpr с проверками даже с использованием GCC.

...