c++ 如何在使用运行时类型时删除样板代码?

u2nhd7ah  于 2023-04-01  发布在  其他
关注(0)|答案(1)|浏览(158)

我有一些(大大简化了的)代码如下

inline Type f(const int*) { return Type::Int; }
inline Type f(const double*) { return Type::Double; }
inline void g(int) { }
inline void g(double) { }

但是,该类型是在运行时管理的:

enum class Type { Int, Double } ;

我希望有一个实用程序例程将运行时Type“转换”为模板,以避免大量的代码重复。

void do_call_f(Type t)
{
    if (t == Type::Int)
    {
        const auto result = f(static_cast<const int*>(nullptr));
    }
    if (t == Type::Double)
    {
        const auto result = f(static_cast<const double*>(nullptr));
    }
}

void do_call_g(Type t)
{
    if (t == Type::Int)
    {
        g(123);
    }
    if (t == Type::Double)
    {
        g(1.23);
    }
}

int main()
{
    do_call_f(Type::Int);
    do_call_f(Type::Double);

    do_call_g(Type::Int);
    do_call_g(Type::Double);
}

但是在do_call_f()do_call_g()中有很多样板代码。我如何使用实用程序例程删除这些代码?没有多少模板“魔术”似乎起作用:-(
我喜欢类似(伪代码)的东西

template<typename TFunc, typename TArg>
auto invoke(Type t, TFunc func, TArg&& arg)
{
   if (t == Type::Int)
      return func<char>(std::forward<TArg>(arg));
   if (type == Type::Double)
      return func<double>(std::forward<TArg>(arg));
   // ...
}

对于我的实际代码,这应该是C14;但对于C23来说,“我很好奇”的答案是可以接受的。

xtfmy6hx

xtfmy6hx1#

C没有提供任何直接将函数模板或重载集传递给另一个函数的方法。(重载集的名称可以用作参数,但最终总是只传递一个特定的函数。)
但正如@NathanOliver在评论中所暗示的那样,在C
14或更高版本中,您可以使用泛型lambda封装重载集:[](const auto *ptr) { f(ptr); }是一个类类型的对象,可以传递给函数模板。

// Some boilerplate:
#include <utility>
#include <stdexcept>

enum class Type { Int, Double };
template <Type T> struct TypeTag;
template <> struct TypeTag<Type::Int> {
    using type = int;
    static constexpr Type value = Type::Int;
};
template <> struct TypeTag<Type::Double> {
    using type = double;
    static constexpr Type value = Type::Double;
};
#define TAG_TYPE(tag) typename decltype(tag)::type

template <class F, typename ...Args>
decltype(auto) call_with_tag(Type t, F&& func, Args&& ...args) {
    switch (t) {
    case Type::Int:
        return std::forward<F>(func)(TypeTag<Type::Int>{}, std::forward<Args>(args)...);
    case Type::Double:
        return std::forward<F>(func)(TypeTag<Type::Double>{}, std::forward<Args>(args)...);
    }
    throw std::invalid_argument("Bad Type enum");
}

// end boilerplate

Type f(const int*);
Type f(const double*);
void g(int);
void g(double);
template <class T> T get_g_arg();

Type do_call_f(Type t) {
    auto f_tag = [](auto tag) {
        return f(static_cast<const TAG_TYPE(tag)*>(nullptr));
    };
    return call_with_tag(t, f_tag);
}

void do_call_g(Type t) {
    auto g_tag = [](auto tag) {
        g(get_g_arg<TAG_TYPE(tag)>());
    };
    call_with_tag(t, g_tag);
}

相关问题