はじめに
C++26についに静的リフレクションがくるらしい
最終的にどうなったのかあまり追えてなかったので、とりあえず調べてみる
なんかニコニコしてて元気になれそうな感じなのは把握している^^
言語機能といくつかのメタ関数を見ていきます
言語機能
constexpr auto r = ^^int;
typename[:r:] x = 42; // Same as: int x = 42;
- リフレクション演算子
^^(ニコニコ演算子ではない^^)-
std::meta::info型のリフレクション値を返す - リフレクション演算子が使用できるのは以下のような物がある
- 名前空間 / 名前空間エイリアス
- (
^^::はグローバル名前空間へのリフレクション値)
- (
- 型 / 型エイリアス
- 変数、staticデータメンバ、構造化束縛
- 関数 / メンバ関数
- 非staticデータメンバ
- template
- enum値
- 名前空間 / 名前空間エイリアス
-
- スプライサー構文
[:...:]- リフレクション値から戻すほうの構文
constevalブロック
consteval {
// statement
}
これは以下と同等です
static_assert(
(
[]() -> void consteval {
// statement
}(),
true
)
);
constevalブロックとdefine_aggregateを組み合わせて
リフレクション値からクラスのデータメンバを定義できます。
#include <meta>
struct Hoge; // 宣言のみ
consteval
{
// Hoge型の定義
std::meta::define_aggregate(^^Hoge, {
// int piyo;
data_member_spec(^^int, {.name="piyo"})
});
}
// 以下のような定義ができた
// struct Hoge
// {
// int piyo;
// }
Hoge hoge{.piyo = 1};
template for
リフレクションを扱う際に一緒に覚えておくのが良いC++26の新構文
template <typename... Ts>
void print_all(Ts... elems) {
template for (auto elem : {elems...}) {
std::println("{}", elem);
}
}
上記が以下のように展開されます
template <typename... Ts>
void print_all(Ts... elems) {
{
{
auto elem = elems...[0];
std::println("{}", elem);
}
{
auto elem = elems...[1];
std::println("{}", elem);
}
{
auto elem = elems...[2];
std::println("{}", elem);
}
}
}
ついでに余談ですが、C++26でパラメータパックへのインデックスアクセスができるようになりました。
template <typename... Ts>
void print_first(Ts... elems)
{
std::println("{}", elems...[0]);
}
tupleの展開
auto tup = std::make_tuple(0, 'a');
template for (const auto& elem : tup) {
std::println("{}", elem);
}
リフレクション関連 メタ関数
identifier_of
識別子の名前を取得できる
has_identifierで識別子か判定できる
int a;
constexpr auto r = ^^a;
static_assert(std::meta::has_identifier(r));
std::println("{}", std::meta::identifier_of(r)); // a
display_string_of
こちらは、引数のリフレクション値が識別子でなくても良いが、実装によるいい感じの名前を返してくれる
std::println("{}", std::meta::display_string_of(^^int)); // int
source_location_of
リフレクション値から実装によるいい感じのsource_locationを取得する
#include <print>
#include <meta>
int main()
{
int a; // 6行名で宣言
constexpr auto r = ^^a;
constexpr auto location = std::meta::source_location_of(r);
std::println("{}", location.line()); // 6
}
type_of
型エンティティのリフレクション値から、型のリフレクション値を取得できます
int a;
static_assert(std::meta::type_of(^^a) == ^^int);
parent_of
クラスや関数のスコープに含むものや、名前空間を指定し、どこに属するかを取得できます
namespace My
{
struct Hoge
{
int piyo;
};
}
int main()
{
int a;
static_assert(std::meta::parent_of(^^a) == ^^main);
static_assert(std::meta::parent_of(^^My::Hoge::piyo) == ^^My::Hoge);
static_assert(std::meta::parent_of(^^My::Hoge) == ^^My);
}
dealias
エイリアスの元のリフレクション値を取得します
using MyInt = int;
static_assert(^^MyInt != ^^int);
static_assert(std::meta::dealias(^^MyInt) == ^^int);
object_of
静的記憶領域期間をもつオブジェクトのリフレクション値を取得します
すでにオブジェクトのリフレクション値であるものを渡したら、その値がそのまま返る。
static int x;
int &y = x;
static_assert(^^x != ^^y);
static_assert(std::meta::object_of(^^x) == std::meta::object_of(^^y));
constant_of
定数値のリフレクション値を取得します。
- 列挙子の場合、その値のリフレクション値
- 定数式で使用できるオブジェクトの場合
- スカラーならその値のリフレクション値。
- そうでないならオブジェクトのリフレクション値
constexpr int a = 0;
constexpr int b = 0;
static_assert(^^a != ^^b);
static_assert(std::meta::constant_of(^^a) == std::meta::constant_of(^^b));
template_of
特殊化された型のリフレクション値からテンプレートのリフレクション値を取得します
static_assert(std::meta::template_of(^^std::vector<int>) == ^^std::vector);
template_arguments_of
特殊化された型のリフレクション値からテンプレート引数のリフレクション値のvectorを取得します
static_assert(std::meta::template_arguments_of(^^std::vector<int>)[0] == ^^int);
members_of
classや名前空間に含むリフレクション値のvectorを宣言順に取得します
namespace Hoge
{
int a;
void f();
}
constexpr auto ctx = std::meta::access_context::current();
static_assert(std::meta::members_of(^^Hoge, ctx)[0] == ^^Hoge::a);
static_assert(std::meta::members_of(^^Hoge, ctx)[1] == ^^Hoge::f);
static_data_members_of
classのstaticデータメンバのリフレクション値のvectorを宣言順に取得します
nonstatic_data_members_of
classの非staticデータメンバのリフレクション値のvectorを宣言順に取得します
struct Hoge
{
int m_a;
int m_b;
static int s_a;
static int s_b;
using A = int;
};
constexpr auto ctx = std::meta::access_context::current();
static_assert(std::meta::static_data_members_of(^^Hoge, ctx)[0] == ^^Hoge::s_a);
static_assert(std::meta::nonstatic_data_members_of(^^Hoge, ctx)[0] == ^^Hoge::m_a);
static_assert(std::meta::members_of(^^Hoge, ctx)[4] == ^^Hoge::A);
bases_of
classの直接基底クラス関係のリフレクション値を宣言順に取得します
struct A{};
struct B : A{};
constexpr auto ctx = std::meta::access_context::current();
constexpr auto r = std::meta::bases_of(^^B, ctx)[0];
static_assert(std::meta::parent_of(r) == ^^B);
static_assert(std::meta::type_of(r) == ^^A);
enumerators_of
列挙子のリフレクション値のvectorを取得
enum class Enum
{
Hoge,
Piyo,
Foo,
};
static_assert(std::meta::enumerators_of(^^Enum)[0] == ^^Enum::Hoge);
static_assert(std::meta::enumerators_of(^^Enum)[1] == ^^Enum::Piyo);
static_assert(std::meta::enumerators_of(^^Enum)[2] == ^^Enum::Foo);
substitute
templateのリフレクション値とテンプレート引数のリフレクション値を組み合わせる
constexpr auto r = substitute(^^std::vector, {^^int});
static_assert(r == ^^std::vector<int>);
templateがconceptの場合は、boolの定数リフレクション値になる
constexpr auto r = substitute(^^std::integral, {^^int});
static_assert([:r:]);
constexpr auto r2 = substitute(^^std::integral, {^^float});
static_assert(![:r2:]);
reflect_constant
評価結果から定数値のリフレクション値を生成
constexpr int x = 5;
static_assert(std::meta::reflect_constant(5) == std::meta::constant_of(^^x));
reflect_object
評価結果からオブジェクトのリフレクション値を生成
static int x = 5;
static_assert(std::meta::reflect_object(x) == std::meta::object_of(^^x));
reflect_function
評価結果から関数のリフレクション値を生成
void foo();
consteval bool is_global_func(void (*f)())
{
auto rf = std::meta::reflect_function(*f);
return parent_of(rf) == ^^::;
}
static_assert(is_global_func(&foo));
extract<T>
リフレクション値をT型として取り出す
constexpr int x = 1;
constexpr auto r = ^^x;
static_assert(extract<int>(r) == 1);
レイアウト系
offset_of
非静的データメンバや、基底クラスサブオブジェクトのリフレクション値から先頭からのオフセットを取得
size_of
型、オブジェクト、変数、非静的データメンバ、基本クラスサブオブジェクトのリフレクション値からバイトサイズを返す
alignment_of
型、非静的データメンバ、基本クラスサブオブジェクトのリフレクション値からアライメントを返す
bit_size_of
非静的データメンバや、基底クラスサブオブジェクトのリフレクション値からビットサイズを返す
struct Hoge
{
std::int32_t piyo;
std::int32_t foo;
};
static_assert(std::meta::offset_of(^^Hoge::piyo) == std::meta::member_offset{0, 0});
static_assert(std::meta::offset_of(^^Hoge::foo) == std::meta::member_offset{4, 0});
static_assert(std::meta::size_of(^^Hoge::piyo) == 4);
static_assert(std::meta::alignment_of(^^Hoge::piyo) == 4);
static_assert(std::meta::bit_size_of(^^Hoge::piyo) == 32);
その他判定系
いっぱいあるので一部紹介
// 名前空間か
namespace My{}
static_assert(std::meta::is_namespace(^^My));
// 名前空間エイリアスか
namespace My2 = My;
static_assert(std::meta::is_namespace_alias(^^My2));
// 関数か
void func();
static_assert(std::meta::is_function(^^func));
// 変数か
constexpr int a = 0;
static_assert(std::meta::is_variable(^^a));
// 型か
struct Hoge;
static_assert(std::meta::is_type(^^Hoge));
// 完全型か
static_assert(!std::meta::is_complete_type(^^Hoge));
// 型エイリアスか
using MyInt = int;
static_assert(std::meta::is_type_alias(^^MyInt));
// enumか
enum class Enum{};
static_assert(std::meta::is_enumerable_type(^^Enum));
// templateか
static_assert(std::meta::is_template(^^std::vector));
// conceptか
static_assert(std::meta::is_concept(^^std::integral));
// 値か
static_assert(std::meta::is_value(std::meta::constant_of(^^a)));
// オブジェクトか
static_assert(std::meta::is_object(std::meta::object_of(^^a)));
修飾子系
アクセス指定子
struct Hoge
{
public:
int a;
protected:
int b;
private:
int c;
// publicか
static_assert(std::meta::is_public(^^Hoge::a));
// protectedか
static_assert(std::meta::is_protected(^^Hoge::b));
// privateか
static_assert(std::meta::is_private(^^Hoge::c));
};
CV
const volatile int a = 0;
// constか
static_assert(std::meta::is_const(^^a));
// volatileか
static_assert(std::meta::is_volatile(^^a));
などなど…
まとめ
- C++26についに静的リフレクションがくる
- ソースコードがニコニコ
^^しているが、これはリフレクション演算子 - スプライサー構文
[:...:]でリフレクション値から戻せる - constevalブロックとリフレクションを使ってメンバー変数の定義ができる
define_aggregatedata_member_spec
- template forありがてえ
- 豊富なメタ関数群でいろいろできそう
- 静的リフレクション早く使いたい
- 静的リフレクションでニコニコ
^^しよう
参考