使用参数调用,这将从单个参数解析
call with parameters, that will be parsed from a single parameter
我的目标是创建一个可调用对象(在本例中为Derived
),它有一个参数列表。它将使用单个参数调用,该参数用于解析参数列表的值。
我目前的尝试在结构上类似于一种绑定机制。它看起来像这样:
#include <string>
#include <utility>
#include <type_traits>
// this is a helper meta function
template<typename FunctionType, int ParameterCount> struct parameter_type;
template<typename Ret, typename FirstParam, typename ... MoreParams>
struct parameter_type<Ret(FirstParam, MoreParams...), 0> {
using type = FirstParam;
};
template<typename Ret, int ParameterCount, typename FirstParam, typename ... MoreParams>
struct parameter_type<Ret(FirstParam, MoreParams...), ParameterCount> {
using type = typename parameter_type<Ret(MoreParams...), ParameterCount - 1>::type;
};
// here comes the base with CRTP to call the Derived operator()()
template<typename Derived, typename ... Params> struct Base;
template<typename Derived> struct Base<Derived> {};
template<typename Derived, typename FirstParam, typename ... Params>
struct Base<Derived, FirstParam, Params...> :
public Base<Base<Derived, FirstParam, Params...>, Params...> {
private:
FirstParam first_param_;
public:
Base(const FirstParam& first_param, Params& ... params):
Base<Base<Derived, FirstParam, Params...>, Params...>{params...},
first_param_{first_param} {};
template<typename PrefixParamT>
int operator()(
typename std::enable_if<std::is_convertible<PrefixParamT,
typename parameter_type<Derived, 0>::type>::value,
typename std::remove_reference<PrefixParamT>::type>::type&& prefix,
Params ... params) {
// actually we parse first_param from prefix here
(*static_cast<Derived*>(this))(std::forward(prefix),
first_param_,
params...);
}
};
// we use that to create various callables like this
struct Derived : public Base<Derived, int, const std::string&, double> {
Derived(int a, const std::string& b, double c) :
Base<Derived, int, const std::string&, double>{a, b, c} {};
int operator()(const std::string& t, int a, const std::string& b, double c) {
// her comes our business logic
}
// this is necessary to make the basic call operator available to
// the user of this class.
int operator()(const std::string&);
};
// they should be usable like this
int main(int argc, char** argv) {
Derived d{1, argv[0], 0.5};
// call the most basic operator()().
// here all the values from argv[1] should be parsed and converted
// to parameters, that we pass to the most derived operator()()
d(argv[1]);
}
当然,这不会编译,因为typename parameter_type<Derived, 0>::type>
不能被确定,Derived
是一个不完整的类型。我明白这一点,但我还没有想出一个替代的实现方法。
当然,我可以在不损失任何功能的情况下省略示例中的可兑换性检查,只是在编译器消息中有一些清晰度。在我的实际代码中,operator()()
有不同的过载,应该根据Derived::operator()()
的签名来选择。因此我需要这样的检查。
有不同的方法吗?我的目标是使Derived
这样的可调用对象尽可能简单。我们以后会有很多不同签名的。这就是为什么我尽量避免在Derived::operator()()
中解析prefix
的原因。
为了方便将来的读者了解这个问题
感谢@Yakk提供的答案,我想出了一个解决方案。这仍然是示例代码,需要在parse_params_chooser<>
模板中进行更详细的类型特征检查,以启用其他可调用对象,而不是自由函数。但我认为,现在路已经铺好了,应该就这样了。
#include <string>
#include <utility>
#include <tuple>
#include <experimental/tuple>
#include <type_traits>
#include <iostream>
// basic machinery
template <typename Derived, typename ResultType, typename ... Params> struct parse_params_t;
template<typename Derived, typename ResultType, typename FirstParam, typename ... Params>
struct parse_params_t<Derived, ResultType, FirstParam, Params...> :
public parse_params_t<parse_params_t<Derived, ResultType, FirstParam, Params...>, ResultType, Params...> {
private:
typename std::remove_reference<FirstParam>::type first_param_;
public:
parse_params_t(const typename std::remove_reference<FirstParam>::type& first_param, Params&& ... params):
parse_params_t<parse_params_t<Derived, ResultType, FirstParam, Params...>, ResultType, Params...>{std::forward<Params>(params)...},
first_param_{first_param} {};
using parse_params_t<parse_params_t<Derived, ResultType, FirstParam, Params...>, ResultType, Params...>::parse;
template<typename PrefixParamT>
auto parse(const PrefixParamT& prefix, const Params& ... params) -> ResultType {
return static_cast<Derived*>(this)->parse(prefix, first_param_, params...);
}
};
template<typename Derived, typename ResultType, typename LastParam>
struct parse_params_t<Derived, ResultType, LastParam> {
private:
LastParam last_param_;
public:
parse_params_t(const LastParam& last_param):
last_param_{last_param} {};
template<typename PrefixParamT>
auto parse(PrefixParamT&& prefix) -> ResultType {
return static_cast<Derived*>(this)->parse(std::forward<PrefixParamT>(prefix), last_param_);
}
};
// put things together in a last derived type
template <typename ResultType, typename ... Params>
struct parse_params_helper : public parse_params_t<parse_params_helper<ResultType, Params...>, ResultType, Params...> {
parse_params_helper(Params&& ... params):
parse_params_t<parse_params_helper<ResultType, Params...>, ResultType, Params...>{std::forward<Params>(params)...} {};
using parse_params_t<parse_params_helper<ResultType, Params...>, ResultType, Params...>::parse;
template<typename PrefixParamT>
auto parse(const PrefixParamT& prefix, const Params& ... params) -> ResultType {
return {params...};
}
};
// choose parser depending on handler parameter types.
template <typename PrefixParamT, typename Handler> struct parse_params_chooser;
template <typename PrefixParamT, typename ... Params>
struct parse_params_chooser<PrefixParamT, int(Params...)> {
static auto parse(int (handler)(Params...), Params&& ... params) {
return [helper = parse_params_helper<std::tuple<Params...>, Params...>{std::forward<Params>(params)...},
handler](PrefixParamT&& prefix) mutable -> int {
return std::experimental::apply(handler, std::tuple_cat(helper.parse(prefix)));
};
}
};
template <typename PrefixParamT, typename ... Params>
struct parse_params_chooser<PrefixParamT, int(PrefixParamT, Params...)> {
static auto parse(int (handler)(PrefixParamT, Params...), Params&& ... params) {
return [helper = parse_params_helper<std::tuple<Params...>, Params...>{std::forward<Params>(params)...},
handler](PrefixParamT&& prefix) mutable -> int {
return std::experimental::apply(handler, std::tuple_cat(std::make_tuple(prefix), helper.parse(prefix)));
};
}
};
// create a nice free function interface to trigger the meta programm
template <typename PrefixParamT, typename Handler, typename ... Params>
auto parse_params(Handler& handler, Params&& ... params) {
return parse_params_chooser<PrefixParamT, Handler>::parse(handler, std::forward<Params>(params)...);
}
// now we can use that to create various callables like this
auto handler(std::string t, int a, std::string b, double c) -> int {
// her comes our business logic
std::cout << "handler: " << t << " " << a << " " << b << " " << c << std::endl;
}
auto other_handler(int a, std::string b, double c) -> int {
// more business logic
std::cout << "other_handler: " << a << " " << b << " " << c << std::endl;
}
// they should be usable like this
auto main(int argc, char** argv) -> int {
auto h = parse_params<std::string>(handler, 1, argv[0], 0.5);
auto g = parse_params<std::string>(other_handler, 2, std::string(argv[0]) + " blabla", 1.5);
// call the lambda, that will parse argv[1] and call the handler
h(argv[1]);
// call the lambda, that will parse argv[2] and call the handler
g(argv[1]);
}
标题>使用std::tuple
和std::apply
。
在初始化列表中使用Ts...
展开,以特定顺序构建实参。也许可以使用对齐存储或可选存储来使它更容易,或者构造并分配它为lazy。
可能向解析代码传递编译时或运行时索引和类型。
所以derived应该是这样的:
struct bob : auto_parse< bob, void(int, std::string, double) >{
int parse( std::string_view& data, size_t arg_index, tag_t<int> ) { return {}; } // actually parse
// optional! If missing default parser used.
// etc
void execute( int x, std::string const& s, double d ) const { /* code */ }
};
我会避免模棱两可的operator()
位
相关文章:
- 如何将可变参数模板转换为多个单个模板?(C++竞争编程调试模板)
- 关于如何在具有单个参数的变体构造中选择替代方案?
- std::bind,无法让具有单个参数的方法工作
- 使用可变参数函数将整数和/或整数数组放入单个 int 数组中
- 多个可变参数函数的单个模板参数包?
- 如何将多个函数重载作为单个参数传递?
- 具有默认值的单个参数构造函数是否与默认构造函数相同?
- C++fmt库,只使用格式说明符格式化单个参数
- 是否可以使用模板类的单个参数将类型和该类型的指针传递给 c++ 模板类?
- 如何使用每个对象的单个构造函数参数动态分配C++对象数组?
- 从单个标志C++获取多个参数
- QuickSort方法仅使用单个向量作为C 中的输入参数
- 模板参数在具有相同数据类型的单个类型名的构造函数中不起作用
- 具有单个参数的布尔函数映射
- 单个功能中的多个参数包
- 如何在C++中使用单个模板参数传递两个 lambda 函数
- 在什么基础上,由单个元素组成的参数包的折叠表达式被转换为未加括号的表达式
- 当foo是接受单个模板参数的结构时,'foo<Type1, Types...>'是否合法?
- C++是否可以创建依赖于单个构造函数参数的派生类而不是bass类
- C 模板字符在单个tick中作为参数