Search code examples
c++templatesgeneric-programming

parameter deduction in function template using an integer value as the template parameter


I'm trying to implement a generic function that generates a std::string from an id (which is an std::pair<uint32_,uint32_t>).

The function is the following:

typedef uint32_t element_type;

template <element_type type>
std::string to_string (const std::pair<element_type, uint32_t>& id) {
    ....
    const char* name = elemen_type_traits<type>::element_type_name;
    ...
}

I can invoke the function in the following way:

std::cout << to_string<ELEMENT_TYPE_FOO> (foo_0) << std::endl;
std::cout << to_string<ELEMENT_TYPE_FOO> (foo_1) << std::endl;

The only thing is that I want to make sure that the template parameter matches the first field of the std::pair. Is it possible to deduct the parameter value from std::pair.first?

I don't know if it's possible but in the end I would like to have something like this:

std::cout << to_string (foo_0) << std::endl;
std::cout << to_string (foo_1) << std::endl;

Thanks in advance.


Solution

  • If you encode the value inside a type, this is actually achievable:

    // C++11 'enum class' emulation, you don't want to leak 'foo' everywhere
    // also called a "scoped enum"
    struct element_type_value{
       enum type{
         foo = 1337
       };
    };
    
    template<element_type_value::type V>
    struct element_type{};
    
    template<element_type_value::type V>
    std::string to_string(std::pair<element_type<V>, uint32_t> const& /*id*/){
      // ...
      const char* name = element_type_traits<V>::element_type_name;
      // ...
    }
    

    Live example.

    Of course, this only works if the type is always a statically known value, and actually you don't even need the id.first anymore. However, there's no other way of achieving this check, as far as I know.

    I personally would probably drop std::pair and just make a custom struct, together with some other refactoring.

    struct element_type{
       enum type{
         foo = 1337
       };
    };
    
    template<element_type::type V>
    struct element_type_id{
      element_type_id(uint32_t id) : id(id){}
      uint32_t id; // or whatever your original std::pair::second represented
    };
    
    template<element_type::type V>
    std::string to_string(element_type_id<V> const& /*id*/){
      // ...
      const char* name = element_type_traits<V>::element_type_name;
      // ...
    }
    

    Live example.