是否可以使用包含{sub,super}类函数指针的回调表?

时间:2019-05-25 13:07:00

标签: c++ inheritance c++17 pointer-to-member

我目前有一个连接对象,该对象执行传入命令请求的处理。每个命令都是一个字符串,该字符串使用无序映射映射到回调方法。精简示例说明了核心功能:

#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>

class Conn;
typedef void (Conn::*ProcFn)();

class Conn
{
  private:
  void proc_ping();

  static const std::unordered_map<std::string, ProcFn> handlers;

  public:
  void simulate();
};

const std::unordered_map<std::string, ProcFn> Conn::handlers = {
  std::pair("ping", &Conn::proc_ping)
};

void Conn::proc_ping()
{
  std::cout << "ping!" << std::endl;
}

void Conn::simulate()
{
  auto pfn = handlers.at("ping");

  std::invoke(pfn, this);
}

int main()
{
  Conn c;

  c.simulate();
}

这很好,但我意识到我需要多个具有不同主要角色的网络接口(认为“管理接口”与“客户端数据接口”),因此我将Conn类的副本复制到ConnMgmt和ConnData中。但是,很快就发现处理程序之间有足够的重叠,因此有必要为两者创建一个通用类。

我要创建一个ConnBase,其中包含ConnMgmt和ConnData的共享功能。

我的问题与回调函数表有关。

我希望有一个表将命令请求字符串映射到回调函数,但是我希望方法指针能够引用派生类中的方法。即我有一个ConnBase和ConnMgmt或ConnBase和ConnData的调度程序(取决于要调用的接口)。

这是[非功能性]代码,它说明了我要完成的工作的要点:

#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>

class ConnBase;
typedef void (ConnBase::*ProcFn)();

class ConnBase
{
  protected:
  void proc_ping();
};

void ConnBase::proc_ping()
{
  std::cout << "ping!" << std::endl;
}

class ConnMgmt : public ConnBase
{
  protected:
  static const std::unordered_map<std::string, ProcFn> handlers;

  void proc_create_user();

  public:
  void simulate();
};

void ConnMgmt::proc_create_user()
{
  std::cout << "create user!" << std::endl;
}

const std::unordered_map<std::string, ProcFn> ConnMgmt::handlers = {
  std::pair("ping", &ConnBase::proc_ping),
  std::pair("create_user", &ConnMgmt::proc_create_user)
};

void ConnMgmt::simulate()
{
  auto pfn = handlers.at("ping");

  std::invoke(pfn, this);

  pfn = handlers.at("create_user");

  std::invoke(pfn, this);
}

int main()
{
  ConnMgmt c;

  c.simulate();
}

此代码以多种方式被破坏,但是我特别想知道是否:

  • 是否有一个包含“ ConnMgmt :: handlers”表的表,该表包含指向ConnBase和ConnMgmt中的方法的指针,这从根本上讲是无效的?编译器发出嘶哑的声音,说指针的类型错误。
  • 如果有一种方法可以解决此问题,我是否会进入行为不确定(或更糟)的领域?我已经读过,“应该”可以将方法指针类型用于子类,但是有一些关于虚拟类的警告(并且注意:类中有 个虚拟函数,但是所有处理程序功能都不会是虚拟的。
  • 给出我要完成的工作(创建一个方法可能位于不同类(尽管始终属于同一类层次结构)的字符串到方法的分派器),是否有一些惯用的C ++ 17方法可以做到这一点?

(不允许提升)。

1 个答案:

答案 0 :(得分:1)

只需进行一些更改即可完成所需的操作。

如果将ProcFn的类型更改为

typedef void (ConnMgmt::*ProcFn)();

然后,您几乎准备好构建ConnMgmt::handlers表。您需要指定std::pair的类型。然后剩下的问题是ConnBase::proc_ping受保护,并且不可访问。解决该问题的方法是将引用更改为ConnMgmt::proc_ping

const std :: unordered_map ConnMgmt :: handlers = {   std :: pair(“ ping”,&ConnMgmt :: proc_ping),   std :: pair(“ create_user”,&ConnMgmt :: proc_create_user) };

每个派生类将需要自己的处理程序表和ProcFn typedef。