I have the following code:
class A{
public:
A() {}
void foo(const B& b) {
int a = b.a();
}
};
template<class T, typename ... ARGS>
std::function<void()> * invoke(T *t, void(T::* fn)(ARGS...), ARGS... args) {
//Create a new std::function on the heap to be executed later
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
return f;
}
int main(void) {
A myA;
B myB(5, 6, 7);
std::function<void()> *fn = invoke(&myA, &A::foo, myB);
}
The purpose of which is to be able to create a generic std::function from a member function pointer (and object pointer) on the heap for later execution.
My problem is, the compiler doesn't seem to be able to figure out how to expand the invoke template correctly and I get the following error:
template argument deduction/substitution failed:
inconsistent parameter pack deduction with 'const B&' and 'B'
I would like the semantics for my invoke() function to stay the same (i.e. object *, member function *, arguments...).
Is there a way to maintain these semantics while still allowing the compiler to figure out the correct template deduction?
Thanks!
EDIT
I can get it to work if I do the following:
template<class T, typename F, typename ... ARGS>
std::function<void()> * invoke(T *t, F const &fn, ARGS... args) {
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
return f;
}
However, this does not fully meet my needs because two copies are made of the argument instead of one. I would like only a single copy to occur when I create the new std::function allocated on the heap. The arguments should still be passed in by reference to math the signature of the member function.
Your invoke function is already known as std::bind and if you want to ensure that parameters to foo are not copied, then combine bind with cref:
#include <functional>
struct B {
int a() const { return 1; }
};
struct A {
void foo(const B& b) {
int a = b.a();
}
};
int main(void) {
A myA;
B myB;
auto* fn = new std::function<void()>(std::bind(&A::foo, &myA, std::cref(myB)));
}
First of all, you should really be using std::forward when dealing with parameter packs. Right now you are taking all parameters as values.
Secondly, compilation fails because type deduction conflicts between void(T::* fn)(ARGS...) and ARGS... args. The compiler will get confused about whether to take types from args or from your function. For example, A::foo takes a const B& but you are giving it a value type B as well, which leads to a conflict. So you actually need two separate parameter packs to avoid this.
#include <functional>
#include <utility>
// you didn't provide a definition of B, so this is what I had to come up with
struct B {
int x;
int y;
int z;
int a() const {
return x;
}
};
class A{
public:
A() {}
void foo(const B& b) {
int a = b.a();
}
};
// you don't actually need to create a function on the heap
template<class T, typename ... FARGS, typename ...ARGS>
std::function<void()> invoke(T *t, void(T::* fn)(FARGS...), ARGS &&... args) {
return [&, t]() { (t->*fn)( std::forward<ARGS>(args)... ); };
}
int main(void) {
A myA;
B myB{5, 6, 7};
std::function<void()> fn = invoke(&myA, &A::foo, myB);
}
Alternatively, you could also use std::bind which does exactly what you are trying to accomplish:
std::function<void()> fn = std::bind(&A::foo, &myA, myB);
Arguments of invoke and arguments of your member function are not the same. One gets a const reference, the other does not. This needs to be reflected in the types.
template<class T,
typename ... ARGS,
typename ... ARGS2> // <---- !!
std::function<void()> * invoke(T *t,
void(T::* fn)(ARGS2...) // <---- !!
ARGS&&... args) { // you do want perfect forwarding
I figured out a solution that works for me based upon everyone's answers and comments.
In my application, I need to ensure that one, and only one, copy of the arguments to the function being invoked is made because that function will be executed within a different thread at a different time and the original data might not be available (e.g. a temporary variable on the stack). I had modified my code, as suggested, to use perfect forwarding within my template functions. This helped cut down on unnecessary copies significantly, however, I was still getting an extra copy within the lambda in my invoke function. It turns out, I had written the move constructor of my data type incorrectly so, when the lambda created a temporary copy of the data, it had to be copied twice.
Here are snippets of my working code (with my example datatype B):
class B {
public:
B() : _a(1), _b(2), _c(3) {
}
B(int a, int b, int c) : _a(a), _b(b), _c(c) {
}
//Copy Constructor
B(const B &v) : _a(v._a), _b(v._b), _c(v._c) {
copyCount++;
}
//Move Constructor
B(const B&& rhs): _a(rhs._a), _b(rhs._b), _c(rhs._c)
{
}
B& operator=(const B &v) {
this->_a = v._a;
this->_b = v._b;
this->_c = v._c;
copyCount++;
return *this;
}
~B() {
}
inline int a() const { return _a; }
static int copyCount;
private:
int _a;
int _b;
int _c;
};
template<class T, typename ... ARGSF, typename ... ARGS>
inline static void invoke(T *t, void(T::*fn)(ARGSF...), ARGS&&... args) {
std::function<void()> *f = new std::function<void()>([=]() { (t->*fn)( args... ); });
//Queue in the parent thread
if (!t->_parentThread->queueInvokable(f)) delete f;
}
template<class T>
inline ConnectionHandle connect(T* t, void(T::* fn)(ARGS...)) {
const auto lambda = [=](ARGS&&... args) { T::invoke(t, fn, args...); };
return connect(lambda);
}
I have also shown here another function, connect, which saves a lambda to be called later that in turns calls the invoke function.
With all this, only a single copy of the datatype is made as long as
a) The argument to the member function (fn) is of a reference type
b) The datatype has a working move constructor
Related
see example below live : https://onlinegdb.com/Hkg6iQ3ZNI
#include <iostream>
#include <utility>
#include <type_traits>
class A
{
public:
A(int v=-10):v_(v){}
void print()
{
std::cout << "called A: " << v_ << std::endl;
}
private:
int v_;
};
void f(int v)
{
std::cout << "called f: " << v << std::endl;
}
template<typename T,typename ... Args>
void run(A&& a,
T&& t,
Args&& ... args)
{
a.print();
t(std::forward<Args>(args)...);
}
template<typename T,typename ... Args>
void run(T&& t,
Args&& ... args)
{
run(A(),
std::forward<T>(t),
std::forward<Args>(args)...);
}
int main()
{
int v_function=1;
int v_a = 2;
run(f,v_function);
return 0;
}
The code above compiles, runs and print (as expected):
called A: -10
called f: 1
but if the main function is modified to:
int main()
{
int v_function=1;
int v_a = 2;
run(f,v_function);
// !! added lines !!
A a(v_a);
run(a,f,v_function);
return 0;
}
then compilation fails with error:
main.cpp:30:6: error: no match for call to ‘(A) (void (&)(int), int&)’
t(std::forward(args)...);
~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
which seems to indicate that even when an instance of A is passed as first argument, the overload function
void(*)(T&&,Args&&...)
is called, and not
void(*)(A&&,T&&,Args&&...)
With
template<typename T,typename ... Args>
void run(A&& a,
T&& t,
Args&& ... args)
a is not a forwarding reference, but an rvalue reference. That means when you do run(a,f,v_function); that function will not be selected because a is an lvalue and those can't be bound to rvalue references. There are two quick ways to fix this. First, use std::move on a like
run(std::move(a),f,v_function);
but this isn't great. a isn't actually moved in the function so you are kind of violating the principle of least surprise.
The second option is to make A in the function a template type so it becomes a forwarding reference and then you can constrain it to be of type A like
template<typename A_, typename T,typename ... Args, std::enable_if_t<std::is_same_v<std::decay_t<A_>, A>, bool> = true>
void run(A_&& a,
T&& t,
Args&& ... args)
{
a.print();
t(std::forward<Args>(args)...);
}
Your code works, if you are calling run with an rvalue.
Playable example here.
As NathanOliver already sad: void run(A&& a, T&& t, Args&& ... args) expects an rvalue reference.
Basic idea of an rvalue reference: You are passing an rvalue to a function (e.g. a string literal). That value will be copied to the function. This work is unnecessary. Instead, you are just "moving" the reference to that value, so that it is "owned" by a different part of your program. Move constructors are a good starting point for understanding this problem.
I am working in a memory constrained embedded environment where malloc/free new/delete are not advisable, and I'm trying to use the std::function pattern to register callbacks. I do not have access to any of the STL methods in my target code so I'm in the unfortunate situation of having to replicate some of the STL functionality myself. Function pointers are not an option for me due to the necessity for callers to have captures.
For instance, I wish to declare a class Mailbox where an onChange event can be registered
class Mailbox {
std::function<void(int,int)> onChange;
};
That way, callers can register a lambda onChange handler that could capture this or other variables that matter for handling the event.
Since this is part of an API, I want to give the users of Mailbox maximim flexibility to either provide a function pointer, a lambda or a functor.
I have managed to find a great implementation of a std::function that appears to be exceptionally low-overhead and has exactly what I need except that it involves dynamic memory.
If you look at the following code, dynamic memory is used in exactly one place, and it appears fully scoped to the object being templated, suggesting to me that its size ought to be known at compile-time.
Can anyone help me understand how to refactor this implementation so that it is fully static and removes the use of new/malloc? I'm having trouble understanding why the size of CallableT wouldn't be calculable at compile-time.
Code below (not for the faint of heart). Note, it uses make_unique / unique_ptr but those can easily be substituted with new and * and I have tested that use case successfully.
#include <iostream>
#include <memory>
#include <cassert>
using namespace std;
template <typename T>
class naive_function;
template <typename ReturnValue, typename... Args>
class naive_function<ReturnValue(Args...)> {
public:
template <typename T>
naive_function& operator=(T t) {
callable_ = std::make_unique<CallableT<T>>(t);
return *this;
}
ReturnValue operator()(Args... args) const {
assert(callable_);
return callable_->Invoke(args...);
}
private:
class ICallable {
public:
virtual ~ICallable() = default;
virtual ReturnValue Invoke(Args...) = 0;
};
template <typename T>
class CallableT : public ICallable {
public:
CallableT(const T& t)
: t_(t) {
}
~CallableT() override = default;
ReturnValue Invoke(Args... args) override {
return t_(args...);
}
private:
T t_;
};
std::unique_ptr<ICallable> callable_;
};
void func() {
cout << "func" << endl;
}
struct functor {
void operator()() {
cout << "functor" << endl;
}
};
int main() {
naive_function<void()> f;
f = func;
f();
f = functor();
f();
f = []() { cout << "lambda" << endl; };
f();
}
Edit: added clarification on STL
The name for what you're looking for is "in-place function". At least one very good implementation exists today:
sg14::inplace_function<R(A...), Size, Align>
There is also tj::inplace_any<Size, Align>, if you need/want the semantics of any.
Let me preface this answer by saying that storing a general callable faces an interesting choice in terms of memory management. Yes, we can deduce the size of any callable at compile time but we can not store any callable into the same object without memory management. That's because our own object needs to have size independently of the callables its supposed to store but those can be arbitrarily big.
To put this reasoning into one sentence: The layout of our class (and its interface) needs to be compiled without knowledge about all of the callers.
This leaves us with essentially 3 choices
We embrace memory management. We dynamically copy the callable and properly manage that memory through means of unique pointer (std or boost), or through custom calls to new and delete. This is what the original code you found does and is also done by std::function.
We only allow certain callables. We create some custom storage inside our object to hold some forms of callables. This storage has a pre-determined size and we reject any callable given that can not adhere to this requirement (e.g. by a static_assert). Note that this does not necessarily restrict the set of possible callers. Instead, any user of the interface could set up a proxy-class holding merely a pointer but forwarding the call operator. We could even offer such a proxy class ourselves as part of the library. But this does nothing more than shifting the point of allocation from inside the function implementation to outside. It's still worth a try, and #radosław-cybulski comes closest to this in his answer.
We don't do memory management. We could design our interface in a way that it deliberately refuses to take ownership of the callable given to it. This way, we don't need to to memory management and this part is completely up to our caller. This is what I will give code for below. It is not a drop-in replacement for std::function but the only way I see to have a generic, allocation-free, copiable type for the purpose you inteded it.
And here is the code for possibility 3, completely without allocation and fully self-contained (does not need any library import)
template<typename>
class FunctionReference;
namespace detail {
template<typename T>
static T& forward(T& t) { return t; }
template<typename T>
static T&& forward(T&& t) { return static_cast<T&&>(t); }
template<typename C, typename R, typename... Args>
constexpr auto get_call(R (C::* o)(Args...)) // We take the argument for sfinae
-> typename FunctionReference<R(Args...)>::ptr_t {
return [](void* t, Args... args) { return (static_cast<C*>(t)->operator())(forward<Args>(args)...); };
}
template<typename C, typename R, typename... Args>
constexpr auto get_call(R (C::* o)(Args...) const) // We take the argument for sfinae
-> typename FunctionReference<R(Args...)>::ptr_t {
return [](void* t, Args... args) { return (static_cast<const C*>(t)->operator())(forward<Args>(args)...); };
}
template<typename R, typename... Args>
constexpr auto expand_call(R (*)(Args...))
-> typename FunctionReference<R(Args...)>::ptr_t {
return [](void* t, Args... args) { return (static_cast<R (*)(Args...)>(t))(forward<Args>(args)...); };
}
}
template<typename R, typename... Args>
class FunctionReference<R(Args...)> {
public:
using signature_t = R(Args...);
using ptr_t = R(*)(void*, Args...);
private:
void* self;
ptr_t function;
public:
template<typename C>
FunctionReference(C* c) : // Pointer to embrace that we do not manage this object
self(c),
function(detail::get_call(&C::operator()))
{ }
using rawfn_ptr_t = R (*)(Args...);
FunctionReference(rawfn_ptr_t fnptr) :
self(fnptr),
function(detail::expand_call(fnptr))
{ }
R operator()(Args... args) {
return function(self, detail::forward<Args>(args)...);
}
};
For seeing how this then works in action, go to https://godbolt.org/g/6mKoca
Try this:
template <class A> class naive_function;
template <typename ReturnValue, typename... Args>
class naive_function<ReturnValue(Args...)> {
public:
naive_function() { }
template <typename T>
naive_function(T t) : set_(true) {
assert(sizeof(CallableT<T>) <= sizeof(callable_));
new (_get()) CallableT<T>(t);
}
template <typename T>
naive_function(T *ptr, ReturnValue(T::*t)(Args...)) : set_(true) {
assert(sizeof(CallableT<T>) <= sizeof(callable_));
new (_get()) CallableT<T>(ptr, t);
}
naive_function(const naive_function &c) : set_(c.set_) {
if (c.set_) c._get()->Copy(&callable_);
}
~naive_function() {
if (set_) _get()->~ICallable();
}
naive_function &operator = (const naive_function &c) {
if (this != &c) {
if (set_) _get()->~ICallable();
if (c.set_) {
set_ = true;
c._get()->Copy(&callable_);
}
else
set_ = false;
}
return *this;
}
ReturnValue operator()(Args... args) const {
return _get()->Invoke(args...);
}
ReturnValue operator()(Args... args) {
return _get()->Invoke(args...);
}
private:
class ICallable {
public:
virtual ~ICallable() = default;
virtual ReturnValue Invoke(Args...) = 0;
virtual void Copy(void *dst) const = 0;
};
ICallable *_get() {
return ((ICallable*)&callable_);
}
const ICallable *_get() const { return ((const ICallable*)&callable_); }
template <typename T>
class CallableT : public ICallable {
public:
CallableT(const T& t)
: t_(t) {
}
~CallableT() override = default;
ReturnValue Invoke(Args... args) override {
return t_(std::forward<ARGS>(args)...);
}
void Copy(void *dst) const override {
new (dst) CallableT(*this);
}
private:
T t_;
};
template <typename T>
class CallableT<ReturnValue(T::*)(Args...)> : public ICallable {
public:
CallableT(T *ptr, ReturnValue(T::*)(Args...))
: ptr_(ptr), t_(t) {
}
~CallableT() override = default;
ReturnValue Invoke(Args... args) override {
return (ptr_->*t_)(std::forward<ARGS>(args)...);
}
void Copy(void *dst) const override {
new (dst) CallableT(*this);
}
private:
T *ptr_;
ReturnValue(T::*t_)(Args...);
};
static constexpr size_t size() {
auto f = []()->void {};
return std::max(
sizeof(CallableT<void(*)()>),
std::max(
sizeof(CallableT<decltype(f)>),
sizeof(CallableT<void (CallableT<void(*)()>::*)()>)
)
);
};
typedef unsigned char callable_array[size()];
typename std::aligned_union<0, callable_array, CallableT<void(*)()>, CallableT<void (CallableT<void(*)()>::*)()>>::type callable_;
bool set_ = false;
};
Keep in mind, that sort of tricks tend to be slightly fragile.
In this case to avoid memory allocation i used unsigned char[] array of assumed max size - max of CallableT with pointer to function, pointer to member function and lambda object. Types of pointer to function and member function dont matter, as standard guarantees, that for all types those pointers will have the same size. Lambda should be pointer to object, but if for some reason isnt and it's size will change depending on lambda types, then you're out of luck.
First callable_ is initialized with placement new and correct CallableT type. Then, when you try to call, i use beginning of callable_ as pointer to ICallable. This all is standard safe.
Keep in mind, that you copy naive_function object, it's template argument T's copy operator is NOT called.
UPDATE: some improvements (at least try to force alignment) + addition of copying constructor / copy assignment.
My attempt to run the solution given Here, encountered with some issues. After fixing them, seems to work fine.
Will be happy for any review as I am not a c++ expert!
Issues and fixes:
error: lambda expression in an unevaluated operand.
removed the decltype. ( was not present in original code so I guess its safe(???)
using aligned_t = detail::aligned_union<0,
CallableT<void(*)()>,
//CallableT<decltype([]()->void {})>,
CallableT<void (CallableT<void(*)()>::*)()>
>;
Under C++11, errors in code block:
error: fields must have a constant size: 'variable length array in structure' extension will never be supported
error: 'aligned' attribute requires integer constant
error: constexpr variable 'alignment_value' must be initialized by a constant expression
(Note: this code is replacing std::aligned_union)
namespace detail {
template <size_t Len, class... Types>
struct aligned_union {
static constexpr size_t alignment_value = std::max({alignof(Types)...}); // ERROR HERE C++11
struct type {
alignas(alignment_value) char _s[std::max({Len, sizeof(Types)...})]; // ERROR HERE C++11
};
};
}
Used 'external' help from ETLCPP - which has support for embedded, file: largest.h.
Error block was replaced with :
#include"etl/largest.h"
template<typename ...Types>
using largest_t = typename etl::largest_type<Types...>::type;
namespace detail {
template <size_t Len, class... Types>
struct aligned_union {
static constexpr size_t alignment_value = etl::largest_alignment<Types...>::value; //std::max({alignof(Types)...});
struct type {
alignas(alignment_value) char _s[sizeof(largest_t<Types...>)]; //[std::max({Len, sizeof(Types)...})];
};
};
}
Looked redundant, removed:
//static constexpr size_t size() {
// auto f = []()->void {};
// return std::max(
// sizeof(CallableT<void(*)()>),
// std::max(
// sizeof(CallableT<decltype(f)>),
// sizeof(CallableT<void (CallableT<void(*)()>::*)()>)
// )
// );
//};
replaced std::forward with etl::forward file: utility.h
Had anew ,and delete errors : Undefined symbol operator delete
(void)*
So added ( I never allocate.. ):
// Define placement new if no new header is available
inline void* operator new(size_t, void* p) { return p; }
inline void* operator new[](size_t, void* p) { return p; }
inline void operator delete(void*, void*) {}
inline void operator delete[](void*, void*) {}
inline void operator delete[](void*) {}
Still getting a warning thought (???):
: warning: replacement function 'operator delete' cannot be declared 'inline' [-Winline-new-delete]
inline void operator delete(void* ) {}
Linker error:
Error: L6218E: Undefined symbol __cxa_pure_virtual ).
Probably because of virtual distractor : (ref)
virtual ~ICallable() = default;
Had to add this : ( any other solution ???)
extern "C" void __cxa_pure_virtual() { while (1); }
Is there a way to convert a std::function that has to a T* argument to similar one that has a void* argument? It seems possible since the calls should be compatible at the binary level.
As an example, how can I make this work without turning Producer into a template or losing type safety?
#include <functional>
struct Producer {
// produces an int from a callable and an address
template<class Src>
Producer(Src& src, std::function<int (Src*)> f)
: arg_(&src),
f_(f)
{}
int operator()() {
return f_(arg_);
}
// type erasure through void* but still type safe since ctor
// checks that *arg_ and f are consistent
void* arg_;
std::function<int (void*)> f_;
};
int func1(char* c) {
return *c;
}
int func2(int* i) {
return *i;
}
int try_it() {
char c = 'a';
char i = 5;
// we want to make these work
Producer p1(c, func1);
Producer p2(i, func2);
// but we still want this to fail to compile
// Producer p3(c, func2);
return p1() + p2();
}
Edit: Solution with definite UDB but correct behavior. :-/
You could just not take the argument as a std::function, and then you won't have to worry about it. Take any type F, as long as you can call it with a Src* and it returns something that can be converted to int. Start with a SFINAE-friendly std::result_of_t (borrowed shamelessly from Yakk):
template<class F, class...Args>
using invoke_result = decltype( std::declval<F>()(std::declval<Args>()...));
And use that to SFINAE your constructor:
template<class Src,
class F,
class = std::enable_if_t<
std::is_convertible<invoke_result<F, Src*>, int>::value
>>
Producer(Src& src, F f)
: arg_(&src)
, f_([f = std::move(f)](void* arg){
return f(static_cast<Src*>(arg));
})
{ }
No UB there. This also correctly rejects your p3 case. Furthermore, you don't even need arg_ unless you're using it for some other reason. Store f_ as:
std::function<int ()> f_;
and stick the Src in it:
template<class Src,
class F,
class = std::enable_if_t<
std::is_convertible<invoke_result<F, Src*>, int>::value
>>
Producer(Src& src, F f)
: f_([&src, f = std::move(f)](){
return f(&src);
})
{ }
I'm creating a job queue. The job will be created in thread A, then the job will be sent to thread B and thread B will do the job. After the job has been done, the job will be sent back to thread A.
#include <functional>
#include <iostream>
#include <memory>
using namespace std;
template<typename T, typename... Args>
class Job
{
public:
Job(std::weak_ptr<T> &&wp, std::function<void(const Args&...)> &&cb)
: _cb(std::move(cb)),
_cbWithArgs(),
_owner(std::move(wp)) {}
public:
template<typename... RfTs>
void bind(RfTs&&... args)
{
// bind will copy args for three times.
_cbWithArgs = std::bind(_cb, std::forward<RfTs>(args)...);
}
void fire()
{
auto sp = _owner.lock();
if (sp)
{
_cbWithArgs();
}
}
private:
std::function<void(const Args& ...)> _cb;
std::function<void()> _cbWithArgs;
std::weak_ptr<T> _owner;
};
struct Args
{
Args() = default;
Args(const Args &args)
{
cout << "Copied" << endl;
}
};
struct Foo
{
void show(const Args &)
{
cout << "Foo" << endl;
}
};
int main()
{
using namespace std::placeholders;
shared_ptr<Foo> sf (new Foo());
Args args;
// Let's say here thread A created the job.
Job<Foo, Args> job(sf, std::bind(&Foo::show, sf.get(), _1));
// Here thread B has finished the job and bind the result to the
// job.
job.bind(args);
// Here, thread A will check the result.
job.fire();
}
The above codes compiles and works. But it gives the following results (g++ 4.8.4 & clang have the same results):
Copied
Copied
Copied
Foo
There are three copies! Not acceptable, I don't know where I did wrong. Why three copies? I googled and find a method from here: https://stackoverflow.com/a/16868401, it only copys params for one time. But it has to initialize the bind function in constructor.
Thanks, Piotr Skotnicki. Unfortunately, I don't have a C++14 compiler. So, let's make the Args movable:
struct Args
{
Args() = default;
Args(const Args &args)
{
cout << "Copied" << endl;
}
Args(Args &&) = default;
Args& operator=(Args &&) = default;
};
Now, it copys just one time :)
Finally, I adopted the codes from this thread https://stackoverflow.com/a/16868151/5459549. The template gen_seq is true ART, I must say.
The first copy is made by the std::bind itself:
std::bind(_cb, std::forward<RfTs>(args)...)
as it needs to store decayed-copies of its arguments.
The other two copies are made by the assignment to _cbWithArgs which is of type std::function (§ 20.8.11.2.1 [func.wrap.func.con]):
template<class F> function& operator=(F&& f);
18 Effects: function(std::forward<F>(f)).swap(*this);
where:
template<class F> function(F f);
9 [...] *this targets a copy of f initialized with std::move(f).
That is, one copy for the parameter of a constructor, and another one to store argument f.
Since Args is a non-movable type, an attempt to move from a result of a call to std::bind falls back to a copy.
It doesn't seem reasonable in your code to store the results of job execution in a std::function. Instead, you can store those values in a tuple:
template <typename T, typename... Args>
class Job
{
public:
Job(std::weak_ptr<T> wp, std::function<void(const Args&...)> &&cb)
: _cb(std::move(cb)),
_owner(wp) {}
public:
template<typename... RfTs>
void bind(RfTs&&... args)
{
_args = std::forward_as_tuple(std::forward<RfTs>(args)...);
}
void fire()
{
auto sp = _owner.lock();
if (sp)
{
apply(std::index_sequence_for<Args...>{});
}
}
private:
template <std::size_t... Is>
void apply(std::index_sequence<Is...>)
{
_cb(std::get<Is>(_args)...);
}
std::function<void(const Args&...)> _cb;
std::weak_ptr<T> _owner;
std::tuple<Args...> _args;
};
DEMO
I am trying to write a template class named Binder that bind functions and parameters as whole, distinguished by the returning type of the binded function, this is my approach:
template <typename return_type>
class Binder
{
public:
virtual return_type call() {}
};
invoking call will invoke some pre-binded functions with parameters, and return the result.
I want some template classes inherited from Binder that do the real binding job.
below is a one-parameter-function binding class:
template<typename func_t, typename param0_t>
class Binder_1 : public Binder< ***return_type*** >
// HOW TO DETERMINE THE RETURN TYPE OF func_t?
// decltype(func(param0)) is available when writing call(),
// but at this point, I can't use the variables...
{
public:
const func_t &func;
const param0_t ¶m0;
Binder_1 (const func_t &func, const param0_t ¶m0)
: func(func), param0(param0) {}
decltype(func(param0)) call()
{
return func(param0);
}
}
// Binder_2, Binder_3, ....
This is what I want to achieve:
template<typename func_t, typename param0_t>
Binder_1<func_t, param0_t> bind(const func_t &func, const param0_t ¶m0)
{
reurn Binder_1<func_t, param0_t>(func, param0);
}
// ... `bind` for 2, 3, 4, .... number of paramters
int func(int t) { return t; }
double foo2(double a, double b) { return a > b ? a : b; }
double foo1(double a) { return a; }
int main()
{
Binder<int> int_binder = bind(func, 1);
int result = int_binder.call(); // this actually calls func(1);
Binder<double> double_binder = bind(foo2, 1.0, 2.0);
double tmp = double_binder.call(); // calls foo2(1.0, 2.0);
double_binder = bind(foo1, 1.0);
tmp = double_binder.call(); // calls foo1(1.0)
}
can bind function in boost library be adapted to achieve this functionality?
similar solutions are welcome too!
Introducing std::declval<T>().
This is a dummy function declared as:
template <typename T>
typename std::add_rvalue_reference<T>::type declval();
// This means it returns T&& if T is no a reference
// or T& if T is already a reference
and never actually defined.
It is therefore only to be used within unevaluated contexts such as sizeof or... decltype!
With this, you get:
template<typename func_t, typename param0_t>
class Binder_1: public Binder<decltype(std::declval<func_t>()(std::declval<param0_t>())>
It is a bit verbose, but hey! It works :)
You might be able to use result_of.