【问题标题】:Calculate bits to represent an enum at compile time在编译时计算位以表示枚举
【发布时间】:2023-04-05 02:33:01
【问题描述】:

我需要压缩结构以节省内存,我得到了这样的数据结构:

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));,但这太丑陋且不便携。

那么有什么建议吗?

【问题讨论】:

  • RAM 很便宜。节省一些比特很少值得麻烦。
  • 我不确定到底是什么问题。您可以在constexpr 函数中天真地实现clz(x),请参阅例如en.wikipedia.org/wiki/Find_first_set#CLZ 的算法。这还不够吗?
  • C++ 中的枚举不是不可变的吗?您为什么担心有人向Foo 添加更多枚举字段,这实际上会改变每个枚举值的大小吗?
  • @smac89 这点更有可能是当程序员在枚举定义中添加元素以避免错误时,位域大小应该自动正确。
  • @uneven_mark 我认为你是对的,出于便携的原因,我应该自己写constexpr int clz(x)

标签: c++ enums bit-fields


【解决方案1】:

由于 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;
};

编辑std::log2(x)的GCC版本实现为:

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 也可以进行检查。

【讨论】:

  • std::log2 是错字吗?因为这不是 GCC 特定的,而是标准功能。但它返回一个double,因此它不能用作位域的大小。
  • 嗨@uneven_mark,我在答案中添加了GCC实现,返回类型是一个错字。我已经解决了这个问题。我的答案中写的代码对我来说效果很好(gcc 7.4.0)
  • 是的,通过显式转换为int 确实可以编译并且可能可以工作,但它只有在enum 的大小在double 并且只要 std::log2 不会给结果带来任何错误,因为它是一个浮点函数,所以它可能会这样做。如果结果完全可表示,GCC 实现可能不会引入任何错误,但不能保证。这就是为什么 std::log2p1 将在 C++20 中引入正确处理整数的原因。
  • 同意,这就是为什么我使用更多检查自定义我的函数,而不是在这种情况下使用std::log2,即使我使用了 GCC。我将更新我的答案以警告这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-15
相关资源
最近更新 更多