While @Jarod42 's answer is the right one for the given problem, I found a new way to approach the problem using C++26's expansion statements:
The unnamed structs should be tied in a tuple. We'll use a static_assert to check that all constants in the enum is associated with a type. Then we can use C++26's expansion statements, i.e. template for to loop through the tuple of unnamed structs:
static constexpr auto objects = std::tie(foo, bar, baz);
// Validate that all object types have corresponding parameter structs
consteval bool validate_all_objects_defined()
{
for(uint8_t type_val = 0; type_val < MyType::TYPE_COUNT; ++type_val)
{
bool found = false;
template for(constexpr auto& obj : objects)
{
if constexpr(myrequirement<decltype(obj)>)
{
if(obj.type() == type_val)
{
found = true;
}
}
}
if(!found) return false;
}
return true;
}
static_assert(validate_all_objects_defined(),
"Not all object types have corresponding parameter definitions!");
template<MyType servo_type>
constexpr auto& get_instance()
{
template for(constexpr auto& obj : objects)
{
if constexpr(myrequirement<decltype(obj)> && obj.type() == servo_type)
{
return obj;
}
}
// This should never be reached due to the static_assert above
std::unreachable();
}
This way, the users only have to add a constant to the enum, define a structure and add it to the tuple, and adding a constant without defining a new type gives an explicit compile error:
<source>:63:43: error: static assertion failed: Not all object types have corresponding parameter definitions!
63 | static_assert(validate_all_objects_defined(),