【问题标题】:C++ call method passed as argument作为参数传递的 C++ 调用方法
【发布时间】:2013-03-09 15:09:29
【问题描述】:

我有一个 Session 类,它定义了多个接收不同参数的 Send 方法。例如:

class Session
{
public:
    void SendInt(int i);
    void SendTwoInts(int i1, int i2);
    void SendStringAndInt(const std::string& str, int i);
};

我还有 SessionsManager 类来保存所有会话。

class SessionsManager
{
private:
    std::vector<Session*> m_Sessions;
    ...
};

我想将广播方法添加到 SessionsManager 类,为每个会话调用相同的方法。如果我只是为每个 Session Send 方法定义一个单独的广播方法,我会像这样结束:

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

复制粘贴太多了,理论上发送方法的数量将来会增加。我想要更聪明的东西。

在完美的场景中,我想象有一个模板化的 Broadcast 方法,该方法将 Session 方法及其参数作为参数,例如:

template<typename Method, typename ...Args)
void Broadcast(Method, Args ... args)
{
...
}

广播呼叫将是

Broadcast(&Session::SendInt, 2);
Broadcast(&Session::SendTwoInts, 2, 3);

问题是我不确定它是否可能以及如何准确地实现广播。我正在考虑 std::function 和 std::bind 但我仍然无法编译我的代码。

欢迎提出任何想法。

  • 已更新

可以有 2 个参数相同但语义不同的 Send 方法。例如:

void SendName(const std::string& name);
void SendDescription(const std::string& description);

【问题讨论】:

  • 我不明白你为什么不重载发送方法?
  • 因为有可能 2 个 Send 方法具有相同的参数但语义不同。例如 SendName(const std::string& name) 和 SendDescription(const std::string& description)
  • 在您的示例中,您永远不会说sendNamesendDescription,而是说sendIntsend2Ints 等等....您执行了您永远不应该执行的手动名称修改。它的编译器应该这样做。
  • 也许这个例子有点误导
  • @plamkata__:我更新了我的答案以使用成员函数指针

标签: c++ function templates c++11 bind


【解决方案1】:

老实说,我会通过为Broadcast 使用可变参数模板来解决这个问题,并简单地为不同的参数重载Send() 方法。

代码如下:

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

};

你会如何使用它:

int main()
{
    SessionsManager sm;
    sm.Broadcast(1, 2);
    sm.Broadcast(1);
    sm.Broadcast("Hello", 2);
}

这是live example


更新:

如果您真的无法承受重载,则此解决方案可以满足您的原始要求:

#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

};

这就是你将如何使用它:

int main()
{
    SessionsManager sm;
    sm.Broadcast(&Session::SendTwoInts, 1, 2);
    sm.Broadcast(&Session::SendInt, 1);
    sm.Broadcast(&Session::SendStringAndInt, "Hello", 1);
}

这是live example

【讨论】:

  • 感谢您的建议,但问题是 2 个 Send 方法可能采用相同的参数但具有不同的语义。例如 SendName(const std::string& name) 和 SendDescription(const std::string& description)
  • @plamkata__:好的,让我按照你建议的方式尝试
  • 非常感谢 - 这正是我所需要的。顺便说一句,我昨天尝试使用非常相似的解决方案,但没有编译,但那是因为我使用 std::shared_ptr 来保存会话并且 (it->*mf) 无法编译。另外我认为它可以简化为 template void Broadcast(Method m, Args&&... args)
  • @plamkata__:很高兴它有帮助!你是对的,它可以被简化。我编辑了它。此外,编辑后的版本也适用于shared_ptr
【解决方案2】:

std::bind 的解决方案可能如下所示

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

【讨论】:

  • 谢谢。这也是一个不错的解决方案。
【解决方案3】:

我不知道你真正想要达到什么目的。它看起来像one SessionsManager have multiple Sessions。对 SessionManager 执行操作会将操作中继到其所有 Sessions

你有一个广播器,可以向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;
};

现在您将boost::bind 发送到SessionManager::broadcast

【讨论】:

    猜你喜欢
    • 2018-09-09
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 2020-08-12
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    相关资源
    最近更新 更多