2015-02-06 3 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 (не реализованный здесь для простоты) и вызывает H с A.

У меня есть аналогичный 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 раза и получить функтор, который получает аргумент типа «Е», и, наконец, вызывает внутренний обработчик с аргумент «A»:

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

То, что я хочу, чтобы реализовать функцию call_until, которая будет вызывать «Foo» перегружает рекурсивно до тех пор аргумент типа результирующих функтор стал Т.

Скажем, путь конвертеров от 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' на' result_of_t'? Если ваш компилятор не имеет его, напишите один 'template , используя result_of_t = typename std :: result_of :: type;'. Меньше шума. – Yakk

ответ

3

Вашей уточненная проблема заключается в том, что function_traits ожидает типа функции, а не типа лямбды. Чтобы определить это, просто найдите ошибку, извлеките вызванное ею заявление и открутите типы и передайте их напрямую.

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

не удается скомпилировать. Затем я проверил документы, и да, он ожидает тип функции, а не лямбда. Лямбда не являются функциями.


Это эскиз решения проблемы рядом с вашей реальной проблемой. Синтаксис немного отличается, поскольку я ленив.

Адрес 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 raleue:

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 К сожалению, он не скомпилирован :) –

+0

@NikkiChumakov да, объекты с перегрузкой в ​​C++ 03 неловко писать. Вы можете заменить «Action» жестко запрограммированным вызовом 'foo' в' repeat_until_helper' (и называть его 'repeat_foo_until_helper'). Но 'action_result' заканчивается тем, что вам не хватает' decltype' или что-то эквивалентное, и вам не хватает списков типов, чтобы сделать ручное ограничение 'decltype' жизнеспособным. – Yakk

+0

Наконец, я смог запрограммировать версию C++ 03, вручную определяя свойства результата foo как «template struct foo_traits :: type, bool >> :: type" для каждого Arg в [A, B, C, D, E]. И я использовал эти черты в классе footify. Это уродливо, но работает как-то. Спасибо за вашу помощь. –

Смежные вопросы