Passing an arbitrary lambda expression to a function? - c++

I have a pretty exotic problem, since I am trying to create some sort of a compiler...
I want to pass a lambda expression to a templated function like this:
template<class T>
delegate<T>* bind(std::function<T> func)
{
return nullptr;
}
So that I can now call
bind([&](int a) { // do something });
... So normally, this wouldn't be a problem, since std::function is able to capture a lambda. But here the problem is that (let's assume) I don't know or don't want to provide the info about what "T" is exactly. It could be any function signature you could also insert into std::function<>...
I also need to pass this "inferred" signature back to the "delegate" class I want to return and that delegate class needs to be a pointer to class...
For now I have come up with this:
template<class T>
struct delegate : public std::function<T>
{
delegate(const std::function<T>& func) : std::function<T>(func) { }
delegate(const delegate<T>& func) { }
};
template<class T>
delegate<T>* bind(std::function<T>&& func)
{
return new delegate<T>(std::forward<std::function<T>>(func));
}
But the above example of a call to "bind" fails with "failed template argument deduction". How can I get this to work without having to specify the "T" parameter explicitly when calling "bind" (then it works, at least)?
Since in theory my compiler has all the info to workaround this issue, I could just insert the instance of "T", but this would make the generated code unnecessarily convoluted.
BTW, I am using the lastest Clang compiler.
Here is the final solution:
template<typename T> struct get_signature;
template<typename Mem, typename Ret, typename... Args> struct get_signature<Ret(Mem::*)(Args...) const> {
typedef Ret type(Args...);
};
template<class T>
delegate<typename get_signature<decltype(&T::operator())>::type>* bind(T func)
{
return nullptr;
}
Be aware that you may need to adjust the "const" modifiers to your needs.

First, you have completely screwed up your rvalue references. Your forwarding does not work at all.
Secondly, that's impossible in some cases.. For some function objects, you could infer the signature by taking the member function pointer to operator() and inspecting it's signature. But for others, they will be overloaded and it will not work.
template<typename T> struct get_signature;
template<typename Mem, typename Ret, typename... Args>
struct get_signature<Ret(Mem::*)(Args...)> {
typedef Ret(Args...) type;
};
template<class T>
delegate<typename get_signature<&T::operator()>::type>* bind(T func)
{
return nullptr;
}
Thirdly, std::bind?

Related

how to get a return type of a member function pointer

Is there a way to determine a return type of a member function pointer?
Code sample:
///// my library
void my_func(auto mptr) { // have to use `auto`
// some logic based on a return type of mptr: int, string, A, etc.
}
///// client code
struct A {
int foo();
std::string bar(int);
};
class B{
public:
A func(int, double);
};
// ... and many other classes
my_func(&A::foo);
my_func(&A::bar);
my_func(&B::func);
// ... many other calls of my_func()
I need to "fill in" my_func().
Edit:
I can't use std::result_of/std::invoke_result as I don't know the full list of parameters of mptr. It's not important with which params a method is supposed to be called as I'm not calling it. I would like to avoid creating an object of base class of mptr even if I'm able to determine it (using declval is ok).
You can use partial template specialization to determine the return type of mptr:
template <typename T>
struct ReturnType;
template <typename Object, typename Return, typename... Args>
struct ReturnType<Return (Object::*)(Args...)>
{
using Type = Return;
};
void my_func(auto mptr) {
typename ReturnType<decltype(mptr)>::Type obj;
}
Live Demo
You can write a function that deduces the type of a member function pointer, and returns the deduced return type. Note that only a declaration, and no definition is needed
template <typename C, typename Ret, typename... Args>
auto ret_type(Ret (C::*)(Args...)) -> Ret;
void my_func(auto mptr)
{
using type = decltype(ret_type(mptr));
}
In my opinion, this is also easier to read than the specialization solution.
Here's a demo
You can also account for cv-qualifiers by adding overloads. e.g.
template <typename C, typename Ret, typename... Args>
auto ret_type(Ret (C::*)(Args...) const) -> Ret;
Here's a demo

C++ template functions with the same name but different return type

I am trying to make some file streamer that can read or write different types. Everything works except for the reading part with specific method. That method returns std::unique_ptr<T> when called and is a "wrapper" for another method that returns T. For some reason compiler does not use this method instead it tries to compile it with the other method (the one that returns T). The compilation fails because of this. I've already tried to search around internet but I can not find any accurate answer. Could you please help me with this.
The two methods I have defined:
template <typename T>
T read()
{
T obj;
obj.readFromFile<T>();
return std::move(obj);
}
and
template <
typename T,
template<typename> class D,
template<typename, typename> class Container
>
typename std::enable_if_t<
std::is_same<Container<T, D<T>>, std::unique_ptr<T, D<T>>>::value,
Container<T, D<T>>
>
read()
{
return std::move(std::make_unique<T, D<T>>(readFromFile<T>()));
}
The latter method is the one that I am trying to call.
When I write something like this:
std::unique_ptr<A> AfromFile = fileStreamer.read<std::unique_ptr<A>>()
compiler tries to compile it with the first method (template <typename T> T read() {...}) and the compilation fails. I could make this work if I made unique_ptr object first and than do the copy assignment to *unique_ptr<A> object but this is no good for me because i use some macro over those two functions and I cannot make unique_ptr<A> object or object A it self prior to calling the macro. Just for info I am using Visual Studio 2015.
Is there any way to make this work without any significant modification? I also found one suggestion that basically says you have to add a pointer parameter to one function and then call it with static_cast<Obj>(nullptr) as an argument but this does not count in my example.
Thanks for your help.
Update:
I just want to make a remark that all solutions below did work for me however the easiest way to fix my problem was solution provided by Barry.
Thx again for helping me!
It seems you want partial specialization, and as partial specialization on function is not possible, you may forward to class:
template <typename T> struct helper
{
T operator() const
{
T obj;
obj.readFromFile<T>();
return obj;
}
};
template <typename T, typename D>
struct helper<std::unique_ptr<T, D>>
{
std::unique_ptr<T, D> operator() const
{
return std::make_unique<T, D>(readFromFile<T>());
}
};
template <typename T>
T read()
{
return helper<T>{}();
}
The problem is, while I understand your intent of:
std::unique_ptr<A> AfromFile = fileStreamer.read<std::unique_ptr<A>>();
You're not actually calling the function you think you are. You have two overloads of read:
template <class T> T read();
template <class T,
template<typename> class D,
template<typename, typename> class Container
> T read();
The first has one template parameter, the second has 3 (and some sfinae). But you're only calling read() with one template parameter, so the second overload - the one you want - isn't even an option.
For these cases, I like simply tag dispatching so that we can overload instead of having to specialize:
template <class T> struct tag{};
template <class T> T read() { return read(tag<T>{}); }
template <class T>
T read(tag<T> ) {
T obj;
obj.readFromFile<T>();
return obj; // <== NB: no move() here! That inhibits RVO
}
template <class T, class D>
std::unique_ptr<T, D> read(tag<std::unique_ptr<T, D>> ) {
/* unique_ptr case */
}
You cannot have two overloads of a function which only differ by return type. You must use SFINAE to make sure only one is enabled for any given template parameter.
The way you are trying to deduce the template parameters in the second overload is wrong. Currently, you have to specify T, D and ContainerType when you call the function. I sense you probably want to pass just one type and then deduce whether it is a std::unique_ptr.
You cannot call std::make_unique and specify a deleter type. You must call the std::unique_ptr constructor with a newly created object.
You don't need to explicitly move the returned std::unique_ptr.
This is one way to do what you want.
#include <memory>
#include <type_traits>
template<typename T>
T readFromFile() { return T(); }
template<typename T, typename D>
void helper(std::unique_ptr<T, D>);
template<typename T, typename = void>
struct is_unique_ptr : std::false_type {};
template<typename T>
struct is_unique_ptr<T, decltype(helper(std::declval<T>()))> : std::true_type {};
template<typename T, typename = std::enable_if_t<!is_unique_ptr<T>::value>>
T read()
{
return readFromFile<T>();
}
template<typename P, typename = std::enable_if_t<is_unique_ptr<P>::value>, typename = void>
P read()
{
using T = typename P::element_type;
return P(new T(readFromFile<T>()));
}
int main()
{
read<std::unique_ptr<int>>();
read<int>();
}

std::function/bind like type-erasure without Standard C++ library

I'm developing a simple event driven application in C++11 based on the publish/subscribe pattern. Classes have one or more onWhateverEvent() method invoked by the event loop (inversion of control). Since the application is in fact a firmware, where code size is critical and flexibility is not of high priority, the 'subscribe' part is a simple table with event id's and associated handlers.
Here's a very simplified code of the idea:
#include <functional>
enum Events {
EV_TIMER_TICK,
EV_BUTTON_PRESSED
};
struct Button {
void onTick(int event) { /* publish EV_BUTTON_PRESSED */ }
};
struct Menu {
void onButtonPressed(int event) { /* publish EV_SOMETHING_ELSE */ }
};
Button button1;
Button button2;
Menu mainMenu;
std::pair<int, std::function<void(int)>> dispatchTable[] = {
{EV_TIMER_TICK, std::bind(&Button::onTick, &button1, std::placeholders::_1) },
{EV_TIMER_TICK, std::bind(&Button::onTick, &button2, std::placeholders::_1) },
{EV_BUTTON_PRESSED, std::bind(&Menu::onButtonPressed, &mainMenu, std::placeholders::_1) }
};
int main(void)
{
while(1) {
int event = EV_TIMER_TICK; // msgQueue.getEventBlocking();
for (auto& a : dispatchTable) {
if (event == a.first)
a.second(event);
}
}
}
This compiles and runs fine with a desktop compiler, and std:function<void(int)>> fn = std::bind(&SomeClass::onSomething), &someInstance, std::placeholders::_1) elegantly implements type erasure so the event dispatch table can hold handlers of different classes, thus different types.
The problem comes with the embedded compiler (AVR-GCC 4.8.3) which supports C++11, but there's no Standard C++ Library: no <functional> header. I was thinking how can I re-create the above behavior with compiler features only. I evaluated a few options, but there are objections for each (by the compiler or me):
Create an interface with a virtual void Handler::onEvent(int event) method, and derive Button and Menu from it. The dispatch table can hold interface pointers, and virtual method calls do the rest. This is the most simple approach but I don't like the idea of limiting the number of event handler methods to one per class (with doing local if-else event dispatch), and having the overhead of a virtual method call per event.
My second idea still contains a virtual method call, but has no restrictions on the Button and Menu class. It's a virtual method call based type-erasure with functors:
struct FunctBase {
virtual void operator()(int event) = 0;
};
template<typename T>
struct Funct : public FunctBase
{
T* pobj; //instance ptr
void (T::*pmfn)(int); //mem fun ptr
Funct(T* pobj_, void (T::*pmfn_)(int)) : pobj(pobj_), pmfn(pmfn_) {}
void operator()(int ev) override {
(pobj->*pmfn)(ev);
}
};
Funct can hold instance and method pointers, and the dispatch table can be constructed of FunctBase pointers. This way table is as flexible as with function/bind: can hold any class (type) and any number of handlers per class. My only problem that it still contains 1 virtual method call per event, it's just moved to the functor.
My third idea is a simple hack converting method pointers to function pointers:
typedef void (*Pfn)(void*, int);
Pfn pfn1 = reinterpret_cast<Pfn>(&Button::onTick);
Pfn pfn2 = reinterpret_cast<Pfn>(&Menu::onButtonPressed);
As far as I know this is Undefined Behavior and indeed makes the compiler emit a big fat warning. It's based on the assumption that c++ methods have an implicit 1st argument pointing to this. Nonetheless it works, it's lightweight (no virtual calls), and it's flexible.
So my question: Is it possible to do something like option 3 in clean C++ way? I know there's a void* based type-erasure technique (opposed to virtual method call in option 2), but I don't know how to implement it. Looking at desktop version with std::bind also gives me the impression that it binds the first implicit argument to be the instance pointer, but maybe that's just the syntax.
A solid, efficient, std::function<R(Args...)> replacement isn't hard to write.
As we are embedded, we want to avoid allocating memory. So I'd write a small_task< Signature, size_t sz, size_t algn >. It creates a buffer of size sz and alignment algn in which it stores its erased objects.
It also stores a mover, a destroyer, and an invoker function pointer. These pointers can either be locally within the small_task (maximal locality), or within a manual struct vtable { /*...*/ } const* table.
template<class Sig, size_t sz, size_t algn>
struct small_task;
template<class R, class...Args, size_t sz, size_t algn>
struct small_task<R(Args...), sz, algn>{
struct vtable_t {
void(*mover)(void* src, void* dest);
void(*destroyer)(void*);
R(*invoke)(void const* t, Args&&...args);
template<class T>
static vtable_t const* get() {
static const vtable_t table = {
[](void* src, void*dest) {
new(dest) T(std::move(*static_cast<T*>(src)));
},
[](void* t){ static_cast<T*>(t)->~T(); },
[](void const* t, Args&&...args)->R {
return (*static_cast<T const*>(t))(std::forward<Args>(args)...);
}
};
return &table;
}
};
vtable_t const* table = nullptr;
std::aligned_storage_t<sz, algn> data;
template<class F,
class dF=std::decay_t<F>,
// don't use this ctor on own type:
std::enable_if_t<!std::is_same<dF, small_task>{}>* = nullptr,
// use this ctor only if the call is legal:
std::enable_if_t<std::is_convertible<
std::result_of_t<dF const&(Args...)>, R
>{}>* = nullptr
>
small_task( F&& f ):
table( vtable_t::template get<dF>() )
{
// a higher quality small_task would handle null function pointers
// and other "nullable" callables, and construct as a null small_task
static_assert( sizeof(dF) <= sz, "object too large" );
static_assert( alignof(dF) <= algn, "object too aligned" );
new(&data) dF(std::forward<F>(f));
}
// I find this overload to be useful, as it forces some
// functions to resolve their overloads nicely:
// small_task( R(*)(Args...) )
~small_task() {
if (table)
table->destroyer(&data);
}
small_task(small_task&& o):
table(o.table)
{
if (table)
table->mover(&o.data, &data);
}
small_task(){}
small_task& operator=(small_task&& o){
// this is a bit rude and not very exception safe
// you can do better:
this->~small_task();
new(this) small_task( std::move(o) );
return *this;
}
explicit operator bool()const{return table;}
R operator()(Args...args)const{
return table->invoke(&data, std::forward<Args>(args)...);
}
};
template<class Sig>
using task = small_task<Sig, sizeof(void*)*4, alignof(void*) >;
live example.
Another thing missing is a high quality void(Args...) that doesn't care if the passed-in callable has a return value.
The above task supports move, but not copy. Adding copy means that everything stored must be copyable, and requires another function in the vtable (with an implementation similar to move, except src is const and no std::move).
A small amount of C++14 was used, namely the enable_if_t and decay_t aliases and similar. They can be easily written in C++11, or replaced with typename std::enable_if<?>::type.
bind is best replaced with lambdas, honestly. I don't use it even on non-embedded systems.
Another improvement would be to teach it how to deal with small_tasks that are smaller/less aligned by storing their vtable pointer rather than copying it into the data buffer, and wrapping it in another vtable. That would encourage using small_tasks that are just barely large enough for your problem set.
Converting member functions to function pointers is not only undefined behavior, often the calling convention of a function is different than a member function. In particular, this is passed in a particular register under some calling conventions.
Such differences can be subtle, and can crop up when you change unrelated code, or the compiler version changes, or whatever else. So I'd avoid that unless you have little other choice.
As noted, the platform lacks libraries. Every use of std above is tiny, so I'll just write them:
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
using size_t=decltype(sizeof(int));
move
template<class T>
T&& move(T&t){return static_cast<T&&>(t);}
forward
template<class T>
struct remove_reference:tag<T>{};
template<class T>
struct remove_reference<T&>:tag<T>{};
template<class T>using remove_reference_t=type_t<remove_reference<T>>;
template<class T>
T&& forward( remove_reference_t<T>& t ) {
return static_cast<T&&>(t);
}
template<class T>
T&& forward( remove_reference_t<T>&& t ) {
return static_cast<T&&>(t);
}
decay
template<class T>
struct remove_const:tag<T>{};
template<class T>
struct remove_const<T const>:tag<T>{};
template<class T>
struct remove_volatile:tag<T>{};
template<class T>
struct remove_volatile<T volatile>:tag<T>{};
template<class T>
struct remove_cv:remove_const<type_t<remove_volatile<T>>>{};
template<class T>
struct decay3:remove_cv<T>{};
template<class R, class...Args>
struct decay3<R(Args...)>:tag<R(*)(Args...)>{};
template<class T>
struct decay2:decay3<T>{};
template<class T, size_t N>
struct decay2<T[N]>:tag<T*>{};
template<class T>
struct decay:decay2<remove_reference_t<T>>{};
template<class T>
using decay_t=type_t<decay<T>>;
is_convertible
template<class T>
T declval(); // no implementation
template<class T, T t>
struct integral_constant{
static constexpr T value=t;
constexpr integral_constant() {};
constexpr operator T()const{ return value; }
constexpr T operator()()const{ return value; }
};
template<bool b>
using bool_t=integral_constant<bool, b>;
using true_type=bool_t<true>;
using false_type=bool_t<false>;
template<class...>struct voider:tag<void>{};
template<class...Ts>using void_t=type_t<voider<Ts...>>;
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply:false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
namespace details {
template<class From, class To>
using try_convert = decltype( To{declval<From>()} );
}
template<class From, class To>
struct is_convertible : can_apply< details::try_convert, From, To > {};
template<>
struct is_convertible<void,void>:true_type{};
enable_if
template<bool, class=void>
struct enable_if {};
template<class T>
struct enable_if<true, T>:tag<T>{};
template<bool b, class T=void>
using enable_if_t=type_t<enable_if<b,T>>;
result_of
namespace details {
template<class F, class...Args>
using invoke_t = decltype( declval<F>()(declval<Args>()...) );
template<class Sig,class=void>
struct result_of {};
template<class F, class...Args>
struct result_of<F(Args...), void_t< invoke_t<F, Args...> > >:
tag< invoke_t<F, Args...> >
{};
}
template<class Sig>
using result_of = details::result_of<Sig>;
template<class Sig>
using result_of_t=type_t<result_of<Sig>>;
aligned_storage
template<size_t size, size_t align>
struct alignas(align) aligned_storage_t {
char buff[size];
};
is_same
template<class A, class B>
struct is_same:false_type{};
template<class A>
struct is_same<A,A>:true_type{};
live example, about a dozen lines per std library template I needed.
I would put this "std library reimplementation" into namespace notstd to make it clear what is going on.
If you can, use a linker that folds identical functions together, like the gold linker. template metaprogramming can cause binary bloat without a solid linker to strip it.
Your 1st idea is your typical object oriented solution to the problem. It's perfectly fine, but a bit heavy-handed - not quite as usable as std::function. Your 3rd idea is undefined behavior. Nope nope nope.
Your 2nd idea - now there's something we can work with! This is close to how std::function is actually implemented. We can write a class that can take any object that is callable with int and returns void:
class IntFunc {
private:
struct placeholder {
virtual ~placeholder() = default;
virtual void call(int ) = 0;
};
template <typename F>
struct holder : placeholder {
holder(F f) : func(f) { }
void call(int i) override { func(i); }
F func;
};
// if you wrote your own unique_ptr, use it here
// otherwise, will have to add rule of 5 stuff
placeholder* p;
public:
template <typename F>
IntFunc(F f)
: placeholder(new holder<F>(f))
{ }
template <typename Cls>
IntFunc(Cls* instance, void (Cls::*method)(int )) {
auto lambda = [=](int i){ (instance->*method)(i); };
placeholder = new holder<decltype(lambda)>(lambda);
}
void operator()(int i) {
p->call(i);
}
};
With that, you basically have std::function<void(int)> in a usable, generic way.
Now a 4th idea might be to just extend your 3rd idea to something usable. Actually use function pointers:
using Pfn = void (*)(void*, int);
And then use lambdas to make such things:
Pfn buttonOnTick = [](void* ctxt, int i){
static_cast<Button*>(ctxt)->onTick(i);
};
But then you have to hold on to the contexts somehow - which adds extra work.
Before I try to write all the STL stuff by hand, I try to use the STL which I already have from the compiler itself. Because most of the STL code you use is header only, I simply include it and do some minor hacks to get them compiled. In fact id did 10 minutes to get it ready to link!
I used avr-gcc-5.2.0 version without any problems for the task. I have no old installation and I believe it is easier to install the actual version in some minutes instead of fixing problems from the old one.
After compile your example code for avr I got link errors:
build-check-std-a520-nomemdbg-os-dynamic-noncov/main.o: In function `std::__throw_bad_function_call()':
/home/krud/own_components/avr_stl/avr_stl009/testing/main.cpp:42: undefined reference to `operator delete(void*, unsigned int)'
/home/krud/own_components/avr_stl/avr_stl009/testing/main.cpp:42: undefined reference to `operator delete(void*, unsigned int)'
collect2: error: ld returned 1 exit status
Simply write your own __throw_bad_function_call and get rid of the link problem.
For me it makes really no sense to write a own STL implementation. Here I simply used the headers which comes from the compiler installation ( gcc 5.2.0).

Concept checking a function doesn't work with movable-only arguments

I have a function that calls a callback function that accepts a movable-only type (for example unique_ptr).
template <typename Function>
void foo(const Function& function) {
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, std::unique_ptr<Bar>));
auto bar = std::make_unique<Bar>();
...
function(std::move(bar));
}
Trying to compile this code, I get a message that the BOOST_CONCEPT_ASSERT line tries to copy the unique_ptr. If I remove the line, the code works fine. It seems that the Boost.Concept library does not support move semantics. Is there any workaround for this without writing my own concept class (which, incidentally, would not be very simple to support both lvalues and rvalues as their arguments).
That's correct. Unfortunately, UnaryFunction as a concept is written as:
BOOST_concept(UnaryFunction,(Func)(Return)(Arg))
{
BOOST_CONCEPT_USAGE(UnaryFunction) { test(is_void<Return>()); }
private:
void test(boost::mpl::false_)
{
f(arg); // "priming the pump" this way keeps msvc6 happy (ICE)
Return r = f(arg);
ignore_unused_variable_warning(r);
}
void test(boost::mpl::true_)
{
f(arg); // <== would have to have std::move(arg)
// here to work, or at least some kind of
// check against copy-constructibility, etc.
}
#if (BOOST_WORKAROUND(__GNUC__, BOOST_TESTED_AT(4) \
&& BOOST_WORKAROUND(__GNUC__, > 3)))
// Declare a dummy construktor to make gcc happy.
// It seems the compiler can not generate a sensible constructor when this is instantiated with a refence type.
// (warning: non-static reference "const double& boost::UnaryFunction<YourClassHere>::arg"
// in class without a constructor [-Wuninitialized])
UnaryFunction();
#endif
Func f;
Arg arg;
};
Since arg is passed by lvalue, there's no way to get that to work with Boost.Concepts. Directly. You could write a hack though. Since we're just calling checking that f(arg) is valid, we could construct a local type for arg that is convertible to unique_ptr<Bar>. That is:
template <typename Function>
void foo(Function f)
{
struct Foo {
operator std::unique_ptr<int>();
};
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, Foo>));
f(std::make_unique<int>(42));
}
Or more generally:
template <typename T>
struct AsRvalue {
operator T(); // no definition necessary
};
template <typename Function>
void foo(Function f)
{
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, AsRvalue<std::unique_ptr<int>>>));
f(std::make_unique<int>(42));
}
That compiles for me on gcc and clang (though gives a warning on clang about unused typedefs). However, at that point, it may be clearer to just write out your own concept to get it to work. Something like Piotr's would be easiest.
#include <type_traits>
#include <utility>
template <typename...>
struct voider { using type = void; };
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
template <typename, typename = void_t<>>
struct is_callable : std::false_type {};
template <typename F, typename... Args>
struct is_callable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type {};
//...
static_assert(is_callable<Function&(std::unique_ptr<Bar>)>{}, "Not callable");
DEMO

How to make constructor accepting std::function also accept method pointers

Say I have
class Foo
{
public:
Foo(std::function<void(std::string)> callback1,
std::function<void(int)> callback2) :
callback1(callback1), callback2(callback2)
{
}
private:
std::function<void(std::string)> callback1;
std::function<void(int)> callback2
}
I can easily construct it in the case I'm using plain functions
void callback1Handler(std::string)
{
// i'm a potato
}
void callback2Handler(int)
{
// i'm a teapot
}
int main()
{
Foo foo(callback1Handler, callback2Handler);
return 0;
}
But if I'm using a class, then things get complicated.
I'd like to be able to do something like the Addendum of this answer does
class Main
{
public:
Main()
{
Foo foo(&Main::callback1Handler, &Main::callback2Handler);
}
void callback1Handler(std::string)
{
// i'm a potato
}
void callback2Handler(int)
{
// i'm a teapot
}
}
But that obviously wouldn't compile. To make it work one would need to use either std::bind or lambdas, or something else.
I want to try to avoid the ugliness of std::bind(&Main::callbackNHandler, this, std::placeholders::_1, ..., std::placeholders::_N), which is mostly the ugliness of having those placeholders explicitly specified.
Using lambdas as [=](std::string str){callbackNHandler(str);} is also ugly, because you have to specify every single variable the callback accepts.
It would be great if I could just use &Main::callback1Handler, just like in the Main class example, or &Main::callback1Handler, this, which is a bit more flexible, since you can specify the instance pointer of Main.
Note that Foo constructor has to remain the same and callback1&callback2 member variables have to also remain the same. However, you may overload Foo constructor, making the overload convert method pointers (or whatever else you will make it to accept) into std::functions and store them in callback1&callback2.
template<class T, class R, class...Args>
std::function<R(Args...)> method( T* t, R(T::*m)(Args...) ){
return [=](Args&&...args)->R{
return (t->*m)(std::forward<Args>(args)...);
};
}
then method( instance, &Main::callback1Handler ) should work, up to typos.
It gets simpler in C++14.
The above does needless type erasure: a helper type instead of a lambda lets you avoid that.
template<class T, class Sig>
struct method_t;
template<class T, class R, class... Args>
struct method_t<T,R(Args...)> {
T* t;
R(T::*m)(Args...);
template<class...Ts>
R operator()(Ts&&...ts)const{
return (t->*m)(std::forward<Ts>(ts)...);
}
};
template<class T, class R, class...Args>
method_t<T, R(Args...)> method( T* t, R(T::*m)(Args...) ){
return {t, m};
}
with the same signature. It can even be stored in a std::function<R(Args...)>. The advantage of the above is that there is no needless overhead if the signature of the std::function does not exactly match, or if you don't use a std::function at all.
The downside is 3x as much code.
In C++14 we can simply do:
template<class T, class R, class...Args>
auto method( T* t, R(T::*m)(Args...) ){
return [=](auto&&...args)->R{
return (t->*m)(decltype(args)(args)...);
};
}
which is as brief as the first one, and as powerful as the second one.
Using lambdas as [=](std::string str){callbackNHandler(str);} is also ugly, because you have to specify every single variable the callback accepts.
C++14 generic lambdas solve that problem:
[this](auto... args){ callback1Handler(args...); }
For C++11 you could write a simple utility that can be used like so:
Foo foo(simple_bind(&Main::callback1Handler, this),
simple_bind(&Main::callback2Handler, this));
That would be defined as a function template like this:
template<typename T, typename F>
SimpleBinder<T, F>
simple_bind(F T::* memfn, T* that)
{ return { that, memfn }; }
Where the type SimpleBinder<T, F> is just:
template<typename T, typename F>
struct SimpleBinder
{
T* m_this;
F T::* m_memfn;
template<typename... Args>
void operator()(Args&&... args)
{ (m_this->*m_memfn)(std::forward<Args>(args)...); }
};
This isn't a fully generic utility (for example, it doesn't do anything with the return type of the member function, always returning void, but that is OK for your requirements, where both the std::function objects return void anyway), but it solves the problem of associating an arbitrary member function with a this pointer, and then calling it with some set of arguments.
Although the SimpleBind object appears to accept any arguments, in fact you'll get a nasty compiler error if you try to call it with arguments that aren't compatible with the member function it binds. Again, that's not a problem, because the std::function objects you store it in will ensure that only the right type of arguments are ever passed to it.