简体   繁体   English

内置类型的基于C ++类型的调度

[英]C++ type-based dispatch for builtin types

I need a dispatcher function, something like below 我需要一个调度程序功能,如下所示

template<typename T>
T dispatcher() {
    // if T is double
    return _func_double();
    // if T is int
    return _func_int();
    // if T is char*
    return _func_char_pointer();
}

and will be used like below 并将使用如下

// some code that use above dispatcher
template<typename T1, typename T2, typename T3>
void do_multiple_thing(T1 *a1, T2 *a2, T2 *a3) {
    *a1 = dispatcher<T1>();
    *a2 = dispatcher<T2>();
    *a3 = dispatcher<T3>();
}

Could you tell me how to achieve that? 你能告诉我怎么做到的吗?

PS PS
- solution for builtin types only suffices. - 内置类型的解决方案只需要足够。
- both preprocessing and template appoach is acceptable. - 预处理和模板适用都是可以接受的。

If you have compiler with C++17 support this snippet of code should work: 如果你有支持C ++ 17的编译器,那么这段代码应该可以工作:

template<typename T>
T dispatcher() {
    // if T is double
    if constexpr (std::is_same<T, double>::value)
        return _func_double();
    // if T is int
    if constexpr (std::is_same<T, int>::value)
        return _func_int();
    // if T is char*
    if constexpr (std::is_same<T, char*>::value)
        return _func_char_pointer();
}

Otherwise you will have to do template specialization, and make overload for each of parameters that you want 否则,您将不得不进行模板特化,并为您想要的每个参数进行重载

//only needed for static assert
template<typename T>
struct always_false : std::false_type {};


template<typename T>
T dispatcher() 
{ 
//to make sure that on type you didn't overload you will have exception
    throw std::exception("This type was not overloaded")
//static assert that will provide compile time error
    static_assert(always_false<T>::value , "You must specialize dispatcher for your type");
}
//or to get compile time error without static assert 
template<typename T>
T dispatcher() = delete; //the simplest solution

template<>
double dispatcher<double>() 
{
    return _func_double();
}
//... and so on for all other functions

In C++17, you might combine if constexpr and std::is_same : 在C ++ 17中,如果constexpr和std::is_same可以合并:

template<typename T>
T dispatcher() {
    if constexpr (std::is_same<T, double>::value) {
        return _func_double();
    } else if constexpr (std::is_same<T, int>::value) {
        return _func_int();
    } else if constexpr (std::is_same<T, char*>::value) {
        return _func_char_pointer();
    } else {
        return {}; // or static_assert(always_false<T>::value); ?
    }
}

Before, you might use specialization or tag dispatching with overload: 之前,您可以使用带有重载的特化或标记调度:

template<typename T>
T dispatcher() {
    return {}; // or static_assert(always_false<T>::value); ?
}

template<>
double dispatcher() {
    return _func_double();
}

template<>
int dispatcher() {
    return _func_int();
}

template<>
char* dispatcher() {
    return _func_char_pointer();
}

or 要么

template<typename T> struct tag {};


template<typename T>
T dispatcher(tag<T>) = delete; // or { return {}; } ?

double dispatcher(tag<double>) { return _func_double(); }

int dispatcher(tag<int>) { return _func_int(); }

char* dispatcher(tag<char*>) { return _func_char_pointer(); }

// some code that use above dispatcher
template<typename T1, typename T2, typename T3>
void do_multiple_thing(T1 *a1, T2 *a2, T2 *a3) {
    *a1 = dispatcher(tag<T1>());
    *a2 = dispatcher(tag<T2>());
    *a3 = dispatcher(tag<T3>());
}
template <typename T>
T fetch_magic_value();

template <>
int fetch_magic_value<int>() { return 23; }

template <>
char fetch_magic_value<char>() { return ' '; }

template <>
std::string fetch_magic_value<std::string>() { return "tada"; }

template<typename T>
void do_multiple_thing(T *x) {
  *x = fetch_magic_value<T>();
}

template<typename T, typename... Args>
void do_multiple_thing(T *first, Args *...args)
{
    do_multiple_thing(first);
    do_multiple_thing(args...);
}

https://wandbox.org/permlink/v2NMhoy8v3q5VhRf https://wandbox.org/permlink/v2NMhoy8v3q5VhRf

C++17 version: https://wandbox.org/permlink/0pi08jvYF5vlIpud C ++ 17版本: https//wandbox.org/permlink/0pi08jvYF5vlIpud

If you need a generic solution for writing such dispatchers, something along the lines of this can be used: 如果您需要一个通用的解决方案来编写这样的调度程序,可以使用以下内容:

// calls the first function that has return type `T` with no arguments
template <class T, class F, class... Fs>
constexpr T call_by_return_type(F&& f, Fs&&... fs) {
    if constexpr (std::is_same_v<T, std::invoke_result_t<F>>) {
        return f();
    } else if constexpr (sizeof...(fs) > 0) {
        return call_by_return_type<T>(std::forward<Fs>(fs)...);
    } else {
        static_assert(
            sizeof(T) == 0,
            "`T` must match at least one function's return type"
        );
    }
}

And then you can create dispatchers as a combination of functions (can be any function-object that is called with no arguments): 然后,您可以将调度程序创建为函数的组合(可以是任何不带参数调用的函数对象):

template <class T>
constexpr T dispatcher() {
    return call_by_return_type<T>(
        _func_double,
        _func_int,
        _func_char_pointer
    );
}

Example in godbolt.org godbolt.org中的示例


Note: I assumed you already have _func_<return-type> functions that need to be grouped to form a dispatcher, otherwise I could think of more elegant interfaces. 注意:我假设您已经有_func_<return-type>函数需要分组以形成调度程序,否则我会想到更优雅的接口。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM