6

I am currently trying to use std::bind to create a std::function<void()> from the function template

template<class Iterator>
void printRange(Iterator first, Iterator last) {
    std::copy(first, last, std::ostream_iterator<typename Iterator::value_type>(std::cout, " "));
    std::cout << std::endl;
}

Conceptually, what I want is

int main() {
    std::vector<int> v{1, 2, 3};
    auto f0 = std::bind(printRange, v.begin(), v.end()); // won't compile, of course
    f0();
    return 0;
}

I understand that this does not compile and I have to instantiate the function template before I can actually use it. For example, the following alternatives would work:

auto f1 = std::bind(printRange<std::vector<int>::const_iterator>, v.begin(), v.end());
auto f2 = std::bind(printRange<decltype(v.begin())>, v.begin(), v.end());
auto f3 = [&v]() { printRange(v.begin(), v.end()); };

I already created a convenience function

template<class Iterator>
std::function<void()> makePrintRangeFunction(Iterator first, Iterator last) {
    return std::bind(printRange<Iterator>, first, last);
}

to ease the process:

auto f4 = makePrintRangeFunction(v.begin(), v.end());

I wonder if it is possible to create a more generic std::function<void()> generator, accepting a function template as a first argument and the function template arguments as a variable-length argument list? If not using built-in language features, maybe via a macro?

Marcel
  • 616
  • 1
  • 5
  • 15
  • 2
    and a lambda is not acceptable? – TemplateRex Jan 20 '16 at 13:17
  • Yeah, fwiw, I have found lambdas to be so much easier to code and read for all things that would have required `bind` in the past. – underscore_d Jan 20 '16 at 13:18
  • @TemplateRex: In that case lambdas are ok, but once I need to capute more than just one variable, I thought `std::bind` would result in an expression that is more readable... – Marcel Jan 20 '16 at 13:54
  • possible duplicate of http://stackoverflow.com/q/15651488/819272 – TemplateRex Jan 20 '16 at 14:21
  • @Marcel, just use a very 'thin' lambda, which immediately calls the actual function. This will be as readable as bind. Let me know if you'd like to see an example. – SergeyA Jan 20 '16 at 14:29
  • @SergeyA: I know I can use a default capture mode in the lambda expression, but I think this is considered bad style (see, e.g., Meyer's Effective C++ Item 31: Avoid default capture modes). But if you're thinking of something different, I would be happy to see it. – Marcel Jan 20 '16 at 14:35
  • @Marcel, looks like I slightly misunderstood the question. You seem to be remembering the result of `bind`? But what would be the point of it? bind is usually used in-place. – SergeyA Jan 20 '16 at 14:40

3 Answers3

3

As long as you do not need to have template function return type, you can do this:

#include <functional>
#include <iostream>
#include <typeinfo>


template<typename ... T>
std::function<void()> makePrintRangeFunction(void (*f)(T...), T... param) {
    return std::bind(f, param...);
}

template<typename T, typename V>
void print(T type, V val)
{
    std::cout << typeid(type).name() << '\n' << val << '\n';
}

int main()
{
    int i = 5;
    double d = 10.5;
    auto f = makePrintRangeFunction(print, i, d);
    f();
}
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
Revolver_Ocelot
  • 8,609
  • 3
  • 30
  • 48
2

Maybe the following code will help :)

template <class F, class... Args>
void test(F&& f, Args&&... args) {
 std::function<typename std::result_of<F(Args...)>::type()> task(
   std::bind(std::forward<F>(f), std::forward<Args>(args)...));
   task();
}
Tommy
  • 21
  • 2
1

If your compiler supports C++14 you could define a generic lambda wrapper as:

template<typename F>
auto fungen(F f) {
  return [=](auto... args) { f(args...); };
}

Use case:

int main() {
  std::vector<int> v {1, 2, 3, 4};
  auto f = fungen(printRange<std::vector<int>::iterator>);
  f(v.begin(), v.end());
}

Live Demo

101010
  • 41,839
  • 11
  • 94
  • 168
  • I don't really where your solution is different from `auto f1 = std::bind(printRange::const_iterator>, v.begin(), v.end());` or am I missing something? – Marcel Jan 20 '16 at 13:48
  • It seems that OP wants compiler to deduce template arguments from function parameters. Use case: `auto f = fungen(printRange, v.begin(), v.end());`. Sadly template templates work only with class types and I do not see if it is possible to twist template argument deduction to work in this case. – Revolver_Ocelot Jan 20 '16 at 13:50
  • @Revolver_Ocelot: ...and if I used a class type instead, I would have to make the template parameter explicit again, right? – Marcel Jan 20 '16 at 13:55