C ++ 11 std ::在运行时有条件?

时间:2017-09-08 18:22:31

标签: c++ c++11 conditional std

我希望做这样的事情:

void func(void *data, const int dtype)
{
    typedef typename std::conditional<dtype==0,float,double>::type DataType;

    funcT((DataType *)data);

    return;
}

这不会编译,因为dtype需要在编译时知道。我试图避免使用switch语句,因为我正在使用8种数据类型,有许多函数,例如上面的函数,通过ctypes从Python调用。

有没有像std :: conditional这样的方法可以在运行时完成,利用传入的dtype标识符?

5 个答案:

答案 0 :(得分:5)

必须在编译时解析所有类型。因此,没有类型可以依赖于函数的运行时参数。处理这样的事情的方法基本上是建立一个访问机制,然后你可以重用它。基本上,这样的事情:

array_reverse

现在,您可以通过撰写访问者来实现功能:

template <class F>
void visit_data(void* data, const int dtype, F f) {
    switch (dtype)
    case 0: f(*static_cast<float*>(data));
    case 1: f(*static_cast<double*>(data));
}

您的访问者也可以使用通用代码:

struct func_impl {
    void operator()(float&) { ... }
    void operator()(double&) { ... }
};

然后你可以通过利用访客来编写你的功能:

struct func_impl2 {
    template <class T>
    void operator()(T&) { ... }
};

类型列表中的切换案例只会在整个代码库中出现一次。如果添加新类型,任何不处理它的访问者在使用时都会产生编译时错误。

你也可以使用lambdas与一个或两个辅助函数内联;特别适用于你有普通lambda的地方。

答案 1 :(得分:4)

如果您可以使用C ++ 17,可以使用std::visitorstd::variant来解决,例如:

using var_t = std::variant<float, double>;
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

void func(var_t arg) {
    std::visit(overloaded {
            [](float  arg) { foo_float(arg); },
            [](double arg) { foo_double(arg); },
    }, arg);
}

答案 2 :(得分:3)

我将以回答开头,然后详细说明如何降级

假设您有一个类型列表:

template<std::size_t I, class...Ts>
using get_type = std::decay_t<decltype(std::get<I>(std::declval<std::tuple<Ts...>&>()))>;
template<std::size_t I, class Types>
struct type_at_helper;
template<std::size_t I, class...Ts>
struct type_at_helper<I, types<Ts...>>{
  using type=get_type<I,Ts...>;
};
template<std::size_t I, class Types>
using type_at = typename type_at_helper<I,Types>::type;

接下来我们编写一些实用函数

type_at<2, decltype(supperted_types)>

现在,charnamespace helper { template<class F> using invoker = void(*)(F&&, void*); template<class F, class Types, std::size_t I> invoker<F> get_invoker() { return [](F&& f, void* pdata) { std::forward<F>(f)( static_cast<type_at<I, Types>*>(pdata) ); }; } template<class F, class Types, std::size_t...Is> void dispatch( F&& f, void* data, unsigned type_index, std::index_sequence<Is...>, Types ={} ) { using pF=std::decay_t<F>*; using invoker = void(*)(pF, void*); static const invoker table[]={ get_invoker<F, Types, Is>()... }; table[type_index]( std::forward<F>(f), data ); } } template<class F, class...Ts> void dispatch( F&& f, void* data, unsigned type_index, types<Ts...> {} ) { details::dispatch( std::forward<F>(f), data, type_index, std::make_index_sequence<sizeof...(Ts)>{}, types<Ts...>{} ); }

make_index_sequence

并完成。

降级为只需撰写index_sequenceaction="<?php echo $_SERVER['PHP_SELF']; ?>"即可。 Here is a high quality one,但有更容易的。

答案 3 :(得分:2)

  

有没有像std::conditional这样的方法可以在运行时完成,使用传入的dtype标识符?

不,没有。运行时值不能用于在编译类型中进行基于类型的决策。

鉴于您的帖子,最简单的解决方案是使用if语句。

void func(void *data, const int dtype)
{
   if ( dtype == 0 )
   {
      funcT(static_cast<float*>(data));
   }
   else
   {
      funcT(static_cast<double*>(data));
   }
}

为了能够处理大量此类功能,我建议使用std::map<int, std::function<void(void*)>>

这是一个为我编译和构建的简单程序。

#include <map>
#include <functional>

void funcT(float* data)
{
}

void funcT(double* data)
{
}

struct MyType {};

void funcT(MyType* data)
{
}

void func(void *data, const int dtype)
{
   std::map<int, std::function<void(void*)>> functions =
   {
      {0, [](void* in) {funcT(static_cast<float*>(in));}},
      {1, [](void* in) {funcT(static_cast<double*>(in));}},

      // ...

      {7, [](void* in) {funcT(static_cast<MyType*>(in));}}
   };

   if ( functions[dtype] != nullptr )
   {
      functions[dtype](data);
   }
}

int main(){}

使用lambda函数的一个优点是,您可以自由地为各种类型调用不同命名的函数。例如,您可以选择使用:

void foo(MyType* data) {}

{7, [](void* in) {foo(static_cast<MyType*>(in));}}

答案 4 :(得分:0)

我对该问题的解决方案是一个通用selectFunc()函数,该函数将根据FS从提供的函数集dtype中选择一个函数,并将其返回:

using FuncType = void(*)(void*);

template<typename FS>
FuncType selectFunc(int dtype);

函数集将是一个具有静态handle()方法的类,它接受不同类型和静态fallback()方法,如果dtype无效,则会调用该方法。

使用示例:

struct FuncSet
{
    static void fallback() {};
    static void handle(float*) {};
    static void handle(double*) {};
};

void func(void *data, int dtype)
{
    // select a function from FuncSet based on dtype:
    auto f = selectFunc<FuncSet>(dtype);
    // invoke the selected function with the provided data:
    f(data);
    // note, two lines above could be combined into one line
}

<强>实施

// Static method which would call the correct FS::handle() method
template<typename FS, typename T>
struct Helper
{
    static void dispatch(void *data) {  FS::handle(static_cast<T*>(data)); }
};

// Static method which would call FS::fallback()
template<typename FS>
struct Helper<FS, void>
{
    static void dispatch(void*) { FS::fallback(); }
};

template<typename FS>
FuncType selectFunc(int dtype)
{
    switch ( dtype ) {
    case 0:  return &Helper<FS, float>::dispatch;
    case 1:  return &Helper<FS, double>::dispatch;
    // ... add other types here ...
    default: return &Helper<FS, void>::dispatch; // call fallback()
    }
}
相关问题