将参数转换为包装函数期望的类型
Convert arguments to types expected by wrapped function
假设我想要一个通用函数 F
接受类型 Args
的参数并返回 R
。我想包装这个函数,使其符合以下形式:
using gen_type = void (*)(struct value *values,
size_t num_args,
struct value *return_value)
我也有与通用函数相互转换的函数 struct value
:
template<T> T from_value(struct value);
// or have something like from_value_(struct value, int&)
// called from from_value
template<> from_value<int>(struct value);
struct value to_value(int i);
现在,我可以有这样的功能吗:
template<typename F, typename ... Args>
gen_type wrap(F func) {
return [](struct value *values,
size_t num_args,
struct value *return_value) {
if (num_args != sizeof...(Args)) { /* error */ }
auto res = func(/* apply from_value<> to each argument type */)
*return_value = to_value(res);
}
}
问题是评论的地方应该怎么办。
(附带问题:有什么方法可以避免在调用 wrap
时必须描述 F 的参数类型?)
不确定是否是您想要的,但 std::any
(C++17) 似乎符合您的要求:
template <typename Sig> struct callable_trait;
// Miss all combinations of cv qualifier, ref qualifier, C-ellipsis
template <typename R, typename C, typename ... Args>
struct callable_trait<R (C::*)(Args...) const>
{
using Ret = R;
using ArgsTuple = std::tuple<Args...>;
};
// assume no overloads, and const non template operator from above minimal specialization
template <typename C> struct callable_trait : callable_trait<decltype(&C::operator())> {};
template <typename F>
auto wrap(F func) {
return [=](const std::any values[], std::size_t num_args, /* std::span<std::any> */
std::any* return_value) {
using ArgsTuple = typename callable_trait<F>::ArgsTuple;
constexpr std::size_t size = std::tuple_size_v<ArgsTuple>;
if (num_args != size) { throw std::runtime_error("wrong number of arguments");/* error */ }
// C++20 construct actually, you might create free function instead prior that
*return_value = [=]<std::size_t...Is>(std::index_sequence<Is...>){
return func(std::any_cast<std::tuple_element_t<Is, ArgsTuple>>(values[Is])...);
}(std::make_index_sequence<size>()); // immediate call
};
}
下面是如何使用 Boost.Hana 和 func
作为模板参数来完成的示例。
#include <iostream>
#include <math.h>
#include <boost/hana.hpp>
namespace hana=boost::hana;
// sample value type
struct value
{
size_t content;
};
// sample conversion functions for sample value type
template <typename T>
T from_value(const value& val)
{
return T(val.content);
}
template <>
int from_value<int>(const value& val)
{
return val.content*2;
}
template <>
float from_value<float>(const value& val)
{
return val.content*3.0;
}
template <typename T>
value to_value(const T& val)
{
return value{static_cast<size_t>(round(val))};
}
// concatenate results of from_value to tuple
template <typename ...>
struct concat_values
{
};
template <typename T>
struct concat_values<T>
{
template <typename ArrT>
static auto apply(size_t index,const ArrT& arr)
{
return hana::make_tuple(from_value<T>(arr[index]));
}
};
template <typename T, typename ... Types>
struct concat_values<T,Types...>
{
template <typename ArrT>
static auto apply(size_t index,const ArrT& arr)
{
return hana::prepend(concat_values<Types...>::apply(index+1,arr),
from_value<T>(arr[index])
);
}
};
// wrap lambda
template <typename FuncT, FuncT func, typename ... Args>
auto wrap()
{
return [](value *values,
size_t num_args,
value *return_value)
{
if (num_args != sizeof...(Args)) { throw std::runtime_error("Invalid number of arguments!"); }
auto res=hana::unpack(
concat_values<Args...>::apply(0,values),
*func
);
*return_value = to_value(res);
};
}
// try it
// sample func
double sample_sum(size_t a, int b, float c)
{
return a+b*2+c*3;
}
// sample function with C-style signature that accepts wrapped function
void sample_invoke(void (*f)(value*,size_t,value*))
{
value inputs[3]={{1},{2},{3}};
value result{0};
(*f)(inputs,3,&result);
std::cout<<"Result "<<result.content<<std::endl;
}
// run
int main()
{
auto wrapped=wrap<decltype(&sample_sum),&sample_sum,size_t,int,float>();
sample_invoke(wrapped);
return 0;
}
打印:
Result 36
见Demo。
更新
std::index_sequence
的另一个实现:
// apply function
template <typename ... Args, typename FuncT, std::size_t... Idx>
auto apply_func(FuncT func,value* values,std::index_sequence<Idx...>)
{
return func(from_value<Args>(values[Idx])...);
}
// wrap lambda
template <typename FuncT, FuncT func, typename ... Args>
auto wrap()
{
return [](value *values,
size_t num_args,
value *return_value)
{
if (num_args != sizeof...(Args)) { throw std::runtime_error("Invalid number of arguments!"); }
auto res=apply_func<Args...>(*func,values,std::index_sequence_for<Args...>());
*return_value = to_value(res);
};
}
直播Demo.
假设我想要一个通用函数 F
接受类型 Args
的参数并返回 R
。我想包装这个函数,使其符合以下形式:
using gen_type = void (*)(struct value *values,
size_t num_args,
struct value *return_value)
我也有与通用函数相互转换的函数 struct value
:
template<T> T from_value(struct value);
// or have something like from_value_(struct value, int&)
// called from from_value
template<> from_value<int>(struct value);
struct value to_value(int i);
现在,我可以有这样的功能吗:
template<typename F, typename ... Args>
gen_type wrap(F func) {
return [](struct value *values,
size_t num_args,
struct value *return_value) {
if (num_args != sizeof...(Args)) { /* error */ }
auto res = func(/* apply from_value<> to each argument type */)
*return_value = to_value(res);
}
}
问题是评论的地方应该怎么办。
(附带问题:有什么方法可以避免在调用 wrap
时必须描述 F 的参数类型?)
不确定是否是您想要的,但 std::any
(C++17) 似乎符合您的要求:
template <typename Sig> struct callable_trait;
// Miss all combinations of cv qualifier, ref qualifier, C-ellipsis
template <typename R, typename C, typename ... Args>
struct callable_trait<R (C::*)(Args...) const>
{
using Ret = R;
using ArgsTuple = std::tuple<Args...>;
};
// assume no overloads, and const non template operator from above minimal specialization
template <typename C> struct callable_trait : callable_trait<decltype(&C::operator())> {};
template <typename F>
auto wrap(F func) {
return [=](const std::any values[], std::size_t num_args, /* std::span<std::any> */
std::any* return_value) {
using ArgsTuple = typename callable_trait<F>::ArgsTuple;
constexpr std::size_t size = std::tuple_size_v<ArgsTuple>;
if (num_args != size) { throw std::runtime_error("wrong number of arguments");/* error */ }
// C++20 construct actually, you might create free function instead prior that
*return_value = [=]<std::size_t...Is>(std::index_sequence<Is...>){
return func(std::any_cast<std::tuple_element_t<Is, ArgsTuple>>(values[Is])...);
}(std::make_index_sequence<size>()); // immediate call
};
}
下面是如何使用 Boost.Hana 和 func
作为模板参数来完成的示例。
#include <iostream>
#include <math.h>
#include <boost/hana.hpp>
namespace hana=boost::hana;
// sample value type
struct value
{
size_t content;
};
// sample conversion functions for sample value type
template <typename T>
T from_value(const value& val)
{
return T(val.content);
}
template <>
int from_value<int>(const value& val)
{
return val.content*2;
}
template <>
float from_value<float>(const value& val)
{
return val.content*3.0;
}
template <typename T>
value to_value(const T& val)
{
return value{static_cast<size_t>(round(val))};
}
// concatenate results of from_value to tuple
template <typename ...>
struct concat_values
{
};
template <typename T>
struct concat_values<T>
{
template <typename ArrT>
static auto apply(size_t index,const ArrT& arr)
{
return hana::make_tuple(from_value<T>(arr[index]));
}
};
template <typename T, typename ... Types>
struct concat_values<T,Types...>
{
template <typename ArrT>
static auto apply(size_t index,const ArrT& arr)
{
return hana::prepend(concat_values<Types...>::apply(index+1,arr),
from_value<T>(arr[index])
);
}
};
// wrap lambda
template <typename FuncT, FuncT func, typename ... Args>
auto wrap()
{
return [](value *values,
size_t num_args,
value *return_value)
{
if (num_args != sizeof...(Args)) { throw std::runtime_error("Invalid number of arguments!"); }
auto res=hana::unpack(
concat_values<Args...>::apply(0,values),
*func
);
*return_value = to_value(res);
};
}
// try it
// sample func
double sample_sum(size_t a, int b, float c)
{
return a+b*2+c*3;
}
// sample function with C-style signature that accepts wrapped function
void sample_invoke(void (*f)(value*,size_t,value*))
{
value inputs[3]={{1},{2},{3}};
value result{0};
(*f)(inputs,3,&result);
std::cout<<"Result "<<result.content<<std::endl;
}
// run
int main()
{
auto wrapped=wrap<decltype(&sample_sum),&sample_sum,size_t,int,float>();
sample_invoke(wrapped);
return 0;
}
打印:
Result 36
见Demo。
更新
std::index_sequence
的另一个实现:
// apply function
template <typename ... Args, typename FuncT, std::size_t... Idx>
auto apply_func(FuncT func,value* values,std::index_sequence<Idx...>)
{
return func(from_value<Args>(values[Idx])...);
}
// wrap lambda
template <typename FuncT, FuncT func, typename ... Args>
auto wrap()
{
return [](value *values,
size_t num_args,
value *return_value)
{
if (num_args != sizeof...(Args)) { throw std::runtime_error("Invalid number of arguments!"); }
auto res=apply_func<Args...>(*func,values,std::index_sequence_for<Args...>());
*return_value = to_value(res);
};
}
直播Demo.