내 목표는 매개 변수 목록이있는 호출 가능 번호 (예제에서는 Derived
)를 만드는 것입니다. 매개 변수 목록의 값을 구문 분석하는 데 사용되는 단일 매개 변수로 호출됩니다.단일 매개 변수로 구문 분석 될 매개 변수가있는 호출
현재의 시도는 구조적으로 일종의 바인딩 메커니즘과 유사합니다. typename parameter_type<Derived, 0>::type>
이 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]);
}
,이 컴파일되지 않습니다 : 그것은 다음과 같습니다. 나는 그것을 이해하지만, 나는 아직 대안 구현을 생각해 내지 못했다.
물론이 기능을 잃지 않고 예제에서 전환 가능성에 대한 체크 표시를 생략 할 수 있지만 컴파일러 메시지의 명확성은 약간 있습니다. 내 실제 코드에는 Derived::operator()()
의 서명을 기반으로 선택해야하는 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'를 사용하여 의미했던 것을 확신하지 못했지만, 당신은 새로운 트랙에 나를 놓았습니다, 그것은 지금 유망 해 보입니다. 문제가 해결되면 검색 대상자 모두에게 해답을 게시합니다. BTW : 구성 요소는 고전적인 객체에만 국한되어서는 안되기 때문에'operator()'를 사용하기로했습니다. 나는 또한 람다 (lambdas) 나 자유 함수를 컴포넌트로 사용할 수 있기를 원한다. 그것이 효과가 있다면, 새로운 아이디어는 심지어 파라미터 파싱을 다음과 같이 활성화 할 수도 있습니다 :-) – cdonat
@cdonat는 a가 아니어야합니다. 실행시 호출을'()'에 래핑하십시오. ['std :: apply'] (http://en.cppreference.com/w/cpp/utility/apply)는 호출 가능한 호출과 튜플을 취하고 튜플을 압축하여 호출합니다. args를 여러 멤버 변수 대신 튜플로 구문 분석하십시오. – Yakk
아, 알겠습니다. 분명히 내 예가 너무 단순했다.실제 코드에서 멤버 변수는 실제 매개 변수 값이 아니라 파서에 대한 지침을 저장합니다. 매개 변수 값은 접두사를 구문 분석하여 파서가 해당 명령을 해석하는 동안 결정됩니다. 그것과 같이 생각하십시오. 자동 d = 유도 된 {std : .regexp { "[0-9] *"s}, std :: regexp { "[^ 0-9.] *"s}, std :: regex { "[0-9 . * "s}}'이후에'd ("123 asdf 5.7 ")'. 아이디어는'(123, "asdf"s, 5.7)'로 호출 연산자를 호출하는 것입니다. – cdonat