C++模板元编程嵌套switch语句

C++ template metaprogramming nested switch statement

假设我有一个像这样的联合类型(变体类型)(为了演示目的而简化)

enum class Type : uint32_t
{
  Unknown = 0,
  Int64 = 1,
  UInt64 = 2,
  Double = 3
};

struct Value
{
  Type dtype;
  union
  {
    int64_t i;
    uint64_t u;
    double d;
  };
}

我打算像这样将这个值分派给不同的函数(实际上是函数对象,但再次简化......),

template<typename... T> auto eval(const T&... t)
{
   // ...
}

它采用参数包,或更简单的参数包,例如:

template<typename T1, typename T2> auto sum(const T1& a, const T2& b)
{
   return a+b;
}

一般来说,我需要一个具有以下结构的 switch 语句:

  switch (o1.dtype)
  {
    case Type::Int64:
      switch (o2.dtype)
      {
        case Type::Int64:
          F(o1.i, o2.i);
        case Type::Double:
          F(o2.i, o2.d);
        //...
      }
      break;
    case Type::Double
      //...
      

对于调用具有 2 个参数的仿函数的情况。但对于 3 个参数的情况,情况会变得更糟...

有没有办法通过元编程来泛化它?理想情况下,我将只有一个 switch 语句,并生成所有嵌套语句。

您可以制作一个可变参数模板函数,它在第一个参数上执行 switch 并用其余参数调用自身,以及一个保存第一个参数类型的 lambda:

template<typename F, typename... T>
// decltype(auto) is used to deduce the return type from F, throughout
decltype(auto) apply_func(F f, const Value& v, T&&... vs) { // can't say "any number of arguments, all Values", so say "any arguments of any type" and just convert them to Value later
    switch (v.dtype) {
    case Type::Int64: // bind first argument of f to chosen variant of v, then apply new function (with one less argument) to remaining Values
        return apply_func(
            [&](const auto&... args) -> decltype(auto) {
                return f(v.i, args...);
            }, std::forward<T>(vs)...
        );
    case Type::UInt64:
        return apply_func(
            [&](const auto&... args) -> decltype(auto) {
                return f(v.u, args...);
            }, std::forward<T>(vs)...
        );
    case Type::Double:
        return apply_func(
            [&](const auto&... args) -> decltype(auto) {
                return f(v.d, args...);
            }, std::forward<T>(vs)...
        );
    default: throw std::invalid_argument("Unknown dtype");
    }
}

// final overload, when all arguments have been switched on and the chosen variants have been saved into f
template<typename F>
decltype(auto) apply_func(F f) {
    return f();
}

Godbolt demo