我正在使用第三方库,该库使用自定义向量和变量类型传递值。但是,这些向量将1:1映射到我的函数。我的最终目标是减少样板数量。
一个人为的例子:
#include <functional>
// A third party type I can't change
struct dumb_varient_type
{
int to_int() { return 3; }
double to_double() { return 3.14; }
std::string to_string() { return "Pi"; }
};
template< class R, class... Args >
R do_callback(std::function<R(Args...)> func, std::vector<dumb_varient_type> &args )
{
// call func, with the correct types
return func( vector_of_pain_to_expanded_parameter_pack_or_something(args) );
}
int main(int argc, char **argv) {
std::vector<dumb_varient_type> arg1 = get_args_from_magic();
auto c1 = [](int, double) {/*do work*/};
auto c2 = [](double, std::string) {/*do work*/};
auto val1 = do_callback( c1, arg1 );
auto val2 = do_callback( c2, arg1 );
// std::vector< dumb_varient_type > arg2( /* Anything*/ );
// std::function<int(/*Anything*/)> c3;
// auto val3 = do_callback( c3, arg2 );
}
我不必担心模棱两可;参数列表不需要路由或分派到正确的函数。我知道预期的回调。但是,由于参数列表是在运行时创建的,因此理论上arg计数或类型可能不匹配。
一个人将如何实现do_callback
(或同等学历)
答案 0 :(得分:3)
// A third party type I can't change
struct dumb_varient_type {
int to_int() { return 3; }
double to_double() { return 3.14; }
std::string to_string() { return "Pi"; }
};
写一个标签助手:
template<class T>struct tag_t{using type=T;};
template<class T>constexpr tag_t<T> tag{};
然后在tag_t
或dumb_variant_type
的命名空间中编写以下重载:(注意:不是模板,只是重载)
int to_type( tag_t<int>, dumb_variant_type const& dumb ) { return dumb.to_int(); }
double to_type( tag_t<double>, dumb_variant_type const& dumb ) { return dumb.to_double(); }
等这样可以在通用代码中更统一地转换您的哑变量。
现在我们编写索引器:
template<std::size_t...Is>
auto indexer(std::index_sequence<Is...>){
return [](auto&&f){ return f(std::integral_constant<std::size_t,Is>{}... ); };
}
template<std::size_t N>
auto indexer_upto(std::integral_constant<std::size_t,N> ={}){
return indexer(std::make_index_sequence<N>{});
}
此帮助程序无需拆分功能。它使我们可以随意访问未打包的索引包。
对索引器的调用将返回一个lambda,而该lambda则会接受客户端提供的lambda。使用未打包的编译时参数包调用客户端lambda。
do_callback
简短而有趣:
template< class R, class... Args >
R do_callback(std::function<R(Args...)> func, std::vector<dumb_varient_type> &args )
{
if(sizeof...(Args) > args.size()) throw std::invalid_argument(); // or whatever
return indexer_upto< sizeof...(Args) >()( []( auto...Is ){
return func( to_type(tag<Args>, args[Is]) ... );
});
}
完成。请原谅任何tpyos。
答案 1 :(得分:2)
您需要某种方法将dumb_varient_type
隐式转换为该函数的正确类型。然后,您需要一种将向量参数解压缩到函数调用本身中的方法。后者非常危险,因为您在编译时不知道向量的大小,因此不能保证始终有足够的元素来匹配函数中参数的数量。
第一步的简单,简单的方法是使用正确的转换运算符创建包装类,然后将dumb_varient_type
的向量转换为smarter_dumb_variant_type
。
struct smarter_dumb_variant_type {
smarter_dumb_variant_type(dumb_varient_type& dvt)
: value(dvt)
{ }
operator ()(int) {
return value.to_int();
}
//... rest of the operators
dumb_varient_type& value;
};
std::vector<smarter_dumb_variant_type> Convert(std::vector<dumb_varient_type>&) {
//Implerment...
}
对于第二部分,您需要在编译时从所需的向量中选择然后元素数量。这就是为什么这样做很危险的原因,向量必须必须具有相等或更多的元素,然后再指定否则会导致不确定的行为。
可以在问题here中找到解压缩向量的解决方案(但是请使用smarter_dumb_variant_type
而不是int)
使用此方法,您的do_callback类似于:
template< class R, class... Args, size_t num_args>
R do_callback(std::function<R(Args...)> func, std::vector<dumb_varient_type> &args )
{
unpack_caller<num_args> caller;
return caller(func, Convert(args));
}
如果在大小/计数不匹配的情况下允许抛出异常,则可以考虑将其添加到函数的开头,以防止未定义的行为。
if (args.size() < num_args) {
throw;
}