bind binded function as argument - c++

I have a class foo with a method bar which takes something callable (function-pointer/ functor). this callable something should be passed to another method doit as an binded element with a third method bar_cb method.
#include <functional>
#include <iostream>
class foo {
public:
template<typename T>
void bar(T&& t) {
std::cout << "bar\n";
doit(std::bind(&foo::template bar_cb<T>, this, std::forward<T>(t)));
}
template<typename T>
void doit(T&& t) {
std::cout << "doit\n";
t();
}
template<typename T>
void bar_cb(T&& t) {
std::cout << "bar_cb\n";
t();
}
};
void lala() {
std::cout << "lala\n";
}
class functor {
public:
void operator()() {
std::cout << "functor::operator()\n";
}
};
int main() {
foo f;
functor fn;
f.bar(fn);
f.bar(std::bind(lala)); // error
return 0;
}
This works fine for functors but not for binded functions as argument for foo::bar (lala in my example). Is it possible to pass an unknowable type to a method and bind it in this method as an argument to another (and if so how)?
I know I could wrap a functor (std::function for example) around the function but since I can call an unknowable type I think there is a way to also bind it (I think I'm just missing something simple).
Here a link to an example.

The primary problem is that your bar_cb(T&&) doesn't deduce the template argument because the template argument is actually specified when using &foo::template bar_cb<X> with some template argument X. The bind() expression will, however, copy the bound function, i.e., it may or may not have the type which would be deduced. Also, std::bind() will not pass bind()-expression through but will rather call them!
The easiest work around is to not use std::bind() to bind the function but rather to use a lambda function:
template<typename T>
void bar(T&& t) {
std::cout << "bar\n";
doit([=](){ this->bar_cb(t); });
}
Doing so let's the compiler deduce the correction argument type for bar_cb() (with C++14 you may want to use the capture [this,t = std::forward<T>(t)] although your bar_cb() still won't see an rvalue).
To pass an already bind()-expression through another bind()-expression, without having bind() consider the inner bind()-expression a bind()-expression you need to make it look as if it is not a bind()-expression. You could do so with a thin function wrapper:
template <typename Fun>
class unbinder {
Fun fun;
public:
template <typename F>
unbinder(F&& fun): fun(std::forward<F>(fun)) {}
template <typename... Args>
auto operator()(Args&&... args) const
-> decltype(fun(std::forward<Args>(args)...)) {
return fun(std::forward<Args>(args)...);
}
};
template <typename Fun>
auto unbind(Fun&& fun)
-> unbinder<Fun> {
return unbinder<Fun>(std::forward<Fun>(fun));
}
Since the function stored in the bind() expression will be passed by lvalue, you'll need a different declaration for your bar_cb(), however:
template<typename T>
void bar_cb(T& t) {
...
}
With that, you can register the bind()-expression using
f.bar(unbind(std::bind(lala)));
If you want to use f.bar(std::bind(lala)) you'll need a conditional definition of bar(): if it receives a bind()-expression it needs to automatically hide the fact that it is a bind()-expression by applying unbind() or something similar:
template<typename T>
typename std::enable_if<!std::is_bind_expression<typename std::decay<T>::type>::value>::type
bar(T&& t) {
std::cout << "bar (non-bind)\n";
doit(std::bind(&foo::template bar_cb<T>, this, std::forward<T>(t)));
}
template<typename T>
typename std::enable_if<std::is_bind_expression<typename std::decay<T>::type>::value>::type
bar(T&& t) {
std::cout << "bar (bind)\n";
doit(std::bind(&foo::template bar_cb<unbinder<T>>, this, unbind(std::forward<T>(t))));
}

Related

How to call one of two C++ template functions with condition

There is a class with two member template functions: fun1(), fun2().
I want to call one of the functions decided by a const bool class member : willCallFun1.
There is a solution like fun() in the code, but the if-else branch will check every times while fun() is called. I want avoid branch.
Since when the Foo object is constructed, const bool willCallFun1 has been set, so which function of fun1 and fun2 will be used can already be determined.
class Foo {
public:
const bool willCallFun1;
Foo(bool b) : willCallFun1{b}{
}
template<typename T>
void fun1(T t) {
std::cout << "fun1 "<< t << "\n";
}
template<typename T>
void fun2(T t) {
std::cout << "fun2 "<< t << "\n";
}
template<typename T>
void fun(T t){
if (willCallFun1) fun1<T>(t);
else fun2<T>(t);
}
};
int main() {
Foo f1{true};
f1.fun(123);
f1.fun("hi");
Foo f2{false};
f2.fun(456);
f2.fun("hello");
}
If fun1, fun2 are non-template functions, I can add a new member in the class, such as a function pointer or std::function to bind one of them at the constructor.
I can't find a way to do it when they are template functions.
I would be very grateful if someone could answer.
Since you can't make willCallFun1 into a template parameter and use if constexpr to decide which function to call, you could create an array of member function pointers and lookup the correct one using willCallFun1 as an index variable.
template <typename T>
void fun(T t) {
using fun_sig = void(Foo::*)(T);
static constexpr fun_sig funs[] = {&Foo::fun2<T>, &Foo::fun1<T>};
(this->*funs[willCallFun1])(t);
}
Demo
I'd test if this is actually faster than having a simple if though. I've done this before with disappointing results.

Should std::forward be used when the forwarding reference is not passed to another function but a member function is called on it?

Examples of std::forward I've seen use it when passing the argument to another function, such as this common one from cppreference:
template<class T>
void wrapper(T&& arg)
{
// arg is always lvalue
foo(std::forward<T>(arg)); // Forward as lvalue or as rvalue, depending on T
}
It also has a more complicated case:
if a wrapper does not just forward its argument, but calls a member function on the argument, and forwards its result
// transforming wrapper
template<class T>
void wrapper(T&& arg)
{
foo(forward<decltype(forward<T>(arg).get())>(forward<T>(arg).get()));
}
But what happens when I only call the member function without passing the result to another function?
template<class T>
auto wrapper(T&& arg)
{
return std::forward<T>(arg).get();
}
In this case, is it useful to call std::forward or is arg.get() equivalent? Or does it depend on T?
EDIT: I've found a case where they are not equivalent;
#include <iostream>
struct A {
int get() & {
return 0;
}
int get() && {
return 1;
}
};
template<class T>
auto wrapper_fwd(T&& arg)
{
return std::forward<T>(arg).get();
}
template<class T>
auto wrapper_no(T&& arg)
{
return arg.get();
}
wrapper_fwd(A{}) returns 1 and wrapper_no(A{}) returns 0.
But in my actual use-case arg is a lambda and operator() isn't overloaded like this. Is there still a possible difference?

Pass on already deduced type as parameter type for a callback

So I'm trying to do something like the following
int x = 123;
myFunction(x, [](auto y) {
std::cout << y;
});
And myFunction is defined as
template <typename T>
void myFunction(T val, void(*callback)(T)) {
callback(val);
}
When trying to compile the code clang gives me the error could not match 'void (*)(T)' against '(lambda at ...)'. I've figured out this is because you can't deduce the type from a lambda.
So that's actually alright because what I actually want is the parameter type of the callback to be the type deduced from the val parameter of myFunction.
So my question put simply is, is it possible somehow to exclude the callback parameter from being deduced and instead just use the deduced type of val?
is it possible somehow to exclude the callback parameter from being deduced and instead just use the deduced type of val?
Sure.
If you define something like this
template <typename T>
struct self
{ using type = T; };
you can define myFunction() as follows
template <typename T>
void myFunction(T val, void(*callback)(typename self<T>::type)) {
callback(val);
}
Now T is deduced from val and used for callback.
Starting from C++20, you can avoid the self definition and use the newly introduced std::type_identity_t
template <typename T>
void myFunction(T val, void(*callback)(std::type_identity_t<T>)) {
callback(val);
}
One option (and there are a few) is to pass the callable item as a template argument and allow templating to take care of some of the details for you - eg:
template <typename T, typename FUNCTOR>
void myFunction(T val, FUNCTOR callback) {
callback(val);
}
// Note: could be a const ref:
//void myFunction(T val, const FUNCTOR &callback) {
int main()
{
int some_int{1};
myFunction(some_int, [](auto y){ std::cout << y << std::endl; });
float some_float{1.1f};
myFunction(some_float, [](auto y){ std::cout << y << std::endl; });
return 0;
}
Full example here (following your code - but this could be further tidied): https://godbolt.org/z/qr7T5GPd6

C++ function template type deduction from function parameter fails

I am writing a small signal-slot implementation to learn more about the usage of templates.
I ran into a problem when I want the compiler to deduce the type of a template parameter of a function template from the function's parameter.
Before asking here, I watched several videos (i.e Arthur O'Dwyers 'Template Normal Programming') and read through several articles but i just can't figure out why it isn't working.
I have built a minimal working example:
#include <iostream>
#include <vector>
//Base Event class for passed around events
struct Event
{
virtual ~Event() = default;
Event(int keyCode) : keyCode_{keyCode} {}
int keyCode_;
};
//Event to be passed around
struct KeyPressedEvent final : Event
{
~KeyPressedEvent() { std::cout << "KeyPressedEvent dtor " << std::endl; }
KeyPressedEvent(int keyCode) : Event(keyCode) { std::cout << "KeyPressedEvent param. ctor " << std::endl; }
};
//Class which holds a callback(slot)
struct Window
{
Window(int y) { std::cout << "window param ctor\n"; }
void kpEventHandler(KeyPressedEvent& kpEvent)
{
std::cout << "non-static member staticKpEventHandler() : " << kpEvent.keyCode_ << "\n";
}
};
//Signal which holds connections to callbacks
template<typename Signature> struct Signal;
template<typename Ret, typename ArgType>
struct Signal<Ret(ArgType)>
{
struct Connection
{
using Instance_t = void*;
using Call_t = void(*)(void*, ArgType&&);
//template <typename Type>
Connection(void* instance, Call_t call) :
instance_{instance}, call_{std::move(call)} {}
bool operator==(const Connection& other) const noexcept
{
bool cmpInstance = instance_ == other.instance_;
bool cmpCall = call_ == other.call_;
return cmpInstance && cmpCall;
}
Instance_t instance_;
Call_t call_;
};
std::vector<Connection> connections_;
template<typename C, Ret(C::* func)(ArgType)>
static void call(void* instancePtr, ArgType&& arg)
{
C* instance = static_cast<C*>(instancePtr);
if (instance != nullptr)
{
(instance->*func)(std::forward<ArgType>(arg));
}
}
template<typename C, Ret(C::* func)(ArgType)>
void connect(C& instance)
{
connections_.emplace_back(&instance, std::move(&call<C, func>));
}
template<typename C, Ret(C::*func)(ArgType)>
void disconnect(C& instance)
{
Connection conn{&instance, &call<C, func>};
connections_.erase(std::remove(connections_.begin(), connections_.end(), conn), connections_.end());
}
};
//Test code
int main()
{
{
Window window{5};
Signal<void(KeyPressedEvent&)> signal;
signal.connect<&Window::kpEventHandler>(window); //Error C2974 'Signal<void (KeyPressedEvent &)>::connect': invalid template argument for 'C', type expected
signal.disconnect<&Window::kpEventHandler>(window); //Error C2974 'Signal<void (KeyPressedEvent &)>::disconnect': invalid template argument for 'C', type expected
}
std::cin.get();
return 0;
}
When I change the test code to the following, it obviously works.
signal.connect<Window, &Window::kpEventHandler>(window);
signal.disconnect<Window, &Window::kpEventHandler>(window);
I got it to work when I change Signal::connect() and Signal::disconnect() to the following implementation using an auto template parameter for the function pointer.
template<auto func, typename C>
void connect(C& instance)
{
static_assert(std::is_member_function_pointer<decltype(func)>::value,
"Signal::connect: func is not a pointer to a member function");
connections_.emplace_back(&instance, std::move(&call<C, func>));
}
template<auto func, typename C>
void disconnect(C& instance)
{
static_assert(std::is_member_function_pointer<decltype(func)>::value,
"Signal::disconnect: func is not a pointer to a member function");
Connection conn{&instance, &call<C, func>};
connections_.erase(std::remove(connections_.begin(), connections_.end(), conn), connections_.end());
}
signal.connect<&Window::kpEventHandler>(window); //works fine
signal.disconnect<&Window::kpEventHandler>(window); //works fine
When I change the order of the template parameters in this solution, I get an error as well:
template<typename C, auto func>
void disconnect(C& instance)
{
static_assert(std::is_member_function_pointer<decltype(func)>::value,
"Signal::disconnect: func is not a pointer to a member function");
Connection conn{&instance, &call<C, func>};
connections_.erase(std::remove(connections_.begin(), connections_.end(), conn), connections_.end());
}
signal.disconnect<&Window::kpEventHandler>(window); //Error C2974 'Signal<void (KeyPressedEvent &)>::disconnect': invalid template argument for 'C', type expected
So my questions are:
Why can't the compiler deduce the types of the parameters in my original version?
Why does it work when I change the Signal::connect() and Signal::disconnect() implementation to use 'auto func'?
Why do I get a compiler error when I change the order of the template parameters in the 'auto func' solution?
Thank you for helping me!
All your questions have the same answer: the order of template parameter is important and, to explicate a template parameter, you have to explicate all template parameters before it.
When you write
template<typename C, Ret(C::* func)(ArgType)>
void connect(C& instance)
or
template<typename C, auto func>
void disconnect(C& instance)
the first template parameter can be deduced from instance, the second one can't be deduced so has to be explicated.
But, this is the problem, if you have to explicate a template parameter, you must explicate all template parameter before it.
So
signal.connect<&Window::kpEventHandler>(window);
doesn't works because <&Window::kpEventHandler> explicate the first template parameter, that is C; and a &Window::kpEventHandler (that is a value) doesn't matches C, the first template argument (that has to be a type).
You have to explicate both template parameters, in the correct order, so
signal.connect<Window, &Window::kpEventHandler>(window);
signal.disconnect<Window, &Window::kpEventHandler>(window);
Different if you place the non-deducible template parameter in first position
template<auto func, typename C>
void connect(C& instance)
This way you can explicate the first template parameter (the func value) and let the compiler to deduce C from the argument instance.
So you can write
signal.connect<&Window::kpEventHandler>(window);
signal.disconnect<&Window::kpEventHandler>(window);
but works also if you explicate the second template parameter too
signal.connect<&Window::kpEventHandler, Windows>(window);
signal.disconnect<&Window::kpEventHandler, Windows>(window);

Perfect-forward non-T arguments while converting T-s

(This question follows from this answer)
I am trying to adapt a trampoline function that is currently just passing through a variable number of arguments.
I would like to have it convert any argument PyObject* pyob to Object{pyob}, but forward all other arguments through.
So (void* self, int, PyObject*, float) -> (int, Object, float)
In that example, the first self argument is stripped away. This always happens. Out of the remaining arguments, one of them is of type PyObject*, and hence requires conversion to Object.
Here is the function:
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R
call(void* s, Args... args)
{
std::cout << "trap:" << typeid(t).name() << std::endl;
try
{
return (get_base(s)->*t)(std::forward<Args>(args)...);
}
catch (...)
{
std::cout << "CAUGHT" << std::endl;
return std::is_integral<R>::value ? static_cast<R>(-42) : static_cast<R>(-3.14);
}
}
};
It appears not to be forwarding arguments. I think it is making a copy of each argument. I've tried:
call(void* s, Args&&... args)
But that just generates compiler errors.
The complete test case is here
How can I fix the function to perfect-forward all arguments apart from those of type PyObject*, which it should convert?
It appears not to be forwarding arguments
You can't perfectly-forward arguments of a function which is not a template, or which is invoked through a pointer to a function, like you do. Perfect-forwarding involves a template argument deduction, which doesn't take place when you invoke a function through a pointer - that pointer points to a concrete instantiation of a function template.
The std::forward<Args>(args) expression is there to possibly utilize a move-constructor to copy-initialize the parameters of the target function from those arguments of call that are passed by value (or by a hard-coded rvalue reference), or let them be bound by an rvalue reference - you won't need any more those instances, you are free to move-from them, saving at least one copy operation. (It could be as simple as static_cast<Args&&>(args)..., because it's just a reference collapsing).
I would like to have it convert any argument PyObject* pyob to Object{pyob}, but forward all other arguments through. How can I fix the function to perfect-forward all arguments apart from those of type PyObject*, which it should convert?
#include <utility>
template <typename T, typename U>
T&& forward_convert(U&& u)
{
return std::forward<T>(std::forward<U>(u));
}
template <typename T>
Object forward_convert(PyObject* a)
{
return Object{a};
}
// ...
return (get_base(s)->*t)(forward_convert<Args>(args)...);
To replace any occurrence of Object with PyObject* while creating the signature of call function, and only then conditionally forward or convert the arguments, you should do what follows:
template <typename T>
struct replace { using type = T; };
template <>
struct replace<Object> { using type = PyObject*; };
// you may probably want some more cv-ref specializations:
//template <>
//struct replace<Object&> { using type = PyObject*; };
template <typename T, T t>
struct trap;
template <typename R, typename... Args, R(Base::*t)(Args...)>
struct trap<R(Base::*)(Args...), t>
{
static R
call(void* s, typename replace<Args>::type... args)
{
try
{
return (get_base(s)->*t)(forward_convert<typename replace<Args>::type>(args)...);
}
catch (...)
{
return std::is_integral<R>::value ? static_cast<R>(-42) : static_cast<R>(-3.14);
}
}
};
DEMO
You have to change call to (Note that I introduce Ts in addition to Args).
template <typename ... Ts>
static R
call(void* s, Ts&&... args)
{
std::cout << "trap:" << typeid(t).name() << std::endl;
try
{
return (get_base(s)->*t)(std::forward<Ts>(args)...);
}
catch (...)
{
std::cout << "CAUGHT" << std::endl;
return std::is_integral<R>::value ? static_cast<R>(-42) : static_cast<R>(-3.14);
}
}