Reputation: 763
I have a Session class that defines multiple Send methods that takes different arguments. For example:
class Session
{
public:
void SendInt(int i);
void SendTwoInts(int i1, int i2);
void SendStringAndInt(const std::string& str, int i);
};
Also I have SessionsManager class that holds all sessions.
class SessionsManager
{
private:
std::vector<Session*> m_Sessions;
...
};
I'd like to add broadcast methods to SessionsManager class that calls the same method for each session. If I just define a separate broadcast method for each Session Send method I will end like:
class SessionsManager
{
public:
void BroadcastInt(int i) { for(auto it : m_Sessions) { it->SendInt(i); } };
void BroadcastTwoInts(int i1, int i2) { for(auto it : m_Sessions) { it->SendTwoInts(i1, i2); } };
...
};
It is too much copy-paste and in theory the number of Send methods will grow in the future. I'd like something smarter.
In the perfect scenario I imagine having templated Broadcast method that takes Session method and its arguments as arguments, i.e. something like:
template<typename Method, typename ...Args)
void Broadcast(Method, Args ... args)
{
...
}
and the broadcast call will be
Broadcast(&Session::SendInt, 2);
Broadcast(&Session::SendTwoInts, 2, 3);
The problem is that I am not sure if it is possible and how exactly to implement Broadcast. I am thinking around std::function and std::bind but still I am unable to compile my code.
Any ideas are welcome.
It is possible to have 2 Send methods with the same arguments but with different semantic. For example:
void SendName(const std::string& name);
void SendDescription(const std::string& description);
Upvotes: 2
Views: 261
Reputation: 12904
I don't know what you are really trying to achive. It looks like one SessionsManager have multiple Sessions
. Performing an action on SessionManager
relays that action to all its Session
s
and you have a Broadcaster that sends commands to SessionManager
struct SessionManager{
template <typename F>
void broadcast(F f){
std::for_each(m_sessions.begin(), m_sessions.end(), f);
}
std::vector<Session*> m_sessions;
};
Now you do boost::bind
and send them to SessionManager::broadcast
Upvotes: 0
Reputation: 2201
A solution with std::bind
could look like
#include <iostream>
#include <functional>
#include <vector>
class Session
{
public:
void SendInt(int i){ std::cout << i; }
void SendTwoInts(int i1, int i2){ std::cout << i1;}
};
class SessionsManager
{
public:
std::vector<Session*> m_Sessions;
template<typename T, typename ...Args>
void Broadcast(T f, Args&& ...args) {
for (auto it : m_Sessions) {
std::bind(f, it, std::forward<Args>(args)...)();
}
}
};
int main() {
SessionsManager m;
m.m_Sessions.push_back(new Session());
m.m_Sessions.push_back(new Session());
m.Broadcast(&Session::SendInt, 2);
m.Broadcast(&Session::SendTwoInts, 3, 2);
}
Upvotes: 1
Reputation: 126412
Honestly I would solve this by using a variadic template for Broadcast
, and simply overload the Send()
method for different arguments.
Here is the code:
#include <vector>
#include <string>
class Session
{
public:
void Send(int i) { }
void Send(int i1, int i2) { }
void Send(const std::string& str, int i) { }
};
class SessionsManager
{
public:
template<typename... Args>
void Broadcast(Args&&... args)
{
for(auto it : m_Sessions)
{
it->Send(std::forward<Args>(args)...);
}
}
private:
std::vector<Session*> m_Sessions;
};
Here is how you would use it:
int main()
{
SessionsManager sm;
sm.Broadcast(1, 2);
sm.Broadcast(1);
sm.Broadcast("Hello", 2);
}
And here is a live example.
UPDATE:
Provided you really cannot afford overloading, this solution meets your original requirements:
#include <vector>
#include <string>
class Session
{
public:
void SendInt(int i) { }
void SendTwoInts(int i1, int i2) { }
void SendStringAndInt(const std::string& str, int i) { }
};
class SessionsManager
{
public:
template<typename M, typename... Args>
void Broadcast(M m, Args&&... args)
{
for(auto it : m_Sessions)
{
((*it).*m)(std::forward<Args>(args)...);
}
}
private:
std::vector<Session*> m_Sessions; // You could use shared_ptr<> here
};
This is how you would use it:
int main()
{
SessionsManager sm;
sm.Broadcast(&Session::SendTwoInts, 1, 2);
sm.Broadcast(&Session::SendInt, 1);
sm.Broadcast(&Session::SendStringAndInt, "Hello", 1);
}
And here is a live example.
Upvotes: 4