I currently have a system to "connect" signals to functions. This signal is a variadic template that has as template parameters the arguments of the functions it can connect to.
In the current implementation, I obviously cannot connect to functions whose arguments aren't exactly the same (or those that can be converted to) as the signal's parameters. Now, as I'm trying to mimic Qt's signal/slot/connect, I'd also like to connect a signal of N parameters to a slot of M<N parameters, which is perfectly well-defined (i.e. ignore the >M parameters of the signal and just pass the first M to the connected function). For an example of the code I have in its most simplistic form, see Coliru.
So the question is two-fold:
How do I make the connect call work for a function void g(int);?
How do I make the emit call work for a function void g(int);?
I'm guessing I'll have to make some "magic" parameter pack reducer for both the slot and its call function, but I can't see how it all should fit together so it's quite hard to actually start trying to code a solution. I'm OK with a C++17-only solution, if at least Clang/GCC and Visual Studio 2015 can compile it.
The code linked above for completeness:
#include <memory>
#include <vector>
template<typename... ArgTypes>
struct slot
{
virtual ~slot() = default;
virtual void call(ArgTypes...) const = 0;
};
template<typename Callable, typename... ArgTypes>
struct callable_slot : slot<ArgTypes...>
{
callable_slot(Callable callable) : callable(callable) {}
void call(ArgTypes... args) const override { callable(args...); }
Callable callable;
};
template<typename... ArgTypes>
struct signal
{
template<typename Callable>
void connect(Callable callable)
{
slots.emplace_back(std::make_unique<callable_slot<Callable, ArgTypes...>>(callable));
}
void emit(ArgTypes... args)
{
for(const auto& slot : slots)
{
slot->call(args...);
}
}
std::vector<std::unique_ptr<slot<ArgTypes...>>> slots;
};
void f(int, char) {}
int main()
{
signal<int, char> s;
s.connect(&f);
s.emit(42, 'c');
}
template<class...> struct voider { using type = void; };
template<class... Ts> using voidify = typename voider<Ts...>::type;
template<class C, class...Args>
using const_lvalue_call_t = decltype(std::declval<const C&>()(std::declval<Args>()...));
template<class T, std::size_t...Is>
auto pick_from_tuple_impl(T &&, std::index_sequence<Is...>)
-> std::tuple<std::tuple_element_t<Is, T>...>;
template<class Tuple, class = std::enable_if_t<(std::tuple_size<Tuple>::value > 0)>>
using drop_last = decltype(pick_from_tuple_impl(std::declval<Tuple>(),
std::make_index_sequence<std::tuple_size<Tuple>::value - 1>()));
template<class C, class ArgsTuple, class = void>
struct try_call
: try_call<C, drop_last<ArgsTuple>> {};
template<class C, class...Args>
struct try_call<C, std::tuple<Args...>, voidify<const_lvalue_call_t<C, Args...>>> {
template<class... Ts>
static void call(const C& c, Args&&... args, Ts&&... /* ignored */) {
c(std::forward<Args>(args)...);
}
};
Then in callable_slot:
void call(ArgTypes... args) const override {
using caller = try_call<Callable, std::tuple<ArgTypes...>>;
caller::call(callable, std::forward<ArgTypes>(args)...);
}
For member pointer support (this requires SFINAE-friendly std::result_of), change const_lvalue_call_t to
template<class C, class...Args>
using const_lvalue_call_t = std::result_of_t<const C&(Args&&...)>;
then change the actual call in try_call::call to
std::ref(c)(std::forward<Args>(args)...);
This is poor man's std::invoke for lvalue callables. If you have C++17, just use std::invoke directly (and use std::void_t instead of voidify, though I like the sound of the latter).
Not sure to understand what do you exactly want but... with std::tuple and std::make_index_sequence ...
First of all you need a type traits that give you the number of arguments of a function (or std::function)
template <typename>
struct numArgs;
template <typename R, typename ... Args>
struct numArgs<R(*)(Args...)>
: std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename R, typename ... Args>
struct numArgs<std::function<R(Args...)>>
: std::integral_constant<std::size_t, sizeof...(Args)>
{ };
Next you have to add a constexpr value in callable_slot to memorize the number of arguments in the Callable function
static constexpr std::size_t numA { numArgs<Callable>::value };
Then you have to modify the call() method to pack the arguments in a std::tuple<ArgTypes...> and call another method passing the tuple and an index sequence from 0 to numA
void call(ArgTypes... args) const override
{ callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }
Last you have to call, in CallI(), the callable() function with only the first numA elements of the tuple of arguments
template <std::size_t ... Is>
void callI (std::tuple<ArgTypes...> const & t,
std::index_sequence<Is...> const &) const
{ callable(std::get<Is>(t)...); }
The following is a full working example
#include <memory>
#include <vector>
#include <iostream>
#include <functional>
template <typename>
struct numArgs;
template <typename R, typename ... Args>
struct numArgs<R(*)(Args...)>
: std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename R, typename ... Args>
struct numArgs<std::function<R(Args...)>>
: std::integral_constant<std::size_t, sizeof...(Args)>
{ };
template <typename ... ArgTypes>
struct slot
{
virtual ~slot() = default;
virtual void call(ArgTypes...) const = 0;
};
template <typename Callable, typename ... ArgTypes>
struct callable_slot : slot<ArgTypes...>
{
static constexpr std::size_t numA { numArgs<Callable>::value };
callable_slot(Callable callable) : callable(callable)
{ }
template <std::size_t ... Is>
void callI (std::tuple<ArgTypes...> const & t,
std::index_sequence<Is...> const &) const
{ callable(std::get<Is>(t)...); }
void call(ArgTypes... args) const override
{ callI(std::make_tuple(args...), std::make_index_sequence<numA>{}); }
Callable callable;
};
template <typename ... ArgTypes>
struct signal
{
template <typename Callable>
void connect(Callable callable)
{
slots.emplace_back(
std::make_unique<callable_slot<Callable, ArgTypes...>>(callable));
}
void emit(ArgTypes... args)
{ for(const auto& slot : slots) slot->call(args...); }
std::vector<std::unique_ptr<slot<ArgTypes...>>> slots;
};
void f (int i, char c)
{ std::cout << "--- f(" << i << ", " << c << ")" << std::endl; }
void g (int i)
{ std::cout << "--- g(" << i << ")" << std::endl; }
struct foo
{
static void j (int i, char c)
{ std::cout << "--- j(" << i << ", " << c << ")" << std::endl; }
void k (int i)
{ std::cout << "--- k(" << i << ")" << std::endl; }
};
int main ()
{
std::function<void(int, char)> h { [](int i, char c)
{ std::cout << "--- h(" << i << ", " << c << ")" << std::endl; }
};
std::function<void(int)> i { [](int i)
{ std::cout << "--- i(" << i << ")" << std::endl; }
};
using std::placeholders::_1;
foo foo_obj{};
std::function<void(int)> k { std::bind(&foo::k, foo_obj, _1) };
signal<int, char> s;
s.connect(f);
s.connect(g);
s.connect(h);
s.connect(i);
s.connect(foo::j);
s.connect(k);
s.emit(42, 'c');
}
This example need C++14 because use std::make_index_sequence and std::index_sequence.
Substitute both of they and prepare a C++11 compliant solution isn't really difficult.
Related
Here an example:
#include <iostream>
template<typename T,
typename ... Args>
void print(T&& t, Args&& ... args)
{
// line where compilation fails when the A::run is called
if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)
{
t.display(std::forward<Args>(args)...);
}
else
{
std::cout << "not applicable !" << std::endl;
}
}
template<typename T>
class A
{
public:
A(T&& t):t_(t){}
template <typename... Args>
void run(Args&& ... args)
{
print<T,Args...>(t_,std::forward<Args>(args)...);
}
T t_;
};
template <typename T> A(T&) -> A<T&>;
template <typename T> A(T&&) -> A<T>;
class B
{
public:
B(int value):value_(value){}
void display(int a, int b)
{
std::cout << value_ << " "
<< a << " "
<< b << std::endl;
}
int value_;
};
int main()
{
int v1=10;
int v2=20;
B b1{1};
A a1{b1};
a1.t_.display(v1,v2);
A a2{B(2)};
a2.t_.display(v1,v2);
//a1.run(v1,v2); // (1)
//a2.run(v1,v2); // (2)
//a1.run(v1);
return 0;
}
The code above compiles and runs fine. But if the 3 last lines (calls to run()) are un-commented, the following compilation error occurs:
(1)
main.cpp:7:48: error: ‘display’ is not a member of ‘B&’
if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)
(2)
main.cpp:27:25: error: no matching function for call to ‘print(B&, int&, int&)’
print<T,Args...>(t_,std::forward<Args>(args)...);
Note :
template <typename T> A(T&) -> A<T&>;
template <typename T> A(T&&) -> A<T>;
explained here:
c++ copy (reference) constructor and move constructor of class cohabitation
Problem (1) and (2) are different problems.
Problem (1) come from the fact that in the following std::is_invocable_v
template<typename T,
typename ... Args>
void print(T&& t, Args&& ... args)
{
if constexpr (std::is_invocable_v<decltype(&T::display),T*,Args...>)
// no T but std::decay_t<T> ...............^...........^
instead of T (that can be a reference) you need the "decayed" type
I propose
template <typename T, typename ... Args>
void print (T && t, Args && ... args)
{
using U = std::decay_t<T>;
if constexpr ( std::is_invocable_v<decltype(&U::display), U*, Args...> )
t.display(std::forward<Args>(args)...);
else
std::cout << "not applicable !" << std::endl;
}
Problem (2) come from the fact that explicating the template parameters in the following print() call
template <typename... Args>
void run(Args&& ... args)
{
print<T,Args...>(t_,std::forward<Args>(args)...);
}
you impede the correct template deduction.
Suggestion: let template deduction, and perfect forwarding, works and call the function as follows
print(t_,std::forward<Args>(args)...);
Here some code with holes:
template<typename... Args>
class A
{
typedef function_type = void(*)(Args...);
public:
void set_args(Args&& ... args)
{
// something magic manages to encapsulate
// args in instance of A
}
void apply_args(function_type function)
{
// something magic manages to "retrieve"
// the encapsulated args
function(std::forward<Args>(args)...);
}
};
Would that be somehow possible ?
You can store your template arguments in class data member of std::tuple type and the use std::apply in order to apply stored arguments to provided function.
So, let's say you have an Action class like this:
template <typename... Args>
class Action {
std::tuple<Args...> args_;
public:
Action() = default;
Action(Args&&... args)
: args_(std::forward<Args>(args)...)
{}
void args(Args&&... args) {
args_ = std::make_tuple<Args...>(std::forward<Args>(args)...);
}
template <typename F>
void apply(F&& fun) {
std::apply(std::forward<F&&>(fun), args_);
}
};
where you set arguments through constructor Action action(1, 2, 3); or through separate function action.set(3, 2, 1);.
Then your main function can look like this:
int main() {
Action action(1, 2);
action.apply([](int a, int b) {
std::cout << "a + b = " << (a + b) << std::endl;
});
return 0;
}
Check live example
You can make use of std::tuple and std::apply
#include <iostream>
#include <tuple>
#include <functional>
#include <string>
template <typename... Ts>
class A
{
private:
std::function<void (Ts...)> f;
std::tuple<Ts...> args;
public:
template <typename F>
A(F&& func, Ts&&... args)
: f(std::forward<F>(func)),
args(std::make_tuple(std::forward<Ts>(args)...))
{}
void Invoke()
{
std::apply(f, args);
}
};
template <typename F, typename... Args>
A<Args...> Create(F&& f, Args&&... args)
{
return A<Args...>(std::forward<F>(f), std::forward<Args>(args)...);
}
int main()
{
auto helloWorld = Create([] (std::string a, std::string b) { std::cout << a << ", " << b; }, std::string("Hello"), std::string("World!"));
helloWorld.Invoke();
}
Consider the following code:
#include <utility>
#include <iostream>
struct S {
template<typename T, typename... A>
auto f(A&&... args) -> decltype(std::declval<T>().f(std::forward<A>(args)...), void()) {
std::cout << "has f(int)" << std::endl;
}
template<typename>
void f(...) {
std::cout << "has not f(int)" << std::endl;
}
};
struct T { void f(int) { } };
struct U { };
int main() {
S s;
s.f<T>(42); // -> has f(int)
s.f<U>(42); // -> has not f(int)
// oops
s.f<T>(); // -> has not f(int)
}
As shown in the example the third call to f works just fine, even if the number of arguments is wrong, for it's not wrong at all for the fallback function.
Is there a way to force the number of arguments when an ellipsis is involved that way?
I mean, can I check at compile time that the size of the arguments list is exactly 1, no matter if the main function or the fallback is chosen?
Good solutions are also the ones that only involves the first template function and result in hard-errors instead of soft-errors because of the size of the parameter pack.
Of course, it can be solved with several techniques without using variadic arguments. As an example: int/char dispatching on internal template methods; explicitly specify the arguments list; whatever...
The question is not about alternative approaches to do that, I already know them.
It's just to know if I'm missing something basic here or it's not possible and that's all.
If I understand correctly your issue, you may add a layer:
struct S {
private:
template<typename T, typename... A>
auto f_impl(A&&... args)
-> decltype(std::declval<T>().f(std::forward<A>(args)...), void()) {
std::cout << "has f(int)" << std::endl;
}
template<typename>
void f_impl(...) {
std::cout << "has not f(int)" << std::endl;
}
public:
template<typename T, typename A>
auto f(A&& args) { return f_impl<T>(std::forward<A>(arg)); }
};
With traits, you may do
template <typename T, typename ... Ts>
using f_t = decltype(std::declval<T>().f(std::declval<Ts>()...));
template <typename T, typename ... Ts>
using has_f = is_detected<f_t, T, Ts...>;
struct S {
template<typename T, typename... A>
std::enable_if_t<has_f<T, A&&...>::value && sizeof...(A) == 1> f(A&&... args)
{
std::cout << "has f(int)" << std::endl;
}
template<typename T, typename... A>
std::enable_if_t<!has_f<T, A&&...>::value && sizeof...(A) == 1> f(A&&... args) {
std::cout << "has not f(int)" << std::endl;
}
};
Demo
You can use a function (assert) that gets pointer to a function to deduce size of paramemters :
#include <utility>
#include <iostream>
template <typename...Args>
struct size_assert{
template <typename T,typename R,typename... Params>
constexpr static bool assert(R(T::*)(Params...) )
{
static_assert(sizeof...(Args) == sizeof...(Params),"Incorrect size of arguments!");
return true;
}
};
struct S {
template<typename T, typename... A, bool = size_assert<A...>::assert(&T::f)>
auto f(A&&... args) -> decltype(std::declval<T>().f(std::forward<A>(args)...), void())
{
std::cout << "has f(int)" << std::endl;
}
template<typename>
void f(...) {
std::cout << "has not f(int)" << std::endl;
}
};
struct T { void f(int) { } };
struct U { };
int main() {
// std::cout <<fc(&f);
S s;
s.f<T>(42); // -> has f(int)
s.f<U>(42); // -> has not f(int)
// oops
s.f<T>(); // -> has not f(int)
}
In case when static polymorphism is used, especially in templates (e.g. with policy/strategy pattern), it may be required to call base function member, but you don't know was instantiated class actually derived from this base or not.
This easily can be solved with old good C++ ellipsis overload trick:
#include <iostream>
template <class I>
struct if_derived_from
{
template <void (I::*f)()>
static void call(I& x) { (x.*f)(); }
static void call(...) { }
};
struct A { void reset() { std::cout << "reset A" << std::endl; } };
struct B { void reset() { std::cout << "reset B" << std::endl; } };
struct C { void reset() { std::cout << "reset C" << std::endl; } };
struct E: C { void reset() { std::cout << "reset E" << std::endl; } };
struct D: E {};
struct X: A, D {};
int main()
{
X x;
if_derived_from<A>::call<&A::reset>(x);
if_derived_from<B>::call<&B::reset>(x);
if_derived_from<C>::call<&C::reset>(x);
if_derived_from<E>::call<&E::reset>(x);
return 0;
}
The question is:
Is there any better simple way (e.g. SFINAE doesn't look so) to achieve same result in C++11/C++14?
Would empty call of ellipsis parameter function be elided by optimizing compiler? Hope such case is not special against any "normal" function.
One option is to introduce two overloads of different priorities and to equip the preferred one with an expression SFINAE.
#include <utility>
template <typename T, typename... Args, typename C, typename R, typename... Params>
auto call_impl(int, R(C::*f)(Args...), T&& t, Params&&... params)
-> decltype((std::forward<T>(t).*f)(std::forward<Params>(params)...))
{
return (std::forward<T>(t).*f)(std::forward<Params>(params)...);
}
template <typename T, typename... Args, typename C, typename R, typename... Params>
void call_impl(char, R(C::*)(Args...), T&&, Params&&...)
{
}
template <typename T, typename... Args, typename C, typename R, typename... Params>
auto call(R(C::*f)(Args...), T&& t, Params&&... params)
-> decltype(call_impl(0, f, std::forward<T>(t), std::forward<Params>(params)...))
{
return call_impl(0, f, std::forward<T>(t), std::forward<Params>(params)...);
}
Test:
int main()
{
X x;
call(&B::reset, x);
}
DEMO
The upper function will be selected first by overload resolution (due to an exact match of 0 against int), and possibly excluded from the set of viable candidates if (t.*f)(params...) is not valid. In the latter case, the call to call_impl falls back to the second overload, which is a no-op.
Given that &A::reset may fail for multiple reasons, and you may not necessarily want to explicitly specify the function's signature, and, on top of that, you want the call to fail if the member function exists, but it does not match function call arguments, then you can exploit generic lambdas:
#include <utility>
#include <type_traits>
template <typename B, typename T, typename F
, std::enable_if_t<std::is_base_of<B, std::decay_t<T>>{}, int> = 0>
auto call(T&& t, F&& f)
-> decltype(std::forward<F>(f)(std::forward<T>(t)))
{
return std::forward<F>(f)(std::forward<T>(t));
}
template <typename B, typename T, typename F
, std::enable_if_t<!std::is_base_of<B, std::decay_t<T>>{}, int> = 0>
void call(T&& t, F&& f)
{
}
Test:
int main()
{
X x;
call<A>(x, [&](auto&& p) { return p.A::reset(); });
call<B>(x, [&](auto&& p) { return p.B::reset(); });
}
DEMO 2
what about something like:
#include <iostream>
#include <type_traits>
struct A { void reset() { std::cout << "reset A" << std::endl; } };
struct B { void reset() { std::cout << "reset B" << std::endl; } };
struct X :public A{};
template <typename T, typename R, typename BT>
typename std::enable_if<std::is_base_of<BT, T>::value, R>::type
call_if_possible(T & obj, R(BT::*mf)())
{
return (obj.*mf)();
}
template <typename T, typename R, typename BT>
typename std::enable_if<!std::is_base_of<BT, T>::value, R>::type
call_if_possible(T & obj, R(BT::*mf)()) { }
int main()
{
X x;
call_if_possible(x, &A::reset);
call_if_possible(x, &B::reset);
}
ideone
edit
maybe more readable way:
template <typename T, typename R, typename BT>
R call_if_possible_impl(T & obj, R(BT::*mf)(), std::false_type){}
template <typename T, typename R, typename BT>
R call_if_possible_impl(T & obj, R(BT::*mf)(), std::true_type)
{
return (obj.*mf)();
}
template <typename T, typename R, typename BT>
R call_if_possible(T & obj, R(BT::*mf)())
{
return call_if_possible_impl(obj, mf, typename std::is_base_of<BT, T>::type());
}
ideone
Basing on previously provided answers by #PiotrSkotnicki and #relaxxx I would like to combine the most simple and readable solution, without SFINAE and other blood-from-the-eyes things. It's just for reference, will not be accepted anyway:
#include <iostream>
#include <type_traits>
template <class Base, class Derived>
using check_base = typename std::is_base_of<Base, Derived>::type;
template <class Base, class Derived, typename Func>
void call(Derived& d, Func&& f)
{
call<Base>(d, std::forward<Func>(f), check_base<Base, Derived>());
}
template <class Base, typename Func>
void call(Base& b, Func&& f, std::true_type)
{
f(b);
}
template <class Base, class Derived, typename Func>
void call(Derived&, Func&&, std::false_type)
{
}
struct A { void reset(int i) { std::cout << "reset A: " << i << std::endl;} };
struct B { void reset() { std::cout << "reset B" << std::endl;} };
struct C { void reset() { std::cout << "reset C" << std::endl;} };
struct E: C { void reset() { std::cout << "reset E" << std::endl;} };
struct D: A, E {};
int main()
{
D d;
int i = 42;
call<A>(d, [&](auto& p) { p.reset(i); } );
call<B>(d, [](auto& p) { p.reset(); } );
call<C>(d, [](auto& p) { p.reset(); } );
call<E>(d, [](auto& p) { p.reset(); } );
}
Live at: http://cpp.sh/5tqa
So suppose, that I have got a class, that contains functional object and in the constructor call I pass arguments, that are to be passed to the functional object some time later. Something like:
class Binder{
public:
Binder(functional_object, listOfParameters);
callFunctionalObject(); // calls functional object with given list of parameters
};
Before C++11 I could not use Variadic templates, so one would do:
struct none{};
template <typename T1, typename T2=none, typename T3=none>
class Binder{
public:
Binder(T1 functionalObject, T2 arg1=none(), T3arg3=none());
void callFunctionalObject();
private:
T1 m_functionalObject;
T2 m_arg1;
T3 m_arg2;
};
Where callFunctionalobject could be implemented as follows:
template<typename T1, typename T2, typename T3>
void Binder<T1,T2,T3>::callFunctionalObject(){
callImpl(m_functionalObject, m_arg1, m_arg2);
}
and callImpl would be overloaded to recognize objects of type none to pass proper amount of arguments to the functional object.
Now switching to C++11 I do not know how to implement the fact, that in private section I have got members, to which I have an direct access.
Could anyone explain me the way I can do the same using C++11 or C++14?
You should store a std::function and a std::tuple and then call the function on the tuple.
Here a working C++14 solution
#include <iostream>
#include <functional>
template<typename T1, typename ...T>
class Binder
{
public:
Binder(std::function<T1(T...)> f, std::tuple<T...> t) : m_functional_obj(f), m_parameters(t) {}
template<std::size_t ...I>
T1 callImpl(std::index_sequence<I...>)
{
return m_functional_obj(std::get<I>(m_parameters)...);
}
T1 callFunctionalObject()
{
return callImpl(std::index_sequence_for<T...>{});
}
private:
std::function<T1(T...)> m_functional_obj;
std::tuple<T...> m_parameters;
};
int test(int i)
{
std::cout << "test(" << i << ")" << std::endl;
return i + 1;
}
int main()
{
Binder<int,int> bibi(test, std::make_tuple<int>(2));
auto res = bibi.callFunctionalObject();
std::cout << "res is " << res << std::endl;
}
Live code
My example:
// Indices
template <std::size_t... Is>
struct Indices {};
template <std::size_t N, std::size_t... Is>
struct BuildIndices : BuildIndices <N - 1, N - 1, Is...> {};
template <std::size_t... Is>
struct BuildIndices<0, Is...> : Indices < Is... > {};
template<class FuncObject, class ... T>
class Binder
{
public:
Binder(FuncObject funcObject, T... args)
: m_funcObject(funcObject), m_arguments(std::make_tuple(args...))
{
}
void Call()
{
DoCall(BuildIndices<sizeof ... (T)> {});
}
private:
template<size_t... Ind>
void DoCall(Indices<Ind...>)
{
return m_funcObject(std::get<Ind>(m_arguments)...);
}
FuncObject m_funcObject;
std::tuple<T...> m_arguments;
};
void Foo(int, char)
{
}
int main()
{
Binder<void(*)(int, char), int, char> f(Foo, 1, 'd');
f.Call();
return 0;
}
The simplest way is to store an std::function object with already-set arguments using std::bind:
class Binder{
public:
template <typename T1, typename... T2>
Binder(T1 functionalObject, T2... args) : f(std::bind(functionalObject, args...)) {}
void callFunctionalObject() { f(); }
private:
std::function<void()> f;
};
void foo(int n, std::string s) {
std::cout << n << " " << s << std::endl;
}
int main()
{
Binder b(foo, 42, "test");
b.callFunctionalObject();
}
If you need something more advanced, then you might want to store the function arguments in and std::tuple and then use some template magic to unwrap it, but please specify what exactly do you need in the question.
P.S. See also "unpacking" a tuple to call a matching function pointer