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.

Mateusz Kubuszok
  • 24,995
  • 4
  • 42
  • 64

1 Answers1

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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Brilliant! It's so simple I feel like a fool now. – Mateusz Kubuszok Jan 28 '15 at 08:54
  • 1
    On a side note: quick search showed that the accepting-everything problem can be addressed as well with right static assertions (http://stackoverflow.com/questions/22882170/c-compile-time-predicate-to-test-if-a-callable-object-of-type-f-can-be-called). – Mateusz Kubuszok Jan 28 '15 at 09:02
  • 1
    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. – MSalters Jan 28 '15 at 09:56