c++templatesfoldpackunnamed-class

Use parameter pack and template function to differenciate unnamed structs


I have written a C++20's concept myrequirement:

template<typename T>
concept myrequirement= requires(T s)
{
    { s.type() } -> std::same_as<uint8_t>;
};

enum MyType
{
    TYPE1,
    TYPE2,
    TYPE3,
};
 

So my users can write unamed structures that will satisfy this requirement like so:

struct
{
    static constexpr uint8_t type() { return TYPE1;}
} foo;

struct
{
    static constexpr uint8_t type() { return TYPE2;}
} bar;

struct
{
    static constexpr uint8_t type() { return TYPE3;}
} baz;

I want the users to be able to retrieve the right unamed structure depending on their type field like so :

auto my_struct = get_instance<TYPE1>();

I thought I could use c++'s pack parameters to be able to write something like this:

template<uint8_t type, myrequirement... StructTypes> 
constexpr auto& get_instance_amongst(const StructTypes&... structs)
{
    // how do we do this ?
}

template<uint8_t type>
constexpr auto& get_instance()
{
    return get_instance_amongst(foo, bar, baz);
}

I've tried quite a lot of code but nothing seems to compile... Is there anything close to this possible ? With fold expression maybe ?


Solution

  • For simple cases like that, you might hardcode the reversal mapping:

    template <uint8_t type>
    auto& get_instance()
    {
        if constexpr (type == TYPE1) { return foo; }
        else if constexpr (type == TYPE2) { return bar; }
        else if constexpr (type == TYPE3) { return baz; }
        else { static_assert(false, "Wrong number"); }
    }
    

    If you want to retrieve from the instances instead, you might do:

    template<uint8_t type, myrequirement... StructTypes> 
    auto& get_instance_amongst(const StructTypes&... structs)
    {
        constexpr bool matchTypes[] = {(StructTypes::type() == type)...};
        constexpr auto index = std::distance(std::begin(matchTypes), std::ranges::find(matchTypes, true));
    
        return std::get<index>(std::tie(structs...));
    }
    
    template<uint8_t type>
    auto& get_instance()
    {
        return get_instance_amongst<type>(foo, bar, baz);
    }
    

    Demo