2015-03-20 2 views
0

Мне нужна функция bind, которая ведет себя как std::bind, но возвращает соответствующую специализацию std::function.std :: bind-like функция возвращает std :: function

Я думаю, что аргументы шаблона std::function можно извлечь, используя номер заполнителя из параметра функции. Однако это не выглядит тривиальным. Существует ли какая-либо реализация?


Почему мне это нужно

Я хочу реализовать waterfall функции с семантикой, похожей на this JavaScript one.

Вот как я себе это будет выглядеть в C++:

std::function<void(const std::string &)> f = waterfall(
    [](const std::function<void(int)> &cb, const std::string &) { 
    ...; 
    cb(1); 
    }, 
    [](const std::function<void(double, double)> &cb, int) { 
    ...; 
    cb(0.5, 10); 
    }, 
    [](double, double) { 
    } 
); 

Другими словами, waterfall бы кучу функций, каждая из которых (кроме последнего) принимает функцию в качестве первого параметр. waterfall свяжет каждую функцию с предыдущей (начиная с последней, конечно) и возвращает одну функцию.

В основном водопад должен быть чем-то вроде этого:

// recursion termination: `waterfall` called with a single functions 
template< typename Arg > 
auto waterfall(const Arg& first) -> decltype(first) { 
    return first; 
} 
// recursion: `waterfall` called with mulitple functions 
template< typename Arg, typename... Args > 
... waterfall(const Arg& first, Args... args) { 
    return std::bind(first, waterfall(std::forward<Args>(args)...)); 
} 

Есть три открытые проблемы, хотя:

  1. выясняя тип возвращаемого waterfall при вызове с несколькими аргументами. Это не может быть decltype(std::bind(first, waterfall(...))) (потому что C++ не позволяет рекурсивно вызывать функцию шаблона, чтобы вывести его тип). Если бы я знал тип функции (т. Е. Arg), то без первого аргумента был бы тип возвращаемого типа, который я ищу.
  2. Я думаю, мне нужно знать количество аргументов first, чтобы правильно std::bind.
  3. std::bind тип возврата не ведет себя так, как я хочу: вложенные std::bind звонки объединяет все связанные функции вместе, а не составлять их.

я смог обойти третью точку, написав std::bind оболочку, которая обертывает связанную функцию в объект типа T таким образом, что std::is_bind_expression<T>::value == false.

Для первых двух точек мне нужно выяснить тип возвращаемого значения и аргументы типа waterfall. Это было бы тривиально, если бы функции были std::function с. Было бы также просто, если бы они были лямбдами, классическими функциями или функторами с одним operator(): мне просто нужно было использовать что-то вроде this function_traits. Однако я бы действительно бы передать функции, связанные с использованием std::bind к waterfall, без необходимости вручную ввергнут их в std::function с, потому что это делает мой код путь короче и пути яснее с большими waterfall с.

Любые идеи, мысли или предложения?

+2

В C++ 11 вам следует предпочесть использовать лямбда-выражения над 'std :: bind' и друзьями. Можете ли вы показать пример того, что вы хотите сделать? Обратите внимание, что блокировки лямбда неявно конвертируются в 'std :: function's. – 5gon12eder

+1

Можете ли вы объяснить, как вы собираетесь использовать это? И почему вы думаете, что вам это нужно? В общем случае вы не можете вывести уникальную подпись для объекта вызова функции, не говоря уже о том, что имеет некоторые аргументы. Почти всегда потребитель объекта знает, как он будет называть объект, и как таковой должен быть тот, который определяет тип стирания, а не поставщик вызываемого объекта. Наиболее распространенной недостоверной причиной является то, что вы пишете протокол удаленного вызова (из другого пространства памяти или другого языка), и даже там интерфейс протокола должен определять подпись! – Yakk

+0

Существует один ответ здесь http://stackoverflow.com/a/21788988/683218 (by dyp) на мой вопрос о том, как вернуть объект типа связывания, который не позволяет лишние параметры. Я не знаю, является ли это актуальным или вы можете адаптировать его для возврата объекта std :: function. – tinlyx

ответ

2

Дизайн здесь должен выполнять большую часть работы в классе помощника details::waterfall с 2-функциональной композицией стиля водопада выполнен в waterfall_call.

#include <iostream> 
#include <utility> 
#include <functional> 
#include <string.h> 

namespace details { 

Это будет немного более эффективным, если бы я распадался за пределами определения структуры, так как это приведет к сокращению уникальных типов в некоторых случаях. Я не забочусь:

template<class F0, class F1> 
    struct waterfall_call { 
    typename std::decay<F0>::type f0; 
    typename std::decay<F1>::type f1; 

f0 Вызов Попутно f1 и арг ...:

template<class...Args> 
    auto operator()(Args&&... args)const 
    -> typename std::result_of<F0 const&(F1 const&,Args...)>::type 
    { 
     return f0(f1, std::forward<Args>(args)...); 
    } 
    }; 

Центр алгоритма:

struct waterfall { 

Для 2-арг, просто использовать waterfall_call:

template<class F0, class F1> 
    waterfall_call<F0, F1> operator()(F0&& f0, F1&& f1)const 
    { 
     return { std::forward<F0>(f0), std::forward<F1>(f1) }; 
    } 

> 2 арг, повторялись се, затем сделать 2 ARG решение:

template<class F0, class...Fs, 
     class I=typename std::result_of<waterfall const&(Fs...)>::type 
    > 
    auto operator()(F0&& f0, Fs&&... fs)const 
    -> typename std::result_of< waterfall(F0, I) >::type 
    { 
     auto child = (*this)(std::forward<Fs>(fs)...); 
     return (*this)(std::forward<F0>(f0), std::move(child)); 
    } 

Для 1 арг, только вперед к распались версии входного арг:

template<class F0> 
    auto operator()(F0&&f0)const 
    ->typename std::decay<F0>::type 
    { 
     return std::forward<F0>(f0); 
    } 
    }; 
} 

И сам waterfall только делегатов на details::waterfall:

template<class...Fs> 
auto waterfall(Fs&&... fs) 
-> typename std::result_of<details::waterfall(Fs...)>::type{ 
    return details::waterfall{}(std::forward<Fs>(fs)...); 
} 

live example

другой путь (быть стороны struct трюк выше) обхода ограничений рекурсивного возврата возвращаемого типа шаблона - это взять параметр по типу шаблона из пространства имен функции и передать его рекурсивным вызовам. Это позволяет использовать поиск ADL, который может рекурсивно найти вашу собственную функцию даже при выводе вашего собственного типа возврата.

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