2015-02-06 8 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