2

So I have the following code:

#include <iostream>

template <typename T>
class funcky
{
  public:
    funcky(char const* funcName, T func)
      : name(funcName), myFunc(func)
    {
    }

  //private:
    char const* name;
    T myFunc;
};

#if 0
int main(void)
{
  char const* out = "nothing";

  // requires template args
  funcky test("hello", [&](int x, int y) -> int
  {
    out = "YES";
    return x + y;
  });

  std::cout << test.name << " = " << test.myFunc(1, 2) << std::endl;
  std::cout << test.name << " = " << out << std::endl;

  return 0;
}

int main2(void)
{
  funcky<void(*)(void)> test("hello", [&, this](void) -> void
  {
    std::cout << this->name << std::endl;
  });

  test.myFunc();

  return 0;
}
#endif

int main(void)
{
  char const* out = "nothing";

  auto myFunc = [&](int x, int y) -> int
  {
    out = "YES";
    return x + y;
  };
  funcky<decltype(myFunc)> test("hello", myFunc);

  std::cout << test.name << " = " << test.myFunc(1, 2) << std::endl;
  std::cout << test.name << " = " << out << std::endl;

  return 0;
}

The top chunk is a function holder that holds a lambda and a name for it.

Next is what I'd like to use API-wise, but fails due to no template arguments being specified.

After that, there's my wondering if it's possible to have a 'this' of a specific type (such as funcky) be used in a lambda not declared inside it. Wishful thinking.

At the very end is code that compiles but uses a lambda outside the funcky constructor and decltype.

Are such things possible in C++11? How I accomplish said things?

Also unless it can kind of have the same API, try not to guess what I'm doing as if I can't do it this way, I'll just rewrite it in a simpler way. It's not worth the effort.

Xeo
  • 129,499
  • 52
  • 291
  • 397
Jookia
  • 6,544
  • 13
  • 50
  • 60

2 Answers2

3

If you want to provide a way for a user to supply a callback to your class, you're better off using std::function, since templating the class on the function / functor type is not a very useful thing to do, as you experienced.

The problem arises from the fact that you can't just take anything in. You should have clear requirements on what can be passed as a callback, since you should know how you want to call it later on. See this on why I make the constructor a template.

#include <functional>
#include <utility>

struct X{
  template<class F>
  X(F&& f) : _callback(std::forward<F>(f)) {} // take anything and stuff it in the 'std::function'

private:
  std::function<int(int,int)> _callback;
};

int main(){
  X x([](int a, int b){ return a + b; });
}

If, however, you don't know how the callback is going to be called (say, the user passes the arguments later on), but you want to support that, template your type on the signature of the callback:

#include <iostream>
#include <functional>
#include <utility>

template<class Signature>
struct X{
  template<class F>
  X(F&& f) : _callback(std::forward<F>(f)) {} // take anything and stuff it in the 'std::function'

private:
  std::function<Signature> _callback;
};

int main(){
  X<int(int,int)> x1([](int a, int b){ return a + b; });
  X<void()> x2([]{ std::cout << "wuzzah\n";});
}
Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • Ah, so there's no way to have the best of both worlds and have Signature implicit or figured from the lambda? – Jookia Sep 21 '12 at 09:45
  • 3
    @Jookia No, that's not going to happen. You should be thinking in terms of callables not lambdas. Lambdas are not special in any way. `struct try_to_deduce_a_signature_for_this_one { template void f(T); };` – R. Martinho Fernandes Sep 21 '12 at 09:47
  • @Jookia: `decltype` doesn't help with finding a signature, at best it can help finding the return type *for certain arguments*, aka you (or the user) always need to know how you want to call it. – Xeo Sep 21 '12 at 10:12
3

Something like

template<typename Functor>
funcky<typename std::decay<Functor>::type>
make_funcky(const char* name, Functor&& functor)
{ return { name, std::forward<Functor>(functor) }; }

can be helpful for things like:

auto test = make_funcky("hello", [&](int x, int y) -> int
{
    out = "YES";
    return x + y;
});

However, inside a lambda expression this always refers to the immediate this outside of the expression. It's not a delayed reference to some this present at the time of the invocation -- it's not an implicit parameter. As such it doesn't make sense to want 'another type' for it.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114