在我的entity component system中,我使用位掩码跟踪和查询每个实体使用哪些组件。
// Thanks to Shafik Yaghmour for the macro fix
#define BIT(x) (static_cast<std::uint64_t>(1) << x)
enum class components : std::uint64_t
{
foo = BIT( 0),
bar = BIT( 1),
// Many more omitted
baz = BIT(63)
};
// Operator | is used to build a mask of components. Some entities
// have dozens of components.
auto mask = components::foo | components::bar | components::baz
// Do something with the entity if it has the requisite components.
if ( entity->has_components( mask ) ) { ... }
我达到了枚举的64位限制。 C ++枚举(可移植)是否可以大于64位?
更新1:我知道std::bitset
,但我无法创建像auto mask = std::bitset<128>{ components::foo, components::baz }
这样的掩码,因为std::bitset
没有构造函数需要std::initializer_list
。如果您的回答告诉我使用std::bitset
,请说明此用法或类似内容。
更新2 :我修改了一项功能,以便从std::bitset
创建std::initializer_list
:
enum class components : std::size_t
{
foo,
bar,
baz,
count
};
template< typename enumeration, std::size_t bit_count = static_cast< std::size_t >( enumeration::count ) >
std::bitset< bit_count > make_bitset(std::initializer_list< enumeration > list)
{
assert(list.size() <= bit_count);
std::bitset< bit_count > bs{};
for (const auto i : list)
{
bs.set(static_cast< std::size_t >(i));
}
return bs;
}
// Which can create the mask like so:
auto mask = make_bitset< components >( {components::foo, components::baz} );
答案 0 :(得分:6)
正如其他人所说,存储必须是std::bitset
。但是如何将它与枚举接口?使用this answer中概述的运算符重载。枚举器只需要存储位索引号,这会给你带来更多的空间;)。
请注意,该代码必须在移位操作中删除,即:
flag_bits( t bit ) // implicit
{ this->set( static_cast< int >( bit ) ); }
答案 1 :(得分:3)
使用std::bitset
。
<强>附录:强>
在我写完上面的答案之后,问题已经更新了,提到了std::bitset
和一些关于如何使用它的想法,到目前为止已经有13个版本。
我没能预见使用std::bitset
会有任何困难,抱歉。因为它不是引入不必要的复杂性,而是放弃它。例如,而不是现在建议的
#include <bitset>
enum class components : std::size_t
{
foo,
bar,
baz,
count
};
template< typename enumeration, std::size_t bit_count = static_cast< std::size_t >( enumeration::count ) >
std::bitset< bit_count > make_bitset(std::initializer_list< enumeration > list)
{
assert(list.size() <= bit_count);
std::bitset< bit_count > bs{};
for (const auto i : list)
{
bs.set(static_cast< std::size_t >(i));
}
return bs;
}
auto main() -> int
{
auto mask = make_bitset< components >( {components::foo, components::baz} );
}
就是这样做。
#include <bitset>
namespace component {
using std::bitset;
enum Enum{ foo, bar, baz };
enum{ max = baz, count = max + 1 };
using Set = bitset<count>;
auto operator|( Set const s, Enum const v )
-> Set
{ return s | Set().set( v, true ); }
} // namespace component
auto main() -> int
{
auto const mask = component::Set() | component::foo | component::baz;
}
或简单地说,没有语法糖,
#include <bitset>
namespace component {
using std::bitset;
enum Enum{ foo, bar, baz };
enum{ max = baz, count = max + 1 };
using Set = bitset<count>;
} // namespace component
auto main() -> int
{
auto const mask = component::Set().set( component::foo ).set( component::baz );
}
答案 2 :(得分:3)
注意,您当前的代码具有未定义的行为,整数文字1
在此处具有类型 int :
#define BIT(x) (1 << x)
^
在大多数现代系统上可能都是32 bits
因此根据草案C ++标准部分32 bit
移位运算符5.8或更多移位是未定义的行为>其中说:
如果右操作数为负数,或者大于或等于提升左操作数的位长度,则行为未定义。
对此的修复方法是使用static_cast将1
转换为 uint64_t :
#define BIT(x) (static_cast<std::uint64_t>(1) << x)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
或者Potatoswatter指出你可以使用ULL
后缀,保证为64 bit
。
没有可移植的方式来使用比 uint64_t 更大的类型,某些编译器(如gcc)提供128 bit
整数类型,但同样不能移植。< / p>
std::bitset应该提供您需要的界面。您可以使用set将任何位设置为任意值,并使用test来测试特定位的值。它提供binary or,它应提供此功能:
auto mask = components::foo | components::baz
例如:
#include <bitset>
#include <iostream>
int main()
{
std::bitset<128> foo ;
std::bitset<128> baz ;
foo.set(66,true ) ;
baz.set(80,true ) ;
std::cout << foo << std::endl ;
std::cout << baz << std::endl ;
std::bitset<128> mask = foo | baz ;
std::cout << mask << std::endl ;
return 0;
}