Home > Mobile >  Expansion of multiple parameter packs of types and integer values
Expansion of multiple parameter packs of types and integer values

Time:07-18

I previously asked this question, which basically asked how do I change the following "pseudo code" to get the result the comments show:

struct MyStructure {

  std::array<pair<TYPE, int>> map {
    { int, 1 },
    { char, 2 },
    { double, 4 },
    { std::string, 8 }
  };

  template <typename T>
  auto operator=(const T &arg) {
    // Depending on the type of 'arg', I want to write a certain value to the member variable 'type_'
  }
  int type_ = 0;
};

int main() {

  MyStructure myStruct;
  myStruct = 1;             // Should cause 1 to be stored in member 'type_ '
  myStruct = "Hello world"; // Should cause 8 to be stored in member 'type_'
}

I obtained a suitable answer in the linked question, but now I have a different requirement. I would like to be able to pass the possible integer values and types through as template arguments, and somehow expand them to auto-generate the specialisations as given on the accepted answer. The reason for this is that MyStructure now has to specify the possible values that can be assigned to it, so the 'hard coded' solution approach would not work.

My first thought was to create a new class that MyStructure derives from, that takes the types parameter pack and the ints parameter pack...

template<typename... Types, int...ints> 
class Base_Type  {
// Somehow expand the packs to generate the specialisations
};

template<typename... Types, int...ints>
struct MyStructure : Base_Type<Types..., ints...> {
// 
  template <typename T>
  auto operator=(const T &arg) {
    // Check T is within 'Types'
    // Query Base_Type for the T specialization to get the corresponding integer value to write to 'type_'
  }
  int type_ = 0;

};

Unfortunately I can't see how to do this because - for a start - I can only apparently have one template parameter pack:

'Types': if a class template has a template parameter pack it must appear at the end of the template parameter list

Is what I want to achieve here possible with C 17?

CodePudding user response:

It's not possible to generate specializations, but you don't actually need those.

It's not possible to have more than one template parameter pack per class template, so we'll have to work with a single one, with a helper struct that combines both a type and its index into a single type.

#include <cstddef>
#include <iostream>
#include <type_traits>

template <typename T, int I>
struct Type
{
    using type = T;
    static constexpr int value = I;
};

template <typename ...P>
struct Foo
{
    int index = 0;

    template <typename T, std::enable_if_t<(std::is_same_v<T, typename P::type> || ...), std::nullptr_t> = nullptr>
    Foo &operator=(const T &)
    {
        (void)((std::is_same_v<T, typename P::type> ? (index = P::value, true) : false) || ...);
        return *this;
    }
};

int main()
{
    Foo<Type<int, 10>, Type<float, 20>> x;
    x = 42;
    std::cout << x.index << '\n'; // 10
    x = 42.f;
    std::cout << x.index << '\n'; // 20
    // x = 42L; // Error.
}

(X || ...) is a fold expression. It repeats X multiple times, once per element of P. Every use of P in X is replaced with its i-th element.

We use it as a poor man's loop. When the types match and ? : returns true, the loop stops.

The cast to (void) silences the "unused result" warning.

  • Related