etl/test/test_meta.cpp
Roland Reichwein 5a077e48b9 Add C++26 reflection
Status (for both STL and Non-STL builds):
- Builds and tests with GCC 16
- Clang 22: Reflection not available yet
- MSVC: Reflection not available yet
2026-10-17 12:23:36 +02:00

2392 lines
94 KiB
C++

/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2026 BMW AG
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#include "unit_test_framework.h"
#include "etl/meta.h"
#if ETL_HAS_REFLECTION
//---------------------------------------------------------------------------
// Helper macros: evaluate consteval-only expressions at compile time,
// then pass the plain bool/value result to the runtime CHECK framework.
// GCC 16's std::meta::info is a consteval-only type and cannot appear
// in runtime expressions such as those generated by CHECK().
//---------------------------------------------------------------------------
#define CONSTEXPR_CHECK(expr) \
do { \
constexpr bool _etl_chk_ = (expr); \
CHECK(_etl_chk_); \
} while (0)
#define CONSTEXPR_CHECK_FALSE(expr) \
do { \
constexpr bool _etl_chk_ = (expr); \
CHECK(!_etl_chk_); \
} while (0)
#define CONSTEXPR_CHECK_EQUAL(expected, expr) \
do { \
constexpr auto _etl_val_ = (expr); \
CHECK_EQUAL(expected, _etl_val_); \
} while (0)
//---------------------------------------------------------------------------
// In an STL build identifier_of / symbol_of return std::string_view.
// In the non-STL build they return etl::string_view.
// Use this alias so the test code is compatible with both.
//---------------------------------------------------------------------------
#if ETL_USING_STL
using meta_string_view = std::string_view;
#else
using meta_string_view = etl::string_view;
#endif
// A struct with external linkage (defined outside anonymous namespace).
struct ExternalLinkageStruct
{
int val;
};
namespace
{
//---------------------------------------------------------------------------
// Simple aggregate with public fields (naming, member queries, layout).
//---------------------------------------------------------------------------
struct SimpleStruct
{
int x;
double y;
};
//---------------------------------------------------------------------------
// A union type (is_union_type).
//---------------------------------------------------------------------------
union TestUnion
{
int i;
float f;
};
//---------------------------------------------------------------------------
// Struct with mixed access (access predicates, inaccessible-member queries).
//---------------------------------------------------------------------------
struct MixedAccess
{
public:
int pub;
protected:
int prot;
private:
[[maybe_unused]]
int priv;
};
//---------------------------------------------------------------------------
// Struct with a mutable member, a const member, and a volatile member.
//---------------------------------------------------------------------------
struct CVQualifiedMembers
{
mutable int m;
const int c = 0;
volatile int v;
};
//---------------------------------------------------------------------------
// Bit-field struct (is_bit_field, bit_size_of, bit_offset_of).
//---------------------------------------------------------------------------
struct BitFields
{
unsigned a : 3;
unsigned b : 5;
};
//---------------------------------------------------------------------------
// Scoped and unscoped enumerations (enumerators_of, is_scoped_enum_type).
//---------------------------------------------------------------------------
enum class Color
{
Red,
Green,
Blue
};
enum Direction
{
North,
South,
East,
West
};
//---------------------------------------------------------------------------
// Base / Derived hierarchy (bases_of, is_base, is_virtual, is_override).
//---------------------------------------------------------------------------
struct Base
{
virtual ~Base() = default;
virtual void vfunc() {}
int base_data = 0;
};
struct Derived : public Base
{
void vfunc() override {}
int derived_data = 0;
};
//---------------------------------------------------------------------------
// Class with a pure-virtual function (is_pure_virtual, is_abstract_type).
//---------------------------------------------------------------------------
struct AbstractBase
{
virtual ~AbstractBase() = default;
virtual void pure() = 0;
};
//---------------------------------------------------------------------------
// Final class (is_final, is_final_type).
//---------------------------------------------------------------------------
struct FinalClass final : Derived
{
};
//---------------------------------------------------------------------------
// Class with explicit / deleted / defaulted / noexcept special members.
//---------------------------------------------------------------------------
struct SpecialMembers
{
SpecialMembers() = default;
SpecialMembers(const SpecialMembers&) = delete;
SpecialMembers(SpecialMembers&&) noexcept = default;
SpecialMembers& operator=(const SpecialMembers&) = default;
SpecialMembers& operator=(SpecialMembers&&) noexcept = default;
~SpecialMembers() = default;
explicit SpecialMembers(int) {}
};
//---------------------------------------------------------------------------
// Class with operator overloads (operator_of, symbol_of, is_operator_function).
//---------------------------------------------------------------------------
struct Operators
{
bool operator==(const Operators&) const = default;
Operators operator+(const Operators&) const
{
return {};
}
int operator()(int a) const
{
return a;
}
};
//---------------------------------------------------------------------------
// Class with a static data member (static_data_members_of, has_static_storage_duration).
//---------------------------------------------------------------------------
struct WithStatic
{
[[maybe_unused]]
static inline int s_count = 0;
int value = 0;
};
//---------------------------------------------------------------------------
// Type alias (is_type_alias, dealias).
//---------------------------------------------------------------------------
using IntAlias = int;
//---------------------------------------------------------------------------
// A simple class template (is_class_template, template_of, template_arguments_of, substitute).
//---------------------------------------------------------------------------
template <typename T, size_t N>
struct FixedArray
{
T data[N];
};
using IntArray5 = FixedArray<int, 5>;
//---------------------------------------------------------------------------
// A constexpr function (reflect_function) and a constexpr variable (reflect_object).
//---------------------------------------------------------------------------
constexpr int global_const = 42;
constexpr int add(int a, int b)
{
return a + b;
}
//---------------------------------------------------------------------------
// A function with default arguments and variadic-style overload (parameter reflection).
//---------------------------------------------------------------------------
[[maybe_unused]]
int with_default(int a, int b = 10)
{
return a + b;
}
//---------------------------------------------------------------------------
// An empty struct (is_empty_type, is_aggregate_type).
//---------------------------------------------------------------------------
struct Empty
{
};
//---------------------------------------------------------------------------
// A polymorphic non-abstract class (is_polymorphic_type but not is_abstract_type).
//---------------------------------------------------------------------------
struct Polymorphic
{
virtual ~Polymorphic() = default;
virtual void foo() {}
};
//---------------------------------------------------------------------------
// A trivially-copyable, non-polymorphic POD-like struct.
//---------------------------------------------------------------------------
struct Trivial
{
int a;
int b;
};
//---------------------------------------------------------------------------
// Class with ref-qualified member functions (is_lvalue/rvalue_reference_qualified).
//---------------------------------------------------------------------------
struct RefQualified
{
void lref() & {}
void rref() && {}
void none() {}
};
//---------------------------------------------------------------------------
// Class with a conversion operator (is_conversion_function).
//---------------------------------------------------------------------------
struct WithConversion
{
operator int() const
{
return 0;
}
};
//---------------------------------------------------------------------------
// Class with a deducing-this (explicit object parameter) member.
//---------------------------------------------------------------------------
struct WithDeducingThis
{
int value = 0;
int get(this WithDeducingThis const& self)
{
return self.value;
}
};
//---------------------------------------------------------------------------
// A thread_local variable (has_thread_storage_duration).
//---------------------------------------------------------------------------
[[maybe_unused]]
thread_local int tls_var = 0;
//---------------------------------------------------------------------------
// A constexpr helper for is_structural_type — structural types can be used
// as non-type template parameters.
//---------------------------------------------------------------------------
struct Structural
{
int x;
// Structural: all members public, no virtual, no user-declared ctors.
};
//---------------------------------------------------------------------------
// A namespace-level variable with internal linkage.
//---------------------------------------------------------------------------
[[maybe_unused]]
static int internal_var = 0;
SUITE(test_meta)
{
//=========================================================================
// Naming / identification
//=========================================================================
//*************************************************************************
TEST(test_identifier_of_type)
{
// identifier_of on a reflected type should return its unqualified name.
constexpr auto name = etl::meta::identifier_of(^^SimpleStruct);
CONSTEXPR_CHECK(name == "SimpleStruct");
}
//*************************************************************************
TEST(test_identifier_of_member)
{
// identifier_of on a reflected data member should return the field name.
constexpr auto name_x =
etl::meta::identifier_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]);
constexpr auto name_y =
etl::meta::identifier_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[1]);
CHECK(meta_string_view("x") == name_x);
CHECK(meta_string_view("y") == name_y);
}
//*************************************************************************
TEST(test_identifier_of_enum_type)
{
// identifier_of on a reflected enumeration type.
constexpr auto name = etl::meta::identifier_of(^^Color);
CONSTEXPR_CHECK(name == "Color");
}
//*************************************************************************
TEST(test_identifier_of_enumerator)
{
// identifier_of on individual enumerators.
CONSTEXPR_CHECK(meta_string_view("Red") == etl::meta::identifier_of(etl::meta::enumerators_of(^^Color)[0]));
CONSTEXPR_CHECK(meta_string_view("Green") == etl::meta::identifier_of(etl::meta::enumerators_of(^^Color)[1]));
CONSTEXPR_CHECK(meta_string_view("Blue") == etl::meta::identifier_of(etl::meta::enumerators_of(^^Color)[2]));
}
//*************************************************************************
TEST(test_has_identifier)
{
// Named entities have identifiers; built-in types may not.
CONSTEXPR_CHECK(etl::meta::has_identifier(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::has_identifier(^^Color));
}
//*************************************************************************
TEST(test_operator_of_and_symbol_of)
{
// Find operator== in Operators and verify operator_of / symbol_of.
// Locate the operator== member.
constexpr auto it = []() consteval
{
auto members = etl::meta::members_of(^^Operators, etl::meta::access_context::unchecked());
for (auto m : members)
{
if (etl::meta::is_operator_function(m) && etl::meta::operator_of(m) == etl::meta::op_equals_equals)
return m;
}
return etl::meta::info{};
}();
CONSTEXPR_CHECK(it != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::operator_of(it) == etl::meta::op_equals_equals);
CONSTEXPR_CHECK(meta_string_view("==") == etl::meta::symbol_of(etl::meta::op_equals_equals));
}
//*************************************************************************
TEST(test_symbol_of_various_operators)
{
// Spot-check symbol_of for a few operator enumerators.
CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_plus) == "+");
CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_minus) == "-");
CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_parentheses) == "()");
CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_spaceship) == "<=>");
CONSTEXPR_CHECK(etl::meta::symbol_of(etl::meta::op_square_brackets) == "[]");
}
//=========================================================================
// Core entity queries
//=========================================================================
//*************************************************************************
TEST(test_type_of_data_member)
{
// type_of on the first member of SimpleStruct should reflect int.
constexpr auto t = etl::meta::type_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]);
CONSTEXPR_CHECK(t == ^^int);
}
//*************************************************************************
TEST(test_type_of_second_member)
{
// type_of on the second member of SimpleStruct should reflect double.
constexpr auto t = etl::meta::type_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[1]);
CONSTEXPR_CHECK(t == ^^double);
}
//*************************************************************************
TEST(test_parent_of_data_member)
{
// parent_of a data member should be the containing class.
constexpr auto parent = etl::meta::parent_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]);
CONSTEXPR_CHECK(parent == ^^SimpleStruct);
}
//*************************************************************************
TEST(test_dealias)
{
// dealias on a type alias should yield the underlying type.
constexpr auto dealiased = etl::meta::dealias(^^IntAlias);
CONSTEXPR_CHECK(dealiased == ^^int);
}
//*************************************************************************
TEST(test_constant_of)
{
// constant_of on a reflected constexpr variable should yield a value reflection.
constexpr auto val = etl::meta::extract<int>(etl::meta::constant_of(^^global_const));
CHECK_EQUAL(42, val);
}
//*************************************************************************
TEST(test_template_of)
{
// template_of on an instantiation should return the primary template.
// IntArray5 is a type alias, so dealias first to get the instantiation.
constexpr auto tmpl = etl::meta::template_of(etl::meta::dealias(^^IntArray5));
CONSTEXPR_CHECK(tmpl == ^^FixedArray);
}
//*************************************************************************
TEST(test_template_arguments_of)
{
// template_arguments_of on FixedArray<int, 5> should give two arguments.
constexpr auto result = []() consteval
{
auto args = etl::meta::template_arguments_of(etl::meta::dealias(^^IntArray5));
struct R
{
size_t sz;
bool arg0_is_int;
size_t arg1;
};
return R{args.size(), args[0] == ^^int, etl::meta::extract<size_t>(args[1])};
}();
CHECK_EQUAL(size_t(2), result.sz);
// First argument is the type int.
CONSTEXPR_CHECK(result.arg0_is_int);
// Second argument is the value 5.
CHECK_EQUAL(size_t(5), result.arg1);
}
//=========================================================================
// 4a: Access specifier predicates
//=========================================================================
//*************************************************************************
TEST(test_is_public)
{
// The first nonstatic data member of MixedAccess (pub) is public.
constexpr auto m0 = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked())[0];
CONSTEXPR_CHECK(etl::meta::is_public(m0));
CONSTEXPR_CHECK_FALSE(etl::meta::is_protected(m0));
CONSTEXPR_CHECK_FALSE(etl::meta::is_private(m0));
}
//*************************************************************************
TEST(test_is_protected)
{
// The second nonstatic data member of MixedAccess (prot) is protected.
constexpr auto m1 = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked())[1];
CONSTEXPR_CHECK_FALSE(etl::meta::is_public(m1));
CONSTEXPR_CHECK(etl::meta::is_protected(m1));
CONSTEXPR_CHECK_FALSE(etl::meta::is_private(m1));
}
//*************************************************************************
TEST(test_is_private)
{
// The third nonstatic data member of MixedAccess (priv) is private.
constexpr auto m2 = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked())[2];
CONSTEXPR_CHECK_FALSE(etl::meta::is_public(m2));
CONSTEXPR_CHECK_FALSE(etl::meta::is_protected(m2));
CONSTEXPR_CHECK(etl::meta::is_private(m2));
}
//=========================================================================
// 4b: Virtual / override predicates
//=========================================================================
//*************************************************************************
TEST(test_is_virtual)
{
// Base::vfunc is virtual.
constexpr auto vfunc = []() consteval
{
auto members = etl::meta::members_of(^^Base, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "vfunc")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(vfunc != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_virtual(vfunc));
}
//*************************************************************************
TEST(test_is_pure_virtual)
{
// AbstractBase::pure is pure-virtual.
constexpr auto pure_fn = []() consteval
{
auto members = etl::meta::members_of(^^AbstractBase, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "pure")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(pure_fn != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_pure_virtual(pure_fn));
CONSTEXPR_CHECK(etl::meta::is_virtual(pure_fn));
}
//*************************************************************************
TEST(test_is_override)
{
// Derived::vfunc is an override.
constexpr auto vfunc = []() consteval
{
auto members = etl::meta::members_of(^^Derived, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "vfunc")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(vfunc != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_override(vfunc));
CONSTEXPR_CHECK(etl::meta::is_virtual(vfunc));
}
//*************************************************************************
TEST(test_non_virtual_member_is_not_virtual)
{
// Base::base_data is not virtual.
// base_data is a data member, so is_virtual should be false.
constexpr auto base_data = []() consteval
{
auto members = etl::meta::nonstatic_data_members_of(^^Base, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::identifier_of(m) == "base_data")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(base_data != etl::meta::info{});
CONSTEXPR_CHECK_FALSE(etl::meta::is_virtual(base_data));
}
//=========================================================================
// 4c: Function property predicates
//=========================================================================
//*************************************************************************
TEST(test_is_defaulted)
{
// SpecialMembers default constructor is defaulted.
constexpr auto def_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_default_constructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(def_ctor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_defaulted(def_ctor));
}
//*************************************************************************
TEST(test_is_deleted)
{
// SpecialMembers copy constructor is deleted.
constexpr auto copy_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_copy_constructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(copy_ctor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_deleted(copy_ctor));
}
//*************************************************************************
TEST(test_is_explicit)
{
// SpecialMembers(int) is explicit.
constexpr auto int_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_constructor(m) && !etl::meta::is_default_constructor(m) && !etl::meta::is_copy_constructor(m)
&& !etl::meta::is_move_constructor(m) && etl::meta::is_explicit(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(int_ctor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_explicit(int_ctor));
}
//*************************************************************************
TEST(test_is_noexcept)
{
// SpecialMembers move constructor is noexcept.
constexpr auto move_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_move_constructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(move_ctor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_noexcept(move_ctor));
}
//=========================================================================
// 4d: Data member / misc predicates
//=========================================================================
//*************************************************************************
TEST(test_is_bit_field)
{
// BitFields::a and ::b are bit-fields.
CONSTEXPR_CHECK(etl::meta::is_bit_field(etl::meta::nonstatic_data_members_of(^^BitFields, etl::meta::access_context::unchecked())[0]));
CONSTEXPR_CHECK(etl::meta::is_bit_field(etl::meta::nonstatic_data_members_of(^^BitFields, etl::meta::access_context::unchecked())[1]));
}
//*************************************************************************
TEST(test_non_bitfield_is_not_bit_field)
{
// SimpleStruct::x is not a bit-field.
CONSTEXPR_CHECK_FALSE(etl::meta::is_bit_field(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]));
}
//*************************************************************************
TEST(test_is_enumerator)
{
// Each element of enumerators_of(^^Color) should satisfy is_enumerator.
constexpr bool all_enumerators = []() consteval
{
auto enums = etl::meta::enumerators_of(^^Color);
for (auto e : enums)
if (!etl::meta::is_enumerator(e))
return false;
return true;
}();
CHECK(all_enumerators);
}
//*************************************************************************
TEST(test_non_enumerator_is_not_enumerator)
{
// A data member is not an enumerator.
CONSTEXPR_CHECK_FALSE(
etl::meta::is_enumerator(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]));
}
//*************************************************************************
TEST(test_is_final)
{
// FinalClass is final.
CONSTEXPR_CHECK(etl::meta::is_final(^^FinalClass));
// Base is not final.
CONSTEXPR_CHECK_FALSE(etl::meta::is_final(^^Base));
}
//=========================================================================
// 4e: CV / ref qualification predicates
//=========================================================================
//*************************************************************************
TEST(test_is_mutable_member)
{
// CVQualifiedMembers::m is mutable.
// members[0] = m (mutable), members[1] = c (const), members[2] = v (volatile)
constexpr auto m0 = etl::meta::nonstatic_data_members_of(^^CVQualifiedMembers, etl::meta::access_context::unchecked())[0];
constexpr auto m1 = etl::meta::nonstatic_data_members_of(^^CVQualifiedMembers, etl::meta::access_context::unchecked())[1];
constexpr auto m2 = etl::meta::nonstatic_data_members_of(^^CVQualifiedMembers, etl::meta::access_context::unchecked())[2];
CONSTEXPR_CHECK(etl::meta::is_mutable_member(m0));
CONSTEXPR_CHECK_FALSE(etl::meta::is_mutable_member(m1));
CONSTEXPR_CHECK_FALSE(etl::meta::is_mutable_member(m2));
}
//*************************************************************************
TEST(test_is_const_on_type)
{
// is_const on ^^const int should be true.
CONSTEXPR_CHECK(etl::meta::is_const(^^const int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_const(^^int));
}
//*************************************************************************
TEST(test_is_volatile_on_type)
{
// is_volatile on ^^volatile int should be true.
CONSTEXPR_CHECK(etl::meta::is_volatile(^^volatile int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_volatile(^^int));
}
//=========================================================================
// 4f: Storage duration & linkage
//=========================================================================
//*************************************************************************
TEST(test_has_static_storage_duration)
{
// WithStatic::s_count has static storage duration.
constexpr auto result = []() consteval
{
auto statics = etl::meta::static_data_members_of(^^WithStatic, etl::meta::access_context::unchecked());
struct R
{
size_t sz;
bool has_static;
};
return R{statics.size(), statics.size() >= 1 && etl::meta::has_static_storage_duration(statics[0])};
}();
CHECK(result.sz >= size_t(1));
CHECK(result.has_static);
}
//*************************************************************************
TEST(test_has_internal_linkage)
{
// internal_var (declared static at namespace scope) has internal linkage.
CONSTEXPR_CHECK(etl::meta::has_internal_linkage(^^internal_var));
CONSTEXPR_CHECK(etl::meta::has_linkage(^^internal_var));
}
//*************************************************************************
TEST(test_has_external_linkage)
{
// ExternalLinkageStruct is defined outside anonymous namespace
// so it has external linkage.
CONSTEXPR_CHECK(etl::meta::has_external_linkage(^^ExternalLinkageStruct));
}
//*************************************************************************
TEST(test_has_linkage)
{
// Types and namespace-scope variables have linkage.
CONSTEXPR_CHECK(etl::meta::has_linkage(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::has_linkage(^^internal_var));
}
//=========================================================================
// 4g: Membership predicates
//=========================================================================
//*************************************************************************
TEST(test_is_class_member)
{
// SimpleStruct::x is a class member.
CONSTEXPR_CHECK(etl::meta::is_class_member(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]));
}
//*************************************************************************
TEST(test_is_namespace_member)
{
// global_const is a namespace member (it lives at namespace scope).
CONSTEXPR_CHECK(etl::meta::is_namespace_member(^^global_const));
// A data member is not a namespace member.
CONSTEXPR_CHECK_FALSE(
etl::meta::is_namespace_member(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]));
}
//*************************************************************************
TEST(test_is_nonstatic_data_member)
{
// SimpleStruct::x is a non-static data member.
CONSTEXPR_CHECK(
etl::meta::is_nonstatic_data_member(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]));
}
//*************************************************************************
TEST(test_is_static_member)
{
// WithStatic::s_count is a static member.
constexpr auto result = []() consteval
{
auto statics = etl::meta::static_data_members_of(^^WithStatic, etl::meta::access_context::unchecked());
struct R
{
size_t sz;
bool is_static;
};
return R{statics.size(), statics.size() >= 1 && etl::meta::is_static_member(statics[0])};
}();
CHECK(result.sz >= size_t(1));
CHECK(result.is_static);
}
//*************************************************************************
TEST(test_is_base)
{
// The base-class specifier in Derived should satisfy is_base.
constexpr auto result = []() consteval
{
auto bases = etl::meta::bases_of(^^Derived, etl::meta::access_context::unchecked());
struct R
{
size_t sz;
bool is_base_val;
};
return R{bases.size(), bases.size() == 1 && etl::meta::is_base(bases[0])};
}();
CHECK_EQUAL(size_t(1), result.sz);
CHECK(result.is_base_val);
}
//=========================================================================
// 4h: Entity kind predicates
//=========================================================================
//*************************************************************************
TEST(test_is_type)
{
CONSTEXPR_CHECK(etl::meta::is_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_type(^^Color));
// A variable is not a type.
CONSTEXPR_CHECK_FALSE(etl::meta::is_type(^^global_const));
}
//*************************************************************************
TEST(test_is_namespace)
{
// The global namespace is a namespace.
CONSTEXPR_CHECK(etl::meta::is_namespace(^^::));
// A type is not a namespace.
CONSTEXPR_CHECK_FALSE(etl::meta::is_namespace(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_function)
{
CONSTEXPR_CHECK(etl::meta::is_function(^^add));
CONSTEXPR_CHECK_FALSE(etl::meta::is_function(^^global_const));
CONSTEXPR_CHECK_FALSE(etl::meta::is_function(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_variable)
{
CONSTEXPR_CHECK(etl::meta::is_variable(^^global_const));
CONSTEXPR_CHECK(etl::meta::is_variable(^^internal_var));
CONSTEXPR_CHECK_FALSE(etl::meta::is_variable(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_type_alias)
{
CONSTEXPR_CHECK(etl::meta::is_type_alias(^^IntAlias));
CONSTEXPR_CHECK_FALSE(etl::meta::is_type_alias(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_type_alias(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_complete_type)
{
CONSTEXPR_CHECK(etl::meta::is_complete_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_complete_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_complete_type(^^Color));
}
//*************************************************************************
TEST(test_is_enumerable_type)
{
// Enums with a definition are enumerable.
CONSTEXPR_CHECK(etl::meta::is_enumerable_type(^^Color));
CONSTEXPR_CHECK(etl::meta::is_enumerable_type(^^Direction));
// A non-enum, non-class scalar is not enumerable.
CONSTEXPR_CHECK_FALSE(etl::meta::is_enumerable_type(^^int));
}
//=========================================================================
// 4i: Template predicates
//=========================================================================
//*************************************************************************
TEST(test_is_template)
{
// FixedArray is a template; its instantiation (dealiased IntArray5) is not.
CONSTEXPR_CHECK(etl::meta::is_template(^^FixedArray));
CONSTEXPR_CHECK_FALSE(etl::meta::is_template(etl::meta::dealias(^^IntArray5)));
}
//*************************************************************************
TEST(test_is_class_template)
{
CONSTEXPR_CHECK(etl::meta::is_class_template(^^FixedArray));
CONSTEXPR_CHECK_FALSE(etl::meta::is_class_template(^^SimpleStruct));
}
//*************************************************************************
TEST(test_has_template_arguments)
{
// An instantiation has template arguments; the primary template does not.
// IntArray5 is a type alias, so dealias to get the instantiation.
CONSTEXPR_CHECK(etl::meta::has_template_arguments(etl::meta::dealias(^^IntArray5)));
CONSTEXPR_CHECK_FALSE(etl::meta::has_template_arguments(^^SimpleStruct));
}
//=========================================================================
// 4j: Special member function predicates
//=========================================================================
//*************************************************************************
TEST(test_is_constructor)
{
// SpecialMembers has constructors; find at least one.
constexpr bool found = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_constructor(m))
return true;
return false;
}();
CHECK(found);
}
//*************************************************************************
TEST(test_is_default_constructor)
{
constexpr auto def_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_default_constructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(def_ctor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_special_member_function(def_ctor));
}
//*************************************************************************
TEST(test_is_copy_constructor)
{
constexpr auto copy_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_copy_constructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(copy_ctor != etl::meta::info{});
// This one is deleted.
CONSTEXPR_CHECK(etl::meta::is_deleted(copy_ctor));
}
//*************************************************************************
TEST(test_is_move_constructor)
{
constexpr auto move_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_move_constructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(move_ctor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_noexcept(move_ctor));
CONSTEXPR_CHECK(etl::meta::is_defaulted(move_ctor));
}
//*************************************************************************
TEST(test_is_destructor)
{
constexpr auto dtor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_destructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(dtor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_special_member_function(dtor));
CONSTEXPR_CHECK(etl::meta::is_defaulted(dtor));
}
//*************************************************************************
TEST(test_is_copy_assignment)
{
constexpr auto copy_assign = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_copy_assignment(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(copy_assign != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_assignment(copy_assign));
CONSTEXPR_CHECK(etl::meta::is_defaulted(copy_assign));
}
//*************************************************************************
TEST(test_is_move_assignment)
{
constexpr auto move_assign = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_move_assignment(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(move_assign != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_assignment(move_assign));
CONSTEXPR_CHECK(etl::meta::is_noexcept(move_assign));
}
//*************************************************************************
TEST(test_is_user_provided)
{
// SpecialMembers(int) is user-provided (has a body).
constexpr auto int_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_constructor(m) && !etl::meta::is_default_constructor(m) && !etl::meta::is_copy_constructor(m)
&& !etl::meta::is_move_constructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(int_ctor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_user_provided(int_ctor));
}
//*************************************************************************
TEST(test_is_user_declared)
{
// All special members of SpecialMembers are user-declared.
constexpr auto def_ctor = []() consteval
{
auto members = etl::meta::members_of(^^SpecialMembers, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_default_constructor(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(def_ctor != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_user_declared(def_ctor));
}
//=========================================================================
// 4k: Value / object predicates
//=========================================================================
//*************************************************************************
TEST(test_is_value)
{
// A reflected constant value is a value.
constexpr auto val = etl::meta::reflect_constant(42);
CONSTEXPR_CHECK(etl::meta::is_value(val));
// A type is not a value.
CONSTEXPR_CHECK_FALSE(etl::meta::is_value(^^int));
}
//*************************************************************************
TEST(test_is_object)
{
// In GCC 16, ^^variable reflects the variable, not the object.
// Use object_of to get the object reflection.
CONSTEXPR_CHECK(etl::meta::is_object(etl::meta::object_of(^^global_const)));
// A type is not an object.
CONSTEXPR_CHECK_FALSE(etl::meta::is_object(^^int));
// A function is not an object.
CONSTEXPR_CHECK_FALSE(etl::meta::is_object(^^add));
}
//*************************************************************************
TEST(test_has_default_member_initializer)
{
// Base::base_data has a default member initializer (= 0).
constexpr auto base_data = []() consteval
{
auto members = etl::meta::nonstatic_data_members_of(^^Base, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::identifier_of(m) == "base_data")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(base_data != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::has_default_member_initializer(base_data));
// SimpleStruct::x has no default member initializer.
CONSTEXPR_CHECK_FALSE(
etl::meta::has_default_member_initializer(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]));
}
//=========================================================================
// Access context & member queries
//=========================================================================
//*************************************************************************
TEST(test_access_context_unchecked)
{
// unchecked() grants access to all members, including private ones.
constexpr auto sz = []() consteval
{
auto members = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked());
return members.size();
}();
CHECK_EQUAL(size_t(3), sz);
}
//*************************************************************************
TEST(test_access_context_unprivileged)
{
// unprivileged() only sees public members.
constexpr auto result = []() consteval
{
auto members = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unprivileged());
struct R
{
size_t sz;
meta_string_view name;
};
return R{members.size(), members.size() >= 1 ? etl::meta::identifier_of(members[0]) : ""};
}();
CHECK_EQUAL(size_t(1), result.sz);
CHECK(meta_string_view("pub") == result.name);
}
//*************************************************************************
TEST(test_is_accessible_public_member)
{
constexpr auto result = []() consteval
{
auto all = etl::meta::nonstatic_data_members_of(^^MixedAccess, etl::meta::access_context::unchecked());
struct R
{
bool pub_accessible;
bool priv_accessible;
};
return R{etl::meta::is_accessible(all[0], etl::meta::access_context::unprivileged()),
etl::meta::is_accessible(all[2], etl::meta::access_context::unprivileged())};
}();
// pub is accessible from unprivileged context.
CHECK(result.pub_accessible);
// priv is not accessible from unprivileged context.
CHECK(!result.priv_accessible);
}
//*************************************************************************
TEST(test_members_of)
{
// members_of returns all members (functions, types, data) of a class.
constexpr auto sz = []() consteval
{
auto members = etl::meta::members_of(^^SimpleStruct, etl::meta::access_context::unchecked());
return members.size();
}();
// SimpleStruct has at least x and y.
CHECK(sz >= size_t(2));
}
//*************************************************************************
TEST(test_bases_of)
{
// Derived has one base (Base).
constexpr auto sz = []() consteval
{
auto bases = etl::meta::bases_of(^^Derived, etl::meta::access_context::unchecked());
return bases.size();
}();
CHECK_EQUAL(size_t(1), sz);
}
//*************************************************************************
TEST(test_bases_of_no_bases)
{
// SimpleStruct has no base classes.
constexpr auto sz = []() consteval
{
auto bases = etl::meta::bases_of(^^SimpleStruct, etl::meta::access_context::unchecked());
return bases.size();
}();
CHECK_EQUAL(size_t(0), sz);
}
//*************************************************************************
TEST(test_static_data_members_of)
{
// WithStatic has one static data member (s_count).
constexpr auto result = []() consteval
{
auto statics = etl::meta::static_data_members_of(^^WithStatic, etl::meta::access_context::unchecked());
struct R
{
size_t sz;
meta_string_view name;
};
return R{statics.size(), statics.size() >= 1 ? etl::meta::identifier_of(statics[0]) : ""};
}();
CHECK_EQUAL(size_t(1), result.sz);
CHECK(meta_string_view("s_count") == result.name);
}
//*************************************************************************
TEST(test_nonstatic_data_members_of)
{
// WithStatic has one non-static data member (value).
constexpr auto result = []() consteval
{
auto nonstatics = etl::meta::nonstatic_data_members_of(^^WithStatic, etl::meta::access_context::unchecked());
struct R
{
size_t sz;
meta_string_view name;
};
return R{nonstatics.size(), nonstatics.size() >= 1 ? etl::meta::identifier_of(nonstatics[0]) : ""};
}();
CHECK_EQUAL(size_t(1), result.sz);
CHECK(meta_string_view("value") == result.name);
}
//*************************************************************************
TEST(test_enumerators_of)
{
// Color has 3 enumerators, Direction has 4.
constexpr auto color_sz = []() consteval
{
return etl::meta::enumerators_of(^^Color).size();
}();
CHECK_EQUAL(size_t(3), color_sz);
constexpr auto dir_sz = []() consteval
{
return etl::meta::enumerators_of(^^Direction).size();
}();
CHECK_EQUAL(size_t(4), dir_sz);
}
//*************************************************************************
TEST(test_has_inaccessible_nonstatic_data_members)
{
// MixedAccess has inaccessible members from unprivileged context.
CONSTEXPR_CHECK(etl::meta::has_inaccessible_nonstatic_data_members(^^MixedAccess, etl::meta::access_context::unprivileged()));
// SimpleStruct has no inaccessible members (all public).
CONSTEXPR_CHECK_FALSE(etl::meta::has_inaccessible_nonstatic_data_members(^^SimpleStruct, etl::meta::access_context::unprivileged()));
}
//*************************************************************************
TEST(test_has_inaccessible_bases)
{
// Derived's base (Base) is public, so no inaccessible bases.
CONSTEXPR_CHECK_FALSE(etl::meta::has_inaccessible_bases(^^Derived, etl::meta::access_context::unprivileged()));
}
//=========================================================================
// substitute / can_substitute / extract
//=========================================================================
//*************************************************************************
TEST(test_substitute)
{
// substitute(^^FixedArray, {^^double, reflect_constant(size_t(10))})
// should produce FixedArray<double, 10>.
constexpr auto result = etl::meta::substitute(^^FixedArray, {^^double, etl::meta::reflect_constant(size_t(10))});
CONSTEXPR_CHECK(etl::meta::is_type(result));
CONSTEXPR_CHECK(etl::meta::is_complete_type(result));
// Verify the template arguments round-trip.
constexpr auto args_check = [&]() consteval
{
auto args = etl::meta::template_arguments_of(result);
struct R
{
bool arg0_is_double;
size_t arg1;
};
return R{args[0] == ^^double, etl::meta::extract<size_t>(args[1])};
}();
CONSTEXPR_CHECK(args_check.arg0_is_double);
CHECK_EQUAL(size_t(10), args_check.arg1);
}
//*************************************************************************
TEST(test_can_substitute_valid)
{
// FixedArray<int, 3> is valid.
CONSTEXPR_CHECK(etl::meta::can_substitute(^^FixedArray, {^^int, etl::meta::reflect_constant(size_t(3))}));
}
//*************************************************************************
TEST(test_extract_int)
{
// Extract an int from a reflected constant.
constexpr auto r = etl::meta::reflect_constant(123);
constexpr int val = etl::meta::extract<int>(r);
CHECK_EQUAL(123, val);
}
//*************************************************************************
TEST(test_extract_bool)
{
constexpr auto r = etl::meta::reflect_constant(true);
constexpr bool val = etl::meta::extract<bool>(r);
CHECK(val);
}
//=========================================================================
// 17a: Primary type categories
// (These tests require substitute, which is STL-only.)
//=========================================================================
//*************************************************************************
TEST(test_is_void_type)
{
CONSTEXPR_CHECK(etl::meta::is_void_type(^^void));
CONSTEXPR_CHECK_FALSE(etl::meta::is_void_type(^^int));
}
//*************************************************************************
TEST(test_is_null_pointer_type)
{
CONSTEXPR_CHECK(etl::meta::is_null_pointer_type(^^decltype(nullptr)));
CONSTEXPR_CHECK_FALSE(etl::meta::is_null_pointer_type(^^int));
}
//*************************************************************************
TEST(test_is_integral_type)
{
CONSTEXPR_CHECK(etl::meta::is_integral_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_integral_type(^^bool));
CONSTEXPR_CHECK(etl::meta::is_integral_type(^^char));
CONSTEXPR_CHECK(etl::meta::is_integral_type(^^unsigned long long));
CONSTEXPR_CHECK_FALSE(etl::meta::is_integral_type(^^double));
CONSTEXPR_CHECK_FALSE(etl::meta::is_integral_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_floating_point_type)
{
CONSTEXPR_CHECK(etl::meta::is_floating_point_type(^^float));
CONSTEXPR_CHECK(etl::meta::is_floating_point_type(^^double));
CONSTEXPR_CHECK(etl::meta::is_floating_point_type(^^long double));
CONSTEXPR_CHECK_FALSE(etl::meta::is_floating_point_type(^^int));
}
//*************************************************************************
TEST(test_is_array_type)
{
CONSTEXPR_CHECK(etl::meta::is_array_type(^^int[5]));
CONSTEXPR_CHECK(etl::meta::is_array_type(^^int[]));
CONSTEXPR_CHECK_FALSE(etl::meta::is_array_type(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_array_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_pointer_type)
{
CONSTEXPR_CHECK(etl::meta::is_pointer_type(^^int*));
CONSTEXPR_CHECK(etl::meta::is_pointer_type(^^const char*));
CONSTEXPR_CHECK_FALSE(etl::meta::is_pointer_type(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_pointer_type(^^int&));
}
//*************************************************************************
TEST(test_is_lvalue_reference_type)
{
CONSTEXPR_CHECK(etl::meta::is_lvalue_reference_type(^^int&));
CONSTEXPR_CHECK(etl::meta::is_lvalue_reference_type(^^const int&));
CONSTEXPR_CHECK_FALSE(etl::meta::is_lvalue_reference_type(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_lvalue_reference_type(^^int&&));
}
//*************************************************************************
TEST(test_is_rvalue_reference_type)
{
CONSTEXPR_CHECK(etl::meta::is_rvalue_reference_type(^^int&&));
CONSTEXPR_CHECK_FALSE(etl::meta::is_rvalue_reference_type(^^int&));
CONSTEXPR_CHECK_FALSE(etl::meta::is_rvalue_reference_type(^^int));
}
//*************************************************************************
TEST(test_is_member_object_pointer_type)
{
CONSTEXPR_CHECK(etl::meta::is_member_object_pointer_type(^^int SimpleStruct::*));
CONSTEXPR_CHECK_FALSE(etl::meta::is_member_object_pointer_type(^^int*));
}
//*************************************************************************
TEST(test_is_member_function_pointer_type)
{
CONSTEXPR_CHECK(etl::meta::is_member_function_pointer_type(^^void(Base::*)()));
CONSTEXPR_CHECK_FALSE(etl::meta::is_member_function_pointer_type(^^int SimpleStruct::*));
}
//*************************************************************************
TEST(test_is_enum_type)
{
CONSTEXPR_CHECK(etl::meta::is_enum_type(^^Color));
CONSTEXPR_CHECK(etl::meta::is_enum_type(^^Direction));
CONSTEXPR_CHECK_FALSE(etl::meta::is_enum_type(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_enum_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_union_type)
{
CONSTEXPR_CHECK(etl::meta::is_union_type(^^TestUnion));
CONSTEXPR_CHECK_FALSE(etl::meta::is_union_type(^^SimpleStruct));
CONSTEXPR_CHECK_FALSE(etl::meta::is_union_type(^^int));
}
//*************************************************************************
TEST(test_is_class_type)
{
CONSTEXPR_CHECK(etl::meta::is_class_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_class_type(^^Base));
CONSTEXPR_CHECK_FALSE(etl::meta::is_class_type(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_class_type(^^Color));
}
//*************************************************************************
TEST(test_is_function_type)
{
CONSTEXPR_CHECK(etl::meta::is_function_type(^^int(int, int)));
CONSTEXPR_CHECK_FALSE(etl::meta::is_function_type(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_function_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_reflection_type)
{
// ^^etl::meta::info doesn't work when info is a using-declaration
// (a using-declarator cannot be reflected).
// In the std <meta> path etl::meta::info is a using-declaration for
// std::meta::info, so reflect the std name there.
// etl::meta::info is a using-declaration for std::meta::info and a
// using-declarator cannot be reflected, so reflect the std name.
CONSTEXPR_CHECK(etl::meta::is_reflection_type(^^std::meta::info));
CONSTEXPR_CHECK_FALSE(etl::meta::is_reflection_type(^^int));
}
//=========================================================================
// 17b: Composite type categories
//=========================================================================
//*************************************************************************
TEST(test_is_reference_type)
{
CONSTEXPR_CHECK(etl::meta::is_reference_type(^^int&));
CONSTEXPR_CHECK(etl::meta::is_reference_type(^^int&&));
CONSTEXPR_CHECK_FALSE(etl::meta::is_reference_type(^^int));
}
//*************************************************************************
TEST(test_is_arithmetic_type)
{
CONSTEXPR_CHECK(etl::meta::is_arithmetic_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_arithmetic_type(^^double));
CONSTEXPR_CHECK_FALSE(etl::meta::is_arithmetic_type(^^SimpleStruct));
CONSTEXPR_CHECK_FALSE(etl::meta::is_arithmetic_type(^^int*));
}
//*************************************************************************
TEST(test_is_fundamental_type)
{
CONSTEXPR_CHECK(etl::meta::is_fundamental_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_fundamental_type(^^void));
CONSTEXPR_CHECK(etl::meta::is_fundamental_type(^^decltype(nullptr)));
CONSTEXPR_CHECK_FALSE(etl::meta::is_fundamental_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_object_type)
{
CONSTEXPR_CHECK(etl::meta::is_object_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_object_type(^^SimpleStruct));
CONSTEXPR_CHECK_FALSE(etl::meta::is_object_type(^^void));
CONSTEXPR_CHECK_FALSE(etl::meta::is_object_type(^^int&));
}
//*************************************************************************
TEST(test_is_scalar_type)
{
CONSTEXPR_CHECK(etl::meta::is_scalar_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_scalar_type(^^int*));
CONSTEXPR_CHECK(etl::meta::is_scalar_type(^^Color));
CONSTEXPR_CHECK_FALSE(etl::meta::is_scalar_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_compound_type)
{
CONSTEXPR_CHECK(etl::meta::is_compound_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_compound_type(^^int*));
CONSTEXPR_CHECK(etl::meta::is_compound_type(^^int&));
CONSTEXPR_CHECK(etl::meta::is_compound_type(^^Color));
CONSTEXPR_CHECK_FALSE(etl::meta::is_compound_type(^^int));
}
//*************************************************************************
TEST(test_is_member_pointer_type)
{
CONSTEXPR_CHECK(etl::meta::is_member_pointer_type(^^int SimpleStruct::*));
CONSTEXPR_CHECK(etl::meta::is_member_pointer_type(^^void(Base::*)()));
CONSTEXPR_CHECK_FALSE(etl::meta::is_member_pointer_type(^^int*));
}
//=========================================================================
// 17c: Type properties
//=========================================================================
//*************************************************************************
TEST(test_is_const_type)
{
CONSTEXPR_CHECK(etl::meta::is_const_type(^^const int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_const_type(^^int));
}
//*************************************************************************
TEST(test_is_volatile_type)
{
CONSTEXPR_CHECK(etl::meta::is_volatile_type(^^volatile int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_volatile_type(^^int));
}
//*************************************************************************
TEST(test_is_trivially_copyable_type)
{
CONSTEXPR_CHECK(etl::meta::is_trivially_copyable_type(^^Trivial));
CONSTEXPR_CHECK(etl::meta::is_trivially_copyable_type(^^int));
// Polymorphic types are not trivially copyable.
CONSTEXPR_CHECK_FALSE(etl::meta::is_trivially_copyable_type(^^Polymorphic));
}
//*************************************************************************
TEST(test_is_empty_type)
{
CONSTEXPR_CHECK(etl::meta::is_empty_type(^^Empty));
CONSTEXPR_CHECK_FALSE(etl::meta::is_empty_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_polymorphic_type)
{
CONSTEXPR_CHECK(etl::meta::is_polymorphic_type(^^Polymorphic));
CONSTEXPR_CHECK(etl::meta::is_polymorphic_type(^^Base));
CONSTEXPR_CHECK_FALSE(etl::meta::is_polymorphic_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_abstract_type)
{
CONSTEXPR_CHECK(etl::meta::is_abstract_type(^^AbstractBase));
CONSTEXPR_CHECK_FALSE(etl::meta::is_abstract_type(^^Base));
CONSTEXPR_CHECK_FALSE(etl::meta::is_abstract_type(^^SimpleStruct));
}
//*************************************************************************
TEST(test_is_final_type)
{
CONSTEXPR_CHECK(etl::meta::is_final_type(^^FinalClass));
CONSTEXPR_CHECK_FALSE(etl::meta::is_final_type(^^Base));
}
//*************************************************************************
TEST(test_is_aggregate_type)
{
CONSTEXPR_CHECK(etl::meta::is_aggregate_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_aggregate_type(^^Empty));
CONSTEXPR_CHECK(etl::meta::is_aggregate_type(^^Trivial));
// SpecialMembers has user-declared constructors, so it's not aggregate.
CONSTEXPR_CHECK_FALSE(etl::meta::is_aggregate_type(^^SpecialMembers));
}
//*************************************************************************
TEST(test_is_signed_type)
{
CONSTEXPR_CHECK(etl::meta::is_signed_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_signed_type(^^double));
CONSTEXPR_CHECK_FALSE(etl::meta::is_signed_type(^^unsigned int));
}
//*************************************************************************
TEST(test_is_unsigned_type)
{
CONSTEXPR_CHECK(etl::meta::is_unsigned_type(^^unsigned int));
CONSTEXPR_CHECK(etl::meta::is_unsigned_type(^^bool));
CONSTEXPR_CHECK_FALSE(etl::meta::is_unsigned_type(^^int));
}
//*************************************************************************
TEST(test_is_bounded_array_type)
{
CONSTEXPR_CHECK(etl::meta::is_bounded_array_type(^^int[5]));
CONSTEXPR_CHECK_FALSE(etl::meta::is_bounded_array_type(^^int[]));
CONSTEXPR_CHECK_FALSE(etl::meta::is_bounded_array_type(^^int));
}
//*************************************************************************
TEST(test_is_unbounded_array_type)
{
CONSTEXPR_CHECK(etl::meta::is_unbounded_array_type(^^int[]));
CONSTEXPR_CHECK_FALSE(etl::meta::is_unbounded_array_type(^^int[5]));
CONSTEXPR_CHECK_FALSE(etl::meta::is_unbounded_array_type(^^int));
}
//*************************************************************************
TEST(test_is_scoped_enum_type)
{
CONSTEXPR_CHECK(etl::meta::is_scoped_enum_type(^^Color));
CONSTEXPR_CHECK_FALSE(etl::meta::is_scoped_enum_type(^^Direction));
CONSTEXPR_CHECK_FALSE(etl::meta::is_scoped_enum_type(^^int));
}
//=========================================================================
// 17d: Constructible / assignable / destructible
//=========================================================================
//*************************************************************************
TEST(test_is_default_constructible_type)
{
CONSTEXPR_CHECK(etl::meta::is_default_constructible_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_default_constructible_type(^^int));
}
//*************************************************************************
TEST(test_is_copy_constructible_type)
{
CONSTEXPR_CHECK(etl::meta::is_copy_constructible_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_copy_constructible_type(^^int));
// SpecialMembers has a deleted copy constructor.
CONSTEXPR_CHECK_FALSE(etl::meta::is_copy_constructible_type(^^SpecialMembers));
}
//*************************************************************************
TEST(test_is_move_constructible_type)
{
CONSTEXPR_CHECK(etl::meta::is_move_constructible_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_move_constructible_type(^^int));
CONSTEXPR_CHECK(etl::meta::is_move_constructible_type(^^SpecialMembers));
}
//*************************************************************************
TEST(test_is_constructible_type)
{
// int is constructible from int.
CONSTEXPR_CHECK(etl::meta::is_constructible_type(^^int, {^^int}));
// SpecialMembers is constructible from int (explicit ctor).
CONSTEXPR_CHECK(etl::meta::is_constructible_type(^^SpecialMembers, {^^int}));
}
//*************************************************************************
TEST(test_is_assignable_type)
{
CONSTEXPR_CHECK(etl::meta::is_assignable_type(^^int&, ^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_assignable_type(^^int, ^^int));
}
//*************************************************************************
TEST(test_is_copy_assignable_type)
{
CONSTEXPR_CHECK(etl::meta::is_copy_assignable_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_copy_assignable_type(^^int));
}
//*************************************************************************
TEST(test_is_move_assignable_type)
{
CONSTEXPR_CHECK(etl::meta::is_move_assignable_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_move_assignable_type(^^SpecialMembers));
}
//*************************************************************************
TEST(test_is_destructible_type)
{
CONSTEXPR_CHECK(etl::meta::is_destructible_type(^^SimpleStruct));
CONSTEXPR_CHECK(etl::meta::is_destructible_type(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_destructible_type(^^void));
}
//*************************************************************************
TEST(test_is_trivially_copy_constructible_type)
{
CONSTEXPR_CHECK(etl::meta::is_trivially_copy_constructible_type(^^Trivial));
CONSTEXPR_CHECK(etl::meta::is_trivially_copy_constructible_type(^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_trivially_copy_constructible_type(^^Polymorphic));
}
//*************************************************************************
TEST(test_is_trivially_copy_assignable_type)
{
CONSTEXPR_CHECK(etl::meta::is_trivially_copy_assignable_type(^^Trivial));
CONSTEXPR_CHECK(etl::meta::is_trivially_copy_assignable_type(^^int));
}
//*************************************************************************
TEST(test_is_trivially_destructible_type)
{
CONSTEXPR_CHECK(etl::meta::is_trivially_destructible_type(^^Trivial));
CONSTEXPR_CHECK(etl::meta::is_trivially_destructible_type(^^int));
// Polymorphic has a virtual destructor, so not trivially destructible.
CONSTEXPR_CHECK_FALSE(etl::meta::is_trivially_destructible_type(^^Polymorphic));
}
//=========================================================================
// 17e: Type property queries (rank, extent)
//=========================================================================
//*************************************************************************
TEST(test_rank)
{
CONSTEXPR_CHECK_EQUAL(size_t(0), etl::meta::rank(^^int));
CONSTEXPR_CHECK_EQUAL(size_t(1), etl::meta::rank(^^int[5]));
CONSTEXPR_CHECK_EQUAL(size_t(2), etl::meta::rank(^^int[3][4]));
}
//*************************************************************************
TEST(test_extent)
{
CONSTEXPR_CHECK_EQUAL(size_t(5), etl::meta::extent(^^int[5]));
CONSTEXPR_CHECK_EQUAL(size_t(3), etl::meta::extent(^^int[3][4], 0));
CONSTEXPR_CHECK_EQUAL(size_t(4), etl::meta::extent(^^int[3][4], 1));
// Unbounded first dimension has extent 0.
CONSTEXPR_CHECK_EQUAL(size_t(0), etl::meta::extent(^^int[]));
}
//=========================================================================
// 17f: Type relations
//=========================================================================
//*************************************************************************
TEST(test_is_same_type)
{
CONSTEXPR_CHECK(etl::meta::is_same_type(^^int, ^^int));
CONSTEXPR_CHECK_FALSE(etl::meta::is_same_type(^^int, ^^double));
CONSTEXPR_CHECK_FALSE(etl::meta::is_same_type(^^int, ^^const int));
}
//*************************************************************************
TEST(test_is_base_of_type)
{
CONSTEXPR_CHECK(etl::meta::is_base_of_type(^^Base, ^^Derived));
CONSTEXPR_CHECK(etl::meta::is_base_of_type(^^Base, ^^FinalClass));
CONSTEXPR_CHECK_FALSE(etl::meta::is_base_of_type(^^Derived, ^^Base));
CONSTEXPR_CHECK_FALSE(etl::meta::is_base_of_type(^^SimpleStruct, ^^Derived));
}
//*************************************************************************
TEST(test_is_convertible_type)
{
CONSTEXPR_CHECK(etl::meta::is_convertible_type(^^int, ^^double));
CONSTEXPR_CHECK(etl::meta::is_convertible_type(^^Derived*, ^^Base*));
CONSTEXPR_CHECK_FALSE(etl::meta::is_convertible_type(^^Base*, ^^Derived*));
}
//*************************************************************************
TEST(test_has_virtual_destructor)
{
CONSTEXPR_CHECK(etl::meta::has_virtual_destructor(^^Base));
CONSTEXPR_CHECK(etl::meta::has_virtual_destructor(^^Polymorphic));
CONSTEXPR_CHECK_FALSE(etl::meta::has_virtual_destructor(^^SimpleStruct));
CONSTEXPR_CHECK_FALSE(etl::meta::has_virtual_destructor(^^Trivial));
}
//=========================================================================
// Type transformations
//=========================================================================
//*************************************************************************
TEST(test_remove_const)
{
CONSTEXPR_CHECK(etl::meta::remove_const(^^const int) == ^^int);
// Removing const from non-const is a no-op.
CONSTEXPR_CHECK(etl::meta::remove_const(^^int) == ^^int);
}
//*************************************************************************
TEST(test_remove_volatile)
{
CONSTEXPR_CHECK(etl::meta::remove_volatile(^^volatile int) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_volatile(^^int) == ^^int);
}
//*************************************************************************
TEST(test_remove_cv)
{
CONSTEXPR_CHECK(etl::meta::remove_cv(^^const volatile int) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_cv(^^const int) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_cv(^^volatile int) == ^^int);
}
//*************************************************************************
TEST(test_add_const)
{
CONSTEXPR_CHECK(etl::meta::add_const(^^int) == ^^const int);
// Adding const to already-const is idempotent.
CONSTEXPR_CHECK(etl::meta::add_const(^^const int) == ^^const int);
}
//*************************************************************************
TEST(test_add_volatile)
{
CONSTEXPR_CHECK(etl::meta::add_volatile(^^int) == ^^volatile int);
}
//*************************************************************************
TEST(test_add_cv)
{
CONSTEXPR_CHECK(etl::meta::add_cv(^^int) == ^^const volatile int);
}
//*************************************************************************
TEST(test_remove_reference)
{
CONSTEXPR_CHECK(etl::meta::remove_reference(^^int&) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_reference(^^int&&) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_reference(^^int) == ^^int);
}
//*************************************************************************
TEST(test_add_lvalue_reference)
{
CONSTEXPR_CHECK(etl::meta::add_lvalue_reference(^^int) == ^^int&);
}
//*************************************************************************
TEST(test_add_rvalue_reference)
{
CONSTEXPR_CHECK(etl::meta::add_rvalue_reference(^^int) == ^^int&&);
}
//*************************************************************************
TEST(test_make_signed)
{
CONSTEXPR_CHECK(etl::meta::make_signed(^^unsigned int) == ^^int);
}
//*************************************************************************
TEST(test_make_unsigned)
{
CONSTEXPR_CHECK(etl::meta::make_unsigned(^^int) == ^^unsigned int);
}
//*************************************************************************
TEST(test_remove_extent)
{
// remove_extent(int[5]) -> int
CONSTEXPR_CHECK(etl::meta::remove_extent(^^int[5]) == ^^int);
// remove_extent(int[3][4]) -> int[4]
CONSTEXPR_CHECK(etl::meta::remove_extent(^^int[3][4]) == ^^int[4]);
}
//*************************************************************************
TEST(test_remove_all_extents)
{
CONSTEXPR_CHECK(etl::meta::remove_all_extents(^^int[3][4]) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_all_extents(^^int[5]) == ^^int);
}
//*************************************************************************
TEST(test_remove_pointer)
{
CONSTEXPR_CHECK(etl::meta::remove_pointer(^^int*) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_pointer(^^const int*) == ^^const int);
// Non-pointer is unchanged.
CONSTEXPR_CHECK(etl::meta::remove_pointer(^^int) == ^^int);
}
//*************************************************************************
TEST(test_add_pointer)
{
CONSTEXPR_CHECK(etl::meta::add_pointer(^^int) == ^^int*);
CONSTEXPR_CHECK(etl::meta::add_pointer(^^const int) == ^^const int*);
}
//*************************************************************************
TEST(test_remove_cvref)
{
CONSTEXPR_CHECK(etl::meta::remove_cvref(^^const int&) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_cvref(^^volatile int&&) == ^^int);
CONSTEXPR_CHECK(etl::meta::remove_cvref(^^const volatile int) == ^^int);
}
//*************************************************************************
TEST(test_decay)
{
// Array decays to pointer.
CONSTEXPR_CHECK(etl::meta::decay(^^int[5]) == ^^int*);
// Function decays to function pointer.
CONSTEXPR_CHECK(etl::meta::decay(^^int(int)) == ^^int (*)(int));
// CV-ref is stripped.
CONSTEXPR_CHECK(etl::meta::decay(^^const int&) == ^^int);
}
//*************************************************************************
TEST(test_common_type)
{
// common_type(int, double) -> double
constexpr auto ct = etl::meta::common_type({^^int, ^^double});
CONSTEXPR_CHECK(ct == ^^double);
}
//*************************************************************************
TEST(test_underlying_type)
{
// Color is an enum class; its underlying type is int by default.
constexpr auto ut = etl::meta::underlying_type(^^Color);
CONSTEXPR_CHECK(etl::meta::is_integral_type(ut));
}
//=========================================================================
// Data layout
//=========================================================================
//*************************************************************************
TEST(test_size_of_type)
{
CONSTEXPR_CHECK_EQUAL(sizeof(int), etl::meta::size_of(^^int));
CONSTEXPR_CHECK_EQUAL(sizeof(double), etl::meta::size_of(^^double));
CONSTEXPR_CHECK_EQUAL(sizeof(SimpleStruct), etl::meta::size_of(^^SimpleStruct));
}
//*************************************************************************
TEST(test_alignment_of_type)
{
CONSTEXPR_CHECK_EQUAL(alignof(int), etl::meta::alignment_of(^^int));
CONSTEXPR_CHECK_EQUAL(alignof(double), etl::meta::alignment_of(^^double));
CONSTEXPR_CHECK_EQUAL(alignof(SimpleStruct), etl::meta::alignment_of(^^SimpleStruct));
}
//*************************************************************************
TEST(test_bit_size_of_bit_field)
{
// BitFields::a is 3 bits, BitFields::b is 5 bits.
CONSTEXPR_CHECK_EQUAL(size_t(3),
etl::meta::bit_size_of(etl::meta::nonstatic_data_members_of(^^BitFields, etl::meta::access_context::unchecked())[0]));
CONSTEXPR_CHECK_EQUAL(size_t(5),
etl::meta::bit_size_of(etl::meta::nonstatic_data_members_of(^^BitFields, etl::meta::access_context::unchecked())[1]));
}
//*************************************************************************
TEST(test_offset_of)
{
// The first member of SimpleStruct should be at offset {0, 0}.
constexpr auto off = etl::meta::offset_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[0]);
CHECK_EQUAL(ptrdiff_t(0), off.bytes);
CHECK_EQUAL(ptrdiff_t(0), off.bits);
}
//*************************************************************************
TEST(test_offset_of_second_member)
{
// The second member of SimpleStruct (double y) should be at a non-zero offset.
constexpr auto off = etl::meta::offset_of(etl::meta::nonstatic_data_members_of(^^SimpleStruct, etl::meta::access_context::unchecked())[1]);
// double typically follows int with padding; offset should be > 0.
CHECK(off.bytes > ptrdiff_t(0));
}
//*************************************************************************
TEST(test_member_offset_total_bits)
{
etl::meta::member_offset off{2, 3};
CHECK_EQUAL(ptrdiff_t(2 * __CHAR_BIT__ + 3), off.total_bits());
}
//*************************************************************************
TEST(test_member_offset_comparison)
{
etl::meta::member_offset a{0, 0};
etl::meta::member_offset b{1, 0};
CHECK(a < b);
CHECK(a == a);
CHECK(!(a == b));
}
//=========================================================================
// reflect_constant / reflect_object / reflect_function
//=========================================================================
//*************************************************************************
TEST(test_reflect_constant_int)
{
constexpr auto r = etl::meta::reflect_constant(42);
constexpr int val = etl::meta::extract<int>(r);
CHECK_EQUAL(42, val);
}
//*************************************************************************
TEST(test_reflect_constant_char)
{
constexpr auto r = etl::meta::reflect_constant('A');
constexpr char val = etl::meta::extract<char>(r);
CHECK_EQUAL('A', val);
}
//*************************************************************************
TEST(test_reflect_constant_negative)
{
constexpr auto r = etl::meta::reflect_constant(-7);
constexpr int val = etl::meta::extract<int>(r);
CHECK_EQUAL(-7, val);
}
//*************************************************************************
TEST(test_reflect_object)
{
// reflect_object on global_const should give a reflection of the object.
constexpr auto r = etl::meta::reflect_object(global_const);
CONSTEXPR_CHECK(etl::meta::is_object(r));
}
//*************************************************************************
TEST(test_reflect_function)
{
// reflect_function on add should give a reflection of the function.
constexpr auto r = etl::meta::reflect_function(add);
CONSTEXPR_CHECK(etl::meta::is_function(r));
}
//=========================================================================
// define_aggregate / data_member_spec
//=========================================================================
// GCC warns about missing field initializers in designated-init of
// data_member_options, but omitted fields are intentionally defaulted.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
//*************************************************************************
TEST(test_data_member_spec_basic)
{
// Create a data member spec for an int named "value".
constexpr auto spec = etl::meta::data_member_spec(^^int, {.name = "value"});
CONSTEXPR_CHECK(etl::meta::is_data_member_spec(spec));
}
//*************************************************************************
TEST(test_data_member_spec_unnamed)
{
// Create a data member spec without an explicit name (using bit-width).
// GCC 16 uses .bit_width; Bloomberg clang-p2996 uses .width.
constexpr auto spec = etl::meta::data_member_spec(^^unsigned, {.bit_width = 8});
CONSTEXPR_CHECK(etl::meta::is_data_member_spec(spec));
}
//*************************************************************************
TEST(test_data_member_spec_with_no_unique_address)
{
// Create a data member spec with no_unique_address.
constexpr auto spec = etl::meta::data_member_spec(^^int, {.name = "val", .no_unique_address = true});
CONSTEXPR_CHECK(etl::meta::is_data_member_spec(spec));
}
//*************************************************************************
TEST(test_data_member_spec_bit_field)
{
// Create a bit-field data member spec.
constexpr auto spec = etl::meta::data_member_spec(^^unsigned, {.name = "bits", .bit_width = 4});
CONSTEXPR_CHECK(etl::meta::is_data_member_spec(spec));
}
//*************************************************************************
TEST(test_define_aggregate)
{
// Define a new aggregate type with two members at compile time.
// define_aggregate must be called from a consteval { } block in GCC 16.
struct NewType;
consteval
{
etl::meta::define_aggregate(^^NewType,
{etl::meta::data_member_spec(^^int, {.name = "a"}), etl::meta::data_member_spec(^^double, {.name = "b"})});
}
// Now NewType is complete; query it in a consteval lambda.
constexpr auto result = []() consteval
{
auto new_type = ^^NewType;
bool is_type_ok = etl::meta::is_type(new_type);
bool is_complete_ok = etl::meta::is_complete_type(new_type);
auto members = etl::meta::nonstatic_data_members_of(new_type, etl::meta::access_context::unchecked());
struct R
{
size_t sz;
meta_string_view name0;
meta_string_view name1;
bool type0_is_int;
bool type1_is_double;
bool is_type;
bool is_complete;
};
return R{members.size(),
etl::meta::identifier_of(members[0]),
etl::meta::identifier_of(members[1]),
etl::meta::type_of(members[0]) == ^^int,
etl::meta::type_of(members[1]) == ^^double,
is_type_ok,
is_complete_ok};
}();
CHECK(result.is_type);
CHECK(result.is_complete);
CHECK_EQUAL(size_t(2), result.sz);
CHECK(meta_string_view("a") == result.name0);
CHECK(meta_string_view("b") == result.name1);
CHECK(result.type0_is_int);
CHECK(result.type1_is_double);
}
#pragma GCC diagnostic pop
//=========================================================================
// Parameter reflection
//=========================================================================
//*************************************************************************
TEST(test_parameters_of)
{
// add(int, int) has 2 parameters.
constexpr auto sz = []() consteval
{
return etl::meta::parameters_of(^^add).size();
}();
CHECK_EQUAL(size_t(2), sz);
}
//*************************************************************************
TEST(test_parameter_names)
{
// Parameters of add are named "a" and "b".
constexpr auto result = []() consteval
{
auto params = etl::meta::parameters_of(^^add);
struct R
{
meta_string_view name0;
meta_string_view name1;
};
return R{etl::meta::identifier_of(params[0]), etl::meta::identifier_of(params[1])};
}();
CHECK(meta_string_view("a") == result.name0);
CHECK(meta_string_view("b") == result.name1);
}
//*************************************************************************
TEST(test_is_function_parameter)
{
constexpr auto result = []() consteval
{
auto params = etl::meta::parameters_of(^^add);
struct R
{
bool p0;
bool p1;
};
return R{etl::meta::is_function_parameter(params[0]), etl::meta::is_function_parameter(params[1])};
}();
CHECK(result.p0);
CHECK(result.p1);
}
//*************************************************************************
TEST(test_return_type_of)
{
// add returns int.
constexpr auto ret = etl::meta::return_type_of(^^add);
CONSTEXPR_CHECK(ret == ^^int);
}
//*************************************************************************
TEST(test_has_default_argument)
{
// with_default(int a, int b = 10): second param has a default.
constexpr auto result = []() consteval
{
auto params = etl::meta::parameters_of(^^with_default);
struct R
{
bool first_has_default;
bool second_has_default;
};
return R{etl::meta::has_default_argument(params[0]), etl::meta::has_default_argument(params[1])};
}();
CHECK(!result.first_has_default);
CHECK(result.second_has_default);
}
//*************************************************************************
//=========================================================================
// Edge-case tests — remaining API gaps
//=========================================================================
//*************************************************************************
TEST(test_is_lvalue_reference_qualified)
{
// RefQualified::lref is &-qualified.
constexpr auto lref_fn = []() consteval
{
auto members = etl::meta::members_of(^^RefQualified, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "lref")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(lref_fn != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_lvalue_reference_qualified(lref_fn));
CONSTEXPR_CHECK_FALSE(etl::meta::is_rvalue_reference_qualified(lref_fn));
}
//*************************************************************************
TEST(test_is_rvalue_reference_qualified)
{
// RefQualified::rref is &&-qualified.
constexpr auto rref_fn = []() consteval
{
auto members = etl::meta::members_of(^^RefQualified, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "rref")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(rref_fn != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_rvalue_reference_qualified(rref_fn));
CONSTEXPR_CHECK_FALSE(etl::meta::is_lvalue_reference_qualified(rref_fn));
}
//*************************************************************************
TEST(test_no_ref_qualification)
{
// RefQualified::none has neither ref-qualification.
constexpr auto none_fn = []() consteval
{
auto members = etl::meta::members_of(^^RefQualified, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "none")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(none_fn != etl::meta::info{});
CONSTEXPR_CHECK_FALSE(etl::meta::is_lvalue_reference_qualified(none_fn));
CONSTEXPR_CHECK_FALSE(etl::meta::is_rvalue_reference_qualified(none_fn));
}
//*************************************************************************
TEST(test_has_thread_storage_duration)
{
CONSTEXPR_CHECK(etl::meta::has_thread_storage_duration(^^tls_var));
CONSTEXPR_CHECK_FALSE(etl::meta::has_static_storage_duration(^^tls_var));
}
//*************************************************************************
TEST(test_has_automatic_storage_duration)
{
// A local variable inside a consteval context has automatic storage.
// We test indirectly: namespace-scope variables do NOT have automatic storage.
CONSTEXPR_CHECK_FALSE(etl::meta::has_automatic_storage_duration(^^global_const));
CONSTEXPR_CHECK_FALSE(etl::meta::has_automatic_storage_duration(^^internal_var));
}
//*************************************************************************
//*************************************************************************
TEST(test_is_structural_type)
{
// Structural (all-public, no virtual, trivially destructible) is structural.
CONSTEXPR_CHECK(etl::meta::is_structural_type(^^Structural));
CONSTEXPR_CHECK(etl::meta::is_structural_type(^^int));
// A class with non-public non-static data members is never structural.
CONSTEXPR_CHECK_FALSE(etl::meta::is_structural_type(^^MixedAccess));
}
//*************************************************************************
TEST(test_object_of)
{
// object_of on a reflected variable returns the object it designates.
constexpr auto obj = etl::meta::object_of(^^global_const);
CONSTEXPR_CHECK(etl::meta::is_object(obj));
}
//*************************************************************************
TEST(test_is_conversion_function)
{
// WithConversion has a conversion operator.
constexpr auto conv = []() consteval
{
auto members = etl::meta::members_of(^^WithConversion, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::is_conversion_function(m))
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(conv != etl::meta::info{});
CONSTEXPR_CHECK(etl::meta::is_conversion_function(conv));
}
//*************************************************************************
TEST(test_is_trivially_constructible_type_multi_arg)
{
// Trivial is trivially constructible from (Trivial const&).
CONSTEXPR_CHECK(etl::meta::is_trivially_constructible_type(^^Trivial, {^^const Trivial& }));
// Trivial is trivially constructible from (Trivial&&).
CONSTEXPR_CHECK(etl::meta::is_trivially_constructible_type(^^Trivial, {^^Trivial&& }));
}
//*************************************************************************
//*************************************************************************
TEST(test_is_explicit_object_parameter)
{
// WithDeducingThis::get has an explicit object parameter (deducing this).
constexpr auto get_fn = []() consteval
{
auto members = etl::meta::members_of(^^WithDeducingThis, etl::meta::access_context::unchecked());
for (auto m : members)
if (etl::meta::has_identifier(m) && etl::meta::identifier_of(m) == "get")
return m;
return etl::meta::info{};
}();
CONSTEXPR_CHECK(get_fn != etl::meta::info{});
constexpr auto result = [&]() consteval
{
auto params = etl::meta::parameters_of(get_fn);
struct R
{
size_t sz;
bool is_explicit;
};
return R{params.size(), params.size() >= 1 && etl::meta::is_explicit_object_parameter(params[0])};
}();
CHECK(result.sz >= size_t(1));
CHECK(result.is_explicit);
}
}
} // namespace
#endif // ETL_HAS_REFLECTION