I'd like to create a class, than when instantiated, accepts a variable number of function references that don't have signatures known beforehand. Here's an example that almost does what I want:
// To show the function refs being used
void p(int arg) { cout << "int " << arg << endl; }
void p(string arg) { cout << "string " << arg << endl; }
void p(int arg1, int arg2) { cout<<"int/int "<<arg1<<arg2<<endl; }
void p(int arg1, string arg2) { cout<<"int/string "<<arg1<<arg2<<endl; }
class foo {
public:
// CTOR takes variadic function refs
template <typename... Args>
foo(Args... args) { p(args()...); }
// "args()..." requires supplied functions to take no parameters
// but makes no requirement on their return values.
};
// Using lambdas, but free functions, std::bind(), etc. work too
foo i([]{return 1;}); // prints "int 1"
foo s([]{return string("one");}); // prints "string one"
foo b([]{return 2;},
[]{return string("two");}); // prints "int/string 2two"
What I can't see how to fix this so that the functions supplied as arguments are not evaluated in the constructor. I'd like the call to p(args()...) to be done later, by another method in foo. This is why foo can't be created as simply as foo i(1): the argument function(s) need to be called later, and multiple times, not just once when the object is created (and they'd be more complex than just returning a constant).
The problem seems to come down to saving references to the constructor parameters to be called later, when the class doesn't know how many or what signature those parameters will have. Somehow the arguments need to be part of a class template and not just a constructor template, but how?
If the functions passed all had the same signature, then one could use a class template with a non-type parameters and supply the functions as template arguments:
template <int (&...Arg)()>
class bar {
public:
bar() { p(Arg()...); }
other() { p(Arg()...); } // Use in any method
};
int one() { return 1; }
int two() { return 2; }
bar<one> bi; // Prints "int 1"
bar<one, two> bii; // Prints "int/int 12"
But this requires all the arguments be functions that return int and also doesn't work with lambdas as they can't be template arguments.
You can use a lambda and a std::function to do that.
Note that a lambda can capture a parameters pack and (let me say) unpack it later.
It follows a minimal, working example:
#include<iostream>
#include<functional>
void p(int arg) { std::cout << "int " << arg << std::endl; }
void p(std::string arg) { std::cout << "string " << arg << std::endl; }
void p(int arg1, int arg2) { std::cout<<"int/int "<<arg1<<arg2<<std::endl; }
void p(int arg1, std::string arg2) { std::cout<<"int/string "<<arg1<<arg2<<std::endl; }
class foo {
public:
template <typename... Args>
foo(Args... args): func{[args...](){ p(args()...); }} {}
void operator()() { func(); }
private:
std::function<void()> func;
};
int main() {
// create your objects...
foo i([]{return 1;});
foo s([]{return std::string("one");});
foo b([]{return 2;}, []{return std::string("two");});
// ... and use them later
i();
s();
b();
}
Related
I wrote a template for a binary search tree. I wish to overload the in-order traversal function. I can get it to work when the function pointer only takes in 1 object of class T. I would like it to use a function pointer that uses 2 integer values as well, so that I can pull the integers from outside the statement in main, then my function pointer will use those values to find an object in my BST. Is that possible? I'll show my code below.
For example, here is a typical implementation using just the T object:
template<class T>
void BSTTemplate<T>::inOrder(Node<T>* p) const
{
if (p != nullptr)
{
inOrder(p->leftLink);
cout << p->info << " ";
inOrder(p->rightLink);
}
}
template<class T>
void BSTTemplate<T>::inOrderTraversal() const
{
inOrder(root);
}
When I activate this, my program spits out all the nodes in the BST in-order.
Here is the same code, but with a function pointer:
template<class T>
void BSTTemplate<T>::inOrder(Node<T>* p, void (*visit) (T&)) const
{
if (p != nullptr)
{
inOrder(p->leftLink, *visit);
(*visit) (p->info);
inOrder(p->rightLink, *visit);
}
}
template<class T>
void BSTTemplate<T>::inOrderTraversal(void (*visit) (T&)) const
{
inOrder(root, *visit);
}
And now, in my main() I can run a statement such as:
//assume tree exists
Tree.inOrderTraversal(MyFunction);
where MyFunction is a void function such that
void MyFunction(MyObject& myObj)
{
//does something
}
The goal for me is that I want to use a function with my Tree such that it takes in 2 integers from 'outside'. (basically user input)
I've tried something like this:
//overloading inOrder
template<class T>
void BSTTemplate<T>::inOrder(Node<T>* p, void (*visit) (T&,int&,int&)) const
{
if (p != nullptr)
{
inOrder(p->leftLink, *visit);
(*visit) (p->info);
inOrder(p->rightLink, *visit);
}
}
template<class T>
void BSTTemplate<T>::inOrderWithInputs(void (*visit) (T&,int&,int&)) const
{
inOrder(root, *visit);
}
But when I try to call the traversal function in main() like this:
Tree.inOrderWithInputs(MyFunction(*arguments*));
The parser expects the arguments of an object of type T, an int, and another int. Previously when the function was only void MyFunction(MyObject& myObj), I need only use the function name as an argument. With multiple arguments to MyFunction itself, I don't have an object to fill it in with.
Ideally I want to do something like
Tree.inOrderTraversal(MyFunction(input1,input2));
where inputs are integers acquired from the user. Then the function manipulates those values to create a key, then the key is used to search the tree in-order.
Help me connect the dots please?
In C++ it is better to use std::function than old style function pointers. std::function is a template you instantiate by specifying the return value and arguments (as many as you need).
It support passing a global function like you used in your code, as well as lambdas (you can also use a class method with std::bind).
When you call inOrderTraversal you should supply the arguments for the function as additional parameters (instead of using something like MyFunction(input1,input2) which actually invokes the function and passes it's return value).
See complete example:
#include <functional>
#include <iostream>
template <typename T>
class BSTTemplate
{
public:
//---------------------vvvvvvvvvvvvv----------------------------
void inOrderWithInputs(std::function<void(T&, int&, int&)> visit,
T& arg1, int& arg2, int& arg3) const // <--- the arguments for invoking the function
{
// Invoke the function with the arguments:
visit(arg1, arg2, arg3);
}
};
void GlobalFunc(float& arg1, int& arg2, int& arg3)
{
std::cout << "exceuting GlobalFunc with args: " << arg1 << ", " << arg2 << ", " << arg3 << std::endl;
}
int main()
{
BSTTemplate<float> bst;
float f{ 0 };
int i1{ 1 };
int i2{ 2 };
// Call with a global function:
bst.inOrderWithInputs(GlobalFunc,
f, i1, i2); // <--- the arguments for invoking the function
// Call with a lambda:
bst.inOrderWithInputs([](float& arg1, int& arg2, int& arg3)
{ std::cout << "exceuting lambda with args: " << arg1 << ", " << arg2 << ", " << arg3 << std::endl; },
f, i1, i2); // <--- the arguments for invoking the function
return 0;
}
Output:
exceuting GlobalFunc with args: 0, 1, 2
exceuting lambda with args: 0, 1, 2
I have this minimal class to represent an event which client can subscribe to.
The event can have an data type associated to it, so when it is triggered by a publisher, an argument of that type would be passed to the client's callback:
template<typename Arg, typename Callback = function<void(const Arg&)>>
class Event
{
public:
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& arg) {
mCallback(arg);
}
private:
Callback mCallback;
};
Now I can create an Event<int> or any other concrete type, but it is really important to me to also allow "empty" event, which has no data associated with it: Event<void>
But sadly that doesn't work:
static void FooVoid() {
cout << "Look ma, no args!" << endl;
}
static void FooInt(int a) {
cout << "int arg " << a << endl;
}
int main()
{
/* Compiles */
Event<int> eInt(&FooInt);
eInt.Trigger(42);
/* Does not compile :(
Event<void> eVoid(&FooVoid);
eVoid.Trigger();
*/
return 0;
}
Is there any way to achieve this desired API? How?
(P.S the solution should work on C++11)
The quickest way of solving this without explicitly specializing for void is to use a parameter pack (added in C++11) for your template argument instead of a single type and using an empty parameter pack instead of void. A parameter pack can homogeneously hold any number of type, including 0 and 1. Then it can be used to generate the right types and member functions. You basically just have to add ... correctly near every use of Arg (link) :
#include <functional>
#include <iostream>
template<typename ... Arg>
class Event
{
public:
using Callback = std::function<void(const Arg&...)>;
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& ... arg) {
mCallback(arg...);
}
private:
Callback mCallback;
};
static void FooVoid() {
std::cout << "Look ma, no args!" << std::endl;
}
static void FooInt(int a) {
std::cout << "int arg " << a << std::endl;
}
int main()
{
/* Compiles */
Event<int> eInt(&FooInt);
eInt.Trigger(42);
Event<> eVoid(&FooVoid);
eVoid.Trigger();
return 0;
}
This has the added benefit that you can use callbacks with more than one argument. If this isn't desirable you can add a static_assert to prevent it :
template<typename ... Arg>
class Event
{
public:
using Callback = std::function<void(const Arg&...)>;
static_assert(sizeof...(Arg) <= 1, "Too many arguments");
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& ... arg) {
mCallback(arg...);
}
private:
Callback mCallback;
};
Notice that this solution requires Event<> instead of Event<void>. You can solve that by adding a short specialization for Event<void> that uses Event<> (link) :
template<>
class Event<void> : public Event<>
{
// Inherit constructors
using Event<>::Event;
};
I want to make an array of known size of class functions. To do so, I've tried using typedef, but it hasn't been working out so far.
Also, some functions take no arguments ex. F(), but others do ex. G(int n), and in the typedef, I don't know how to tell it to accept no arguments for some (tried void but it says it is not a type), and to accept arguments for others.
class myClass
{
// An array of void functions
typedef void(myClass::*arrayOfFunctions)();
private:
arrayOfFunctions array[3] = { &myClass::F, &myClass::G, &myClass::H };
void F() { do stuff; }
void G(int n) { do stuff involving n; }
void H() { do stuff; }
};
What I have tried:
I have successfully made an array of void functions in a main with no classes involved which I can call when wanted, so part of the problem seems to be implementing this in a class and using its class functions.
// This works:
typedef void(*arrayOfFunctions)();
void Action1()
{
// stuff 1
}
void Action2()
{
// stuff 2
}
void Action3()
{
//stuff3
}
int main()
{
arrayOfFunctions functionArray[] = { Action1, Action2, Action3 };
// Call Action1
functionArray[0]();
return 0;
)
As was mentioned in comments, it is not possible directly. You cannot store objects of different type in the same array. However, there are ways to achieve what you want. How to get there very much depends on details. Latest when you call the function you need to know how many parameters to pass.
In your example one possibility is to refactor to have only methods with no parameters:
class myClass {
using memFun = void(myClass::*)();
void set_n(int x) { n = x; }
private:
memFun array[3] = { &myClass::F, &myClass::G, &myClass::H };
void F() { do stuff; }
void G() { do stuff involving n; }
void H() { do stuff; }
int n;
};
I changed the name of the alias, because it is just the type of a function pointer not an array. using is easier to read than typedef (it follows the more common x = something style).
When you call the function G the parameter n has to come from somewhere, so instead of passing it directly you can call set_n before iterating the array and call all mehtods without parameter.
It is not clear how you want to use such an array. If you know an element index at compile time, then you could probably use a std::tuple with template argument deduction. For example:
class my_class {
public:
template<std::size_t n, class... Args>
decltype(auto) call_fn(Args&&... args) {
constexpr auto ptrs = get_fn_pointers();
return std::invoke(std::get<n>(ptrs), this, std::forward<Args>(args)...);
}
private:
static constexpr auto get_fn_pointers() {
return std::tuple(&my_class::f, &my_class::g, &my_class::h);
}
void f() {
std::cout << "f()\n";
}
void g(int n) {
std::cout << "g(" << n << ")\n";
}
int h() {
std::cout << "h() => ";
return 9102;
}
};
int main() {
my_class c;
c.call_fn<0>(); // Output: f()
c.call_fn<1>(2019); // Output: g(2019)
std::cout << c.call_fn<2>(); // Output: h() => 9102
}
template<typename FirstArgT, typename...ArgsT>
class Server :public Server<ArgsT...> {
public:
Server(const function<void (FirstArgT, ArgsT...)>& func)
:Server<ArgsT...>([&](ArgsT args...) -> void { func(arg0, args...); }) { }
private:
FirstArgT arg0;
}
but the compiler says :
Error C3520 'ArgsT': parameter pack must be expanded in this context
Error C3546 '...': there are no parameter packs available to expand
in line 4 and 5.
Is it possible to use variadic parameters as parameters of a lambda is VS2015, or is there an alternative way to do it?
I extended and fixed your code to get it compiled. It would be nice if your next question comes with full example so that we have not the need to extend the rest of the example ;)
Indeed, I have no idea what you code is good for :-)
template<typename ...T> class Server;
template<typename FirstArgT, typename...ArgsT>
class Server<FirstArgT,ArgsT...> :public Server<ArgsT...> {
public:
Server(const std::function<void (FirstArgT, ArgsT...)>& func)
:Server<ArgsT...>([&](ArgsT ... args)-> void { func(arg0, args...); }) { }
private:
FirstArgT arg0;
};
template<typename FirstArgT>
class Server<FirstArgT>
{
public:
Server(const std::function<void (FirstArgT)>& func) {}
};
void Do( int, double) {}
int main()
{
Server<int,double> se( &Do );
}
If your intention is only to store the arguments somewhere and call the function with stored arguments, simply use std::bind.
void Do( int i, double d) { std::cout << i << " " << d << std::endl; }
int main()
{
auto fx= std::bind( &Do, 1, 2.34);
fx();
// which works also for lambda:
auto fx2=
std::bind( []( int i,double d )->void
{ std::cout << i << " " << d << std::endl; }, 4, 5.6);
}
How can I iterate over all base classes of a variadic template class and call a function for each of them.
Here is a minimal example:
struct A { void foo() { std::cout << "A" << std::endl; } };
struct B { void foo() { std::cout << "B" << std::endl; } };
struct C { void foo() { std::cout << "C" << std::endl; } };
template<typename... U>
struct X : public U...
{
void foo() {
static_cast<U*>(this)->foo()...; // ??? should call `foo` for all `U`
}
};
int main() {
X<A,B,C> x;
x.foo();
}
You can't normally without C++17's fold expressions. The ellipsis going there is not valid and the ellipsis going after the asterisk would create a list of pointer template arguments. For the appropriate pattern to be repeated, the ellipsis would have to be at the end of the statement, and that doesn't work here. I found this article to be a good resource for pack expansion.
Instead, there is a trick for it that doesn't require building up any recursive things:
int arr[] = {(static_cast<U*>(this)->foo(), 0)...};
This calls each function and then uses the result with the comma operator to produce the needed ints. Unfortunately, this might result in an unused variable warning. One minimal way around this is to use a std::array (or some class that can be initialized with an initializer list) and cast the result of creating an unnamed one of those to void (casting to void being a somewhat common technique for preventing the warning in general).
Here is a way:
struct thru{template<typename... A> thru(A&&...) {}};
struct A { void foo() { std::cout << "A" << std::endl; } };
struct B { void foo() { std::cout << "B" << std::endl; } };
struct C { void foo() { std::cout << "C" << std::endl; } };
template<typename... U>
struct X : public U...
{
void foo() { thru{(U::foo(), 0)...}; }
};
But if you care about the order of calls, watch out for the known gcc bug as discussed here.