それは、C ++のカーディナリティを決定することが可能ですenum class
:
enum class Example { A, B, C, D, E };
使用しようとしましたsizeof
が、列挙型要素のサイズが返されます。
sizeof(Example); // Returns 4 (on my architecture)
カーディナリティ(私の例では5)を取得する標準的な方法はありますか?
それは、C ++のカーディナリティを決定することが可能ですenum class
:
enum class Example { A, B, C, D, E };
使用しようとしましたsizeof
が、列挙型要素のサイズが返されます。
sizeof(Example); // Returns 4 (on my architecture)
カーディナリティ(私の例では5)を取得する標準的な方法はありますか?
enum
とenum class
esは非常に異なる概念です。
enum class
値を繰り返すことはできないので、数値を知ることのメリットは何でしょうか。
回答:
直接ではありませんが、次のトリックを使用できます。
enum class Example { A, B, C, D, E, Count };
次に、カーディナリティはとして使用できますstatic_cast<int>(Example::Count)
。
もちろん、これは、列挙型の値を0から始めて自動的に割り当てる場合にのみうまく機能します。そうでない場合は、正しいカーディナリティーを手動でCountに割り当てることができます。これは、個別の定数を維持する必要があるのとまったく同じです。とにかく:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
欠点の1つは、コンパイラーでExample::Count
列挙値の引数として使用できることです。したがって、これを使用する場合は注意が必要です。(私は個人的にこれは実際には問題ではないと思います。)
enum class
プレーンなenum
sではなくesを使用すると少し面倒です。明確にするためにキャストで編集します。
C ++ 17の場合magic_enum::enum_count
、libhttps://github.com/Neargye/magic_enumから使用できます。
magic_enum::enum_count<Example>()
-> 4。
このライブラリは、コンパイラ固有のハックを使用します(__PRETTY_FUNCTION__
/に基づく__FUNCSIG__
) Clang> = 5、MSVC> = 15.3、およびGCC> = 9で機能します。
指定された間隔範囲を調べて、名前の付いたすべての列挙を見つけます。これがそれらの数になります。制限についてもっと読む
この投稿https://taylorconor.com/blog/enum-reflectionで、このハックについてさらに詳しく説明します。
constexpr auto TEST_START_LINE = __LINE__;
enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one
ONE = 7
, TWO = 6
, THREE = 9
};
constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3;
これはUglyCoderの回答から派生していますが、3つの方法で改善されています。
BEGIN
およびSIZE
)には余分な要素はありません(Cameronの回答にもこの問題があります)。
これは、列挙子に任意の値を割り当てることができるというCameronの回答に対するUglyCoderの利点を保持しています。
問題(UglyCoderと共有されているが、Cameronとは共有されていない)は、改行とコメントが重要になることです...これは予想外です。したがって、誰かがTEST_SIZE
計算を調整せずに空白またはコメントを含むエントリを追加する可能性があります。
enum class TEST
{
BEGIN = __LINE__
, ONE
, TWO
, NUMBER = __LINE__ - BEGIN - 1
};
auto const TEST_SIZE = TEST::NUMBER;
// or this might be better
constexpr int COUNTER(int val, int )
{
return val;
}
constexpr int E_START{__COUNTER__};
enum class E
{
ONE = COUNTER(90, __COUNTER__) , TWO = COUNTER(1990, __COUNTER__)
};
template<typename T>
constexpr T E_SIZE = __COUNTER__ - E_START - 1;
short
ぶつかる可能性がありますint
。(ただし、これは、提案されたトリックよりもUnityビルドの方が問題だと思います。)
X()-マクロに基づく1つのトリックがあります:画像、次の列挙型があります:
enum MyEnum {BOX, RECT};
次のように再フォーマットします。
#define MyEnumDef \
X(BOX), \
X(RECT)
次に、次のコードで列挙型を定義します。
enum MyEnum
{
#define X(val) val
MyEnumDef
#undef X
};
そして、次のコードは列挙型要素の数を計算します。
template <typename ... T> void null(T...) {}
template <typename ... T>
constexpr size_t countLength(T ... args)
{
null(args...); //kill warnings
return sizeof...(args);
}
constexpr size_t enumLength()
{
#define XValue(val) #val
return countLength(MyEnumDef);
#undef XValue
}
...
std::array<int, enumLength()> some_arr; //enumLength() is compile-time
std::cout << enumLength() << std::endl; //result is: 2
...
#define MyEnumDef
(そしてそれを入れる)ことで簡単#define X(val) val
にできます。これにより、#define X(val) +1
constexpr std::size_t len = MyEnumDef;
。だけを使用して要素の数を数えることができます。
試すことができる1つのトリックは、リストの最後に列挙値を追加し、それをサイズとして使用することです。あなたの例では
enum class Example { A, B, C, D, E, ExampleCount };
enum
sの動作と比較すると、これはExampleCount
タイプのようには機能しませんExample
。内の要素の数を得るためにExample
、ExampleCount
整数型にキャストされなければなりません。
boostのプリプロセッサユーティリティを使用する場合は、を使用してカウントを取得できますBOOST_PP_SEQ_SIZE(...)
。
たとえば、CREATE_ENUM
マクロを次のように定義できます。
#include <boost/preprocessor.hpp>
#define ENUM_PRIMITIVE_TYPE std::int32_t
#define CREATE_ENUM(EnumType, enumValSeq) \
enum class EnumType : ENUM_PRIMITIVE_TYPE \
{ \
BOOST_PP_SEQ_ENUM(enumValSeq) \
}; \
static constexpr ENUM_PRIMITIVE_TYPE EnumType##Count = \
BOOST_PP_SEQ_SIZE(enumValSeq); \
// END MACRO
次に、マクロを呼び出します。
CREATE_ENUM(Example, (A)(B)(C)(D)(E));
次のコードを生成します。
enum class Example : std::int32_t
{
A, B, C, D, E
};
static constexpr std::int32_t ExampleCount = 5;
これは、ブーストプリプロセッサツールに関して表面を傷つけているだけです。たとえば、マクロで、強く型付けされた列挙型の文字列変換ユーティリティとostream演算子を定義することもできます。
Boostプリプロセッサツールの詳細については、https: //www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.htmlをご覧ください。
余談ですが、私は@FantasticMrFoxに強く同意しCount
ます。受け入れられた回答で使用される追加の列挙値は、switch
ステートメントを使用する場合にコンパイラの警告の頭痛の種を大量に作成します。私が見つけunhandled case
、私はそれを弱体化したくないので、より安全なコードの保守のためのコンパイラの警告は非常に便利。
std :: initializer_list:を使用したトリックで解決できます。
#define TypedEnum(Name, Type, ...) \
struct Name { \
enum : Type{ \
__VA_ARGS__ \
}; \
static inline const size_t count = []{ \
static Type __VA_ARGS__; return std::size({__VA_ARGS__}); \
}(); \
};
使用法:
#define Enum(Name, ...) TypedEnum(Name, int, _VA_ARGS_)
Enum(FakeEnum, A = 1, B = 0, C)
int main()
{
std::cout << FakeEnum::A << std::endl
<< FakeEnun::count << std::endl;
}
行数やテンプレートに依存しない別の方法があります。唯一の要件は、列挙値を独自のファイルに固定し、プリプロセッサ/コンパイラに次のようにカウントさせることです。
my_enum_inc.h
ENUMVAL(BANANA)
ENUMVAL(ORANGE=10)
ENUMVAL(KIWI)
...
#undef ENUMVAL
my_enum.h
typedef enum {
#define ENUMVAL(TYPE) TYPE,
#include "my_enum_inc.h"
} Fruits;
#define ENUMVAL(TYPE) +1
const size_t num_fruits =
#include "my_enum_inc.h"
;
これにより、列挙値を使用してコメントを入力し、値を再割り当てでき、コードで無視/説明する必要のある無効な「カウント」列挙値を挿入できません。
コメントを気にしない場合は、追加のファイルは必要なく、上記の誰かのように行うことができます。例:
#define MY_ENUM_LIST \
ENUMVAL(BANANA) \
ENUMVAL(ORANGE = 7) \
ENUMVAL(KIWI)
#include "my_enum_inc.h"
ディレクティブをMY_ENUM_LISTに置き換えますが、#undef ENUMVAL
使用するたびに置き換える必要があります。
これに対する別の種類の「愚かな」解決策は次のとおりです。
enum class Example { A, B, C, D, E };
constexpr int ExampleCount = [] {
Example e{};
int count = 0;
switch (e) {
case Example::A:
count++;
case Example::B:
count++;
case Example::C:
count++;
case Example::D:
count++;
case Example::E:
count++;
}
return count;
}();
これをコンパイルすることによって -Werror=switch
すること、switchケースを省略または複製した場合に、コンパイラの警告が表示されるようにしてください。これもconstexprであるため、これはコンパイル時に計算されます。
ただし、enenum class
の場合でも、列挙型の最初の値が0でなくても、デフォルトの初期化値は0であることに注意してください。したがって、0から開始するか、最初の値を明示的に使用する必要があります。
いいえ、コードで記述する必要があります。
static_cast<int>(Example::E) + 1
どちらが余分な要素を排除するかを検討することもできます。
Example::E
、列挙型の最後の値として置き換えることができる新しい値で拡張できます。そうでない場合でも、Example::E
のリテラル値は変更される可能性があります。
Reflection TS、特に最新バージョンのReflectionTSドラフトの[reflect.ops.enum] / 2は、次のget_enumerators
TransformationTrait
操作を提供します。
[reflect.ops.enum] / 2
template <Enum T> struct get_enumerators
のすべての専門分野は
get_enumerators<T>
、TransformationTrait
要件(20.10.1)を満たしている必要があり ます。名前付きのネストされた型は 、をtype
満たすメタオブジェクト型を指定しObjectSequence
ます。これにEnumerator
は、によって反映される列挙型の列挙子を満たし、反映する要素が含まれT
ます。
ドラフトの[reflect.ops.objseq]はObjectSequence
操作をカバーし、特に[reflect.ops.objseq] / 1は、以下get_size
を満たすメタオブジェクトの要素数を抽出するための特性をカバーしますObjectSequence
。
[reflect.ops.objseq] / 1
template <ObjectSequence T> struct get_size;
すべての専門は、
get_size<T>
満たさなければならないUnaryTypeTrait
の塩基特性を有する要件(20.10.1)integral_constant<size_t, N>
、N
オブジェクトの配列内の要素の数であるが。
したがって、Reflection TSが受け入れられ、現在の形式で実装されることになっていた場合、列挙型の要素数は、コンパイル時に次のように計算できます。
enum class Example { A, B, C, D, E };
using ExampleEnumerators = get_enumerators<Example>::type;
static_assert(get_size<ExampleEnumerators>::value == 5U, "");
我々は、エイリアステンプレートを参照してくださいする可能性がある場所get_enumerators_v
やget_type_v
、さらに反射を簡素化するために:
enum class Example { A, B, C, D, E };
using ExampleEnumerators = get_enumerators_t<Example>;
static_assert(get_size_v<ExampleEnumerators> == 5U, "");
ハーブサッターの旅行レポート: 2018年6月9日のISO C ++委員会の夏の会議からの夏のISOC ++標準会議(Rapperswil)で述べられているように、ReflectionTSは機能が完了していると宣言されています
Reflection TSは機能が完了しています:Reflection TSは機能が完了していると宣言され、夏の間、メインコメント投票のために送信されます。TSの現在のテンプレートメタプログラミングベースの構文は単なるプレースホルダーであることに再度注意してください。要求されているフィードバックは設計の核となる「根性」にあり、委員会は、表面の構文を、
<>
スタイルのメタプログラミングではなく通常のコンパイル時コードを使用するより単純なプログラミングモデルに置き換えることを意図していることをすでに知っています。
そして、された当初はC ++ 20のために予定が、反射TSはまだC ++ 20のリリースにそれを作るチャンスを持っているだろうかどうかはやや不明瞭です。