0

Lets consider following example:

#include <functional>
#include <iostream>

using namespace std;

class Caller {
 public:
  Caller(function<void()> callback) {
    callback();
  }
};

main() {
#if defined(ONELINER)
  Caller caller = [] { cout << "test"; };
#else
  function<void()> fun = [] { cout << "test"; };
  Caller caller(fun);
#endif  // defined(ONELINER)
}

If we simply try to compile it (with -std=c++11 flag) it will happily finish, and display test when run. However if we define ONELINER macro compilation will fail with:

prog.cpp: In function 'int main()':
prog.cpp:17:40: error: conversion from 'main()::<lambda()>' to non-scalar type 'Caller' requested
   Caller caller = [] { cout << "test"; };

I understand that this is caused by the fact that there is implicit conversion from lambda to std::function and then implicit conversion from std::function to Caller, and we cannot perform 2 conversions at the same time.

Is it somehow possible to make syntax Class object = lambda; work? I'm asking because I played recently with writing my own small testing framework for educational reasons and I thought that this:

UNIT_TEST(test_name) {
  // test content
};

is much more elegant than

UNIT_TEST_BEGIN(test_name)
  // unit test
UNIT_TEST_END()

The former can be achieved with lambdas passed into the UnitTest constructor. But with problem that I described I had to use dirty workaround like:

#define UNIT_TEST(test_name) \
    ::std::function<void(::Helper*)> test_name_helper; \
    ::UnitTest test_name ## _test = \
        test_name_helper = \
        [&] (::Helper* helper)

and it doesn't look elegant at all. But even if this can be done without lambdas I'm still intrigued whether Class object = lamda; syntax can be achieved.

2
  • 1
    Most standard functions with a callable argument use templates. Commented Jan 28, 2015 at 8:37
  • Could you elaborate a little? std::function is a template after all so I guess you have some specific use case in mind? Commented Jan 28, 2015 at 8:39

1 Answer 1

2

Modify the constructor as such:

template<typename CB>
Caller(CB callback) {
  callback();
}

That will allow it to accept any callable argument, be it a lambda, a std::function, function pointer or functor.

Unfortunately the constructor will also accept any other type as well, but give a compiler error when callback can't be "called" like a function.

Sign up to request clarification or add additional context in comments.

3 Comments

Brilliant! It's so simple I feel like a fool now.
On a side note: quick search showed that the accepting-everything problem can be addressed as well with right static assertions (stackoverflow.com/questions/22882170/…).
TBH if callback is used in the initializer list to initialize a std::function< >, you'll get a reasonable error straight away. You don't need an additional static assertion for that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.