[英]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::visitor
和std::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);
}
我將從c ++ 14答案開始,然后詳細說明如何降級c ++ 11 。
假設您有一個類型列表:
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...>{} );
}
並做了。
降級到c ++ 11只需編寫make_index_sequence
和index_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.