2015-02-06 141 views
1

我有一个将被用作不同的论点职能一些自定义类型:递归调用重载C++函数

struct A {}; 
struct B {}; 
struct C {}; 
struct D {}; 
struct E {}; 

也有一些函数返回仿函数包装:

template <typename H> 
auto foo (H h, 
    enable_if_t<is_same<typename result_of<H(A)>::type, bool>::value>* = 0) 
{ 
    return [h] (B x) { return h (A {}); }; 
} 

这把H(A)仿函数转换成G(B)仿函数,它将输入参数B-> A(在这里为简单起见不实现)并且用A来调用H。

我有类似的con vertors C-> B,D-> C,E-> d:

template <typename H> 
auto foo (H h, 
    enable_if_t<is_same<typename result_of<H(B)>::type, bool>::value>* = 0) 
{ 
    return [h] (C x) { return h (B {}); }; 
} 

template <typename H> 
auto foo (H h, 
    enable_if_t<is_same<typename result_of<H(C)>::type, bool>::value>* = 0) 
{ 
    return [h] (D x) { return h (C {}); }; 
} 

template <typename H> 
auto foo (H h, 
    enable_if_t<is_same<typename result_of<H(D)>::type, bool>::value>* = 0) 
{ 
    return [h] (E x) { return h (D {}); }; 
} 

现在我可以调用foo 4次,​​将得到获取型 “E” 的说法函子,最后调用与内部处理程序参数“A”:

auto inner_handler = [] (A) -> bool { return false; }; 
auto f = foo (foo (foo (foo (inner_handler)))); 
f (E {}); 

我想是实现call_until功能称之为“foo”的重载递归直到产生仿函数参数类型成为T.

假设转换器的从A路径到E永远存在,并且恰好是一个。换句话说,我想表达

auto f = call_until<E> (inner_handler); 

工作完全一样

auto f = foo (foo (foo (foo (inner_handler)))); 

我是从像开始:

template <typename Stop, typename Handler, typename Result> 
struct call_until_helper 
{ 
    Handler handler_; 
    call_until_helper (Handler h) : handler (h) {} 
}; 

template <typename Stop, typename Handler> 
call_until_helper<Stop, Handler, 
    typename boost::function_traits<Handler>::result_type> 
call_until (Handler handler) 
{ 
    return call_until_helper<Stop, Handler, 
    typename boost::function_traits<Handler>::result_type> (handler); 
} 

但我得到的编译错误还挺停留在这点。我需要一些想法来实现这一点。

代码在线:http://ideone.com/ZRFxnw

+0

尝试使用' - >'表示“从左手边接受参数,并返回右边”,因为这更传统的功能符号相匹配。所以有一个超载'foo:''H:A-> bool'' - >''G:B-> bool'。为了让你的代码更清洁,你可以用'result_of_t'替换'result_of'吗?如果您的编译器没有它,请使用result_of_t = typename std :: result_of :: type;'编写一个模板。噪音较小。 – Yakk 2015-02-06 20:07:41

回答

3

你接近的问题是,function_traits需要一个函数类型,而不是一个拉姆达类型。为了确定这一点,只需找到错误,提取导致它的语句,然后剥离这些类型并直接传入它们。

boost::function_traits< decltype(inner_handler) >::result_type b = false; 

无法编译。然后我检查了文档,是的,它期望一个函数类型,而不是lambda。 Lambdas不是功能。


这是解决实际问题附近问题的草图。语法有点不同,因为我很懒。

这是fooify。它代表在单个对象的foo整个过载组:

struct fooify { 
    template<class...Args> 
    auto operator()(Args&&...args)const{ 
    return foo(std::forward<Args>(args)...); 
    } 
}; 

这是直到测试通过该递归地应用一个动作输入一个辅助类型:

template<class Action, template<class...>class test, class Arg, class=void> 
struct repeat_until_helper { 
    using action_result = result_of_t< Action&(Arg) >; 
    auto operator()(Action&& action, Arg&&arg)const { 
    return repeat_until_helper<Action, test, action_result>{}(
     std::forward<Action>(action), 
     action(std::forward<Arg>(arg)) 
    ); 
    } 
}; 
template<class Action, template<class...>class test, class Arg> 
struct repeat_until_helper< Action, test, Arg, 
    std::enable_if_t< test<Arg>{} > 
> { 
    auto operator()(Action&& action, Arg&&arg)const { 
    return std::forward<Arg>(arg); 
    } 
}; 

这是一个功能使用上述辅助,所以我们只需要在一种类型的(测试)来传递,其余推导出:

template<template<class...>class test, class Action, class Arg> 
auto repeat_until(Action&& action, Arg&& arg) { 
    return repeat_until_helper< Action, test, Arg >{}(std::forward<Action>(action), std::forward<Arg>(arg)); 
} 

这里为类型的测试被称为具有E rvalue:

template<class X, class=void> 
struct can_be_called_with_E:std::false_type{}; 
template<class X> 
struct can_be_called_with_E<X, 
    decltype(
     void(
      std::declval<X>()(std::declval<E>()) 
     ) 
    ) 
>:std::true_type{}; 

我们完成了。语法不同,但这只是一些清理工作。

live example

+0

你的代码工作正常,可以很容易地移植到C++ 11。然而,当我尝试将它移植到C++ 03(使用boost元库,类型特征等)时,我感到吃肉困难。特定的问题是无法为编译器定义返回类型“fooify”函子。这是我最好的尝试将它移植到c + + 03:http://coliru.stacked-crooked.com/a/f8b40e6443be9c9e不幸它不编译:) – 2015-02-09 15:36:16

+0

@NikkiChumakov是的,C++ 03中的重载设置对象是尴尬写。你可以用'repeat_until_helper'中的'foo'的硬编码来替换'Action'(并将其称为'repeat_foo_until_helper')。但是'action_result'因为缺乏'decltype'或任何等价物而变得混乱,并且缺乏可以手动限制的'decltype'类型的列表。 – Yakk 2015-02-09 15:45:21

+0

最后,我能够通过手动定义foo的返回结果特征为“template struct foo_traits :: type,bool >> :: type”来为每个Arg编码C++ 03版本在[A,B,C,D,E]中。我在足部课上使用了这些特质。这是丑陋的,但以某种方式工作。谢谢你的帮助。 – 2015-02-09 21:31:08