简体   繁体   中英

C++ call method passed as argument

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, ie 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.

  • UPDATED

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);

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 .

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);
}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM