2014-01-19 2 views
12

Есть ли способ частично связывают первые/последний п аргументов отзывного объекта (например, функция) без явного указания остальных аргументов?Частичного связывания Аргументов функций

std::bind(), кажется, требует, чтобы все аргументы будут связаны, те, которые должны быть оставлены, должны быть связаны с std::placeholders::_1, _2, _3 и т.д.

Можно ли написать bind_first()/bind_last() для частичного обязательным, начиная с первого/последнего аргумента и автоматически вставляя заполнители для любых оставшихся несвязанных аргументов в исходном порядке в их исходной позиции?

+0

Существует 'станд :: bind1st' но его не рекомендуется. – Gasim

+0

Я боюсь, что это невозможно реализовать – Paranaix

+0

Это на самом деле довольно тривиально, чтобы писать. Все, что вам нужно сделать, это сохранить связанные аргументы в 'tuple', принять переменное количество параметров в' operator() ', распаковать кортеж через индексы и затем добавить фактические аргументы. – Xeo

ответ

0

Вдохновленный вопросом, я написал свой собственный prebind с нуля. В конце концов, это выглядело довольно похоже на всех остальных, но я обещаю, что это оригинал :) - называйте это конвергентной эволюцией.

У этого есть немного другой аромат, хотя. Во-первых, он пересылает его конструктору, но вы можете предпочесть использовать std::decay (это имеет смысл в некотором роде, но мне не нравится писать std::ref везде). Для другого я добавил поддержку вложенных prebinds, так что prebind(foo, prebind(GetRandomNumber))() - это то же самое, что и prebind(foo)(GetRandomNumber()).

#include <tuple> 
#include <type_traits> 
using namespace std; 

struct pb_tag {}; //use inheritance to mark prebinder structs 

//result_of_t will be defined by default in c++1y 
template<typename T > using result_of_t = typename result_of<T>::type; 
template<typename T> using is_prebinder = is_base_of<pb_tag, typename remove_reference<T>::type >; 

//ugly sequence generators for something different 
template<int N, int ...S> struct seq : seq<N-1, N, S...> {}; 
template<int ...S> struct seq<0, S...> {typedef seq type;}; 

//these three functions are only for nested prebind. they map 
//T t -> T t and Prebind<f, T...> -> f(T...) 
template<typename T> 
auto dispatchee(T&& t, false_type) -> decltype(forward<T>(t)){ 
    return forward<T>(t); 
} 

template<typename T> 
auto dispatchee(T&& t, true_type) -> decltype(t()) 
{ 
    return t(); 
} 

template<typename T> 
auto expand(T&& t) -> decltype(dispatchee(forward<T>(t), is_prebinder<T>())) 
{ 
    return dispatchee(forward<T>(t), is_prebinder<T>()); 
} 

template<typename T> using expand_type = decltype(expand(declval<T>())); 

//the functor which holds the closure in a tuple 
template<typename f, typename ...ltypes> 
struct prebinder : public pb_tag 
{ 
    tuple<f, ltypes...> closure; 
    typedef typename seq<sizeof...(ltypes)>::type sequence; 
    prebinder(f F, ltypes... largs) : closure(F, largs...) {} 

    template<int ...S, typename ...rtypes> 
    result_of_t<f(expand_type<ltypes>..., rtypes...)> 
    apply(seq<0, S...>, rtypes&& ... rargs){ 
     return (get<0>(closure))(expand(get<S>(closure))... , forward<rtypes>(rargs)...); 
    } 

    template<typename ...rtypes> 
    result_of_t<f(expand_type<ltypes>..., rtypes...)> 
    operator() (rtypes&& ... rargs){ 
     return apply(sequence(), forward<rtypes>(rargs)...); 
    } 
}; 

template<typename f, typename ...ltypes> 
prebinder<f, ltypes...> prebind(f&& F, ltypes&&... largs) 
{ 
    return prebinder<f, ltypes...>(forward<f>(F), forward<ltypes>(largs)...); 
} 

Его также можно легко изменить на почтовый ящик.

Использование выглядит следующим образом:

int g(int a){ return 1 + a; } 

int h(){ return 1; } 

int i(int a, int b, int c, int d){ 
    return 1 + a + b + c + d; 
} 

int main() 
{ 
    //completely bound 
    auto a = prebind(g, 1); 
    cout << a() << endl; 

    //nested bind by reference 
    auto b = prebind(g, a); 
    cout << b() << endl; 
    get<1>(a.closure) = 2; 
    cout << b() << endl; 

    //bind to prebinder 
    auto c = prebind(b); 
    cout << c() << endl; 

    //nested bind of temp to temp 
    auto d = prebind(prebind(g), prebind(h)); 
    cout << d() << endl; 

    //and the one you wanted orginally 
    auto e = prebind(i, 1, 1, 1); 
    cout << e(1) << endl; 

    return 0; 
} 
+0

Вы уверены? Я уверен, что я думал об этом в бесчисленных случаях и всегда обнаруживал, что это неправда. –

+0

Я исправил свой ответ, чтобы дать реальный prebind, [live example.] (Http://ideone.com/UUyhah) – user3125280

6

Ни подталкивания, ни стандартная заливка библиотека bind в автоматически пробелы. Вы можете написать такой гаджет самостоятельно, если у вас будет дождливый вечер для заполнения; вот пример для трейлинг аргументы только простой функции:

#include <tuple> 
#include <type_traits> 
#include <utility> 

template <typename F, typename ...Args> struct trailing_binder; 

template <typename R, typename ...Frgs, typename ...Args> 
struct trailing_binder<R(Frgs...), Args...> 
{ 
    template <typename ...Brgs> 
    trailing_binder(R (*f)(Frgs...), Brgs &&... brgs) 
    : the_function(f) 
    , the_args(std::forward<Brgs>(brgs)...) 
    { } 

    template <unsigned int ...I> struct intlist {}; 

    template <typename ...Brgs> 
    typename std::enable_if<sizeof...(Brgs) + sizeof...(Args) == sizeof...(Frgs), R>::type 
    operator()(Brgs &&... brgs) 
    { 
     return unwrap(std::integral_constant<bool, 0 == sizeof...(Args)>(), 
         intlist<>(), 
         std::forward<Brgs>(brgs)...); 
    } 

private: 
    template <unsigned int ...I, typename ...Brgs> 
    R unwrap(std::false_type, intlist<I...>, Brgs &&... brgs) 
    { 
     return unwrap(std::integral_constant<bool, sizeof...(I) + 1 == sizeof...(Args)>(), 
         intlist<I..., sizeof...(I)>(), 
         std::forward<Brgs>(brgs)...); 
    } 

    template <unsigned int ...I, typename ...Brgs> 
    R unwrap(std::true_type, intlist<I...>, Brgs &&... brgs) 
    { 
     return the_function(std::get<I>(the_args)..., std::forward<Brgs>(brgs)...); 
    } 

    R (*the_function)(Frgs...); 
    std::tuple<Args...> the_args; 
}; 

template <typename R, typename ...Args, typename ...Frgs> 
trailing_binder<R(Frgs...), Args...> trailing_bind(R (*f)(Frgs...), Args &&... args) 
{ 
    return trailing_binder<R(Frgs...), typename std::decay<Args>::type...>(f, std::forward<Args>(args)...); 
} 

Использование:

int f(int a, int b, int c, int d) { return a + b + c + d; } 

int main() 
{ 
    auto b = trailing_bind(f, 1); 
    return b(3, 8, 13); 
} 
+0

Я рекомендую вам проверить мою ссылку комментариев по этому вопросу. :) – Xeo

+0

@Xeo: Вы имеете в виду ссылку со всеми ошибками компилятора? :-) Имейте в виду, что целью является нечто вроде 'bind (f, 1)', который возвращает вызываемый с 3 параметрами. –

+0

* Вопрос *, а не другой ответ. : P – Xeo

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

  • Нет связанных вопросов^_^