簡體   English   中英

C 11 std ::條件在運行時?

[英]C++11 std::conditional at runtime?

我正在尋找做這樣的事情:

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標識符完成操作?

所有類型都必須在編譯時解析。 因此,沒有類型可以依賴於函數的運行時參數。 處理此類問題的方法基本上是一次構建訪問機制,然后可以重用它。 基本上是這樣的:

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&) { ... }
};

然后,您可以利用訪問者來編寫函數:

void func(void* data, const int dtype) {
    visit_data(data, dtype, func_impl{});
}

類型列表上的切換大小寫在整個代碼庫中只會出現一次。 如果添加新類型,則任何不處理它的訪問者(如果使用)將給出編譯時錯誤。

您也可以使用lambdas與一兩個輔助函數一起內聯; 在具有通用lambda的14中特別有用。

如果可以使用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);
}

我將從答案開始,然后詳細說明如何降級

假設您有一個類型列表:

template<class...>
struct types{using type=types;};

const types<int, double, char> supported_types;

接下來,我們編寫一些實用程序函數

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)>char

namespace 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_sequenceindex_sequence 這是一種高質量的產品 ,但是那里更容易一些。

有沒有辦法像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));}}

我對這個問題的解決方案是一個通用的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()
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM