是否可以使用包含 {sub,super}class 函数指针的回调 table?
Is it possible to use a callback table which contains {sub,super}class function pointers?
我目前有一个连接对象,它对传入的命令请求执行处理。每个命令都是一个字符串,使用无序映射映射到回调方法。一个说明核心功能的简化示例:
#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();
}
这工作正常,但我意识到我需要多个不同的网络接口,它们具有不同的主要角色(想想 "management interface" 与 "client data interface")所以我复制了 Conn class 到 ConnMgmt 和 ConnData。然而,很快就发现处理程序之间有足够的重叠,因此为两者创建一个公共 class 是有意义的。
我想做的是创建一个包含 ConnMgmt 和 ConnData 共享功能的 ConnBase。
我的问题与回调函数有关 table。
我想要一个 table 来将命令请求字符串映射到回调函数,但是我希望方法指针能够引用方法在派生的 classes 中。 IE。我有一个用于 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" table 包含指向 ConnBase 和 ConnMgmt 中的方法的指针的想法是否从根本上被打破了?编译器报错,说指针类型错误。
- 如果有一种方法可以解决问题,我会进入未定义行为(或更糟)的领域吗?我已经读到它 "should" 可以为 subclasses 使用方法指针类型,但是有一些与虚拟 classes 有关的警告(并且在那条注释上:有 在classes中是个虚函数,但是none个处理函数是虚函数。
- 考虑到我想要完成的事情(创建一个字符串到方法的调度程序,其中方法可能位于不同的 classes(尽管始终属于相同的 class 层次结构)),是有一些惯用的 C++17 方法吗?
(不允许提升)。
您可以通过一些更改来完成所需的操作。
如果将 ProcFn
的类型更改为
typedef void (ConnMgmt::*ProcFn)();
那么你几乎已经准备好构建你的 ConnMgmt::handlers
table。您需要为 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)
};
每个派生的 class 都需要自己的处理程序 table 和 ProcFn
类型定义。
我目前有一个连接对象,它对传入的命令请求执行处理。每个命令都是一个字符串,使用无序映射映射到回调方法。一个说明核心功能的简化示例:
#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();
}
这工作正常,但我意识到我需要多个不同的网络接口,它们具有不同的主要角色(想想 "management interface" 与 "client data interface")所以我复制了 Conn class 到 ConnMgmt 和 ConnData。然而,很快就发现处理程序之间有足够的重叠,因此为两者创建一个公共 class 是有意义的。
我想做的是创建一个包含 ConnMgmt 和 ConnData 共享功能的 ConnBase。
我的问题与回调函数有关 table。
我想要一个 table 来将命令请求字符串映射到回调函数,但是我希望方法指针能够引用方法在派生的 classes 中。 IE。我有一个用于 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" table 包含指向 ConnBase 和 ConnMgmt 中的方法的指针的想法是否从根本上被打破了?编译器报错,说指针类型错误。
- 如果有一种方法可以解决问题,我会进入未定义行为(或更糟)的领域吗?我已经读到它 "should" 可以为 subclasses 使用方法指针类型,但是有一些与虚拟 classes 有关的警告(并且在那条注释上:有 在classes中是个虚函数,但是none个处理函数是虚函数。
- 考虑到我想要完成的事情(创建一个字符串到方法的调度程序,其中方法可能位于不同的 classes(尽管始终属于相同的 class 层次结构)),是有一些惯用的 C++17 方法吗?
(不允许提升)。
您可以通过一些更改来完成所需的操作。
如果将 ProcFn
的类型更改为
typedef void (ConnMgmt::*ProcFn)();
那么你几乎已经准备好构建你的 ConnMgmt::handlers
table。您需要为 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) };
每个派生的 class 都需要自己的处理程序 table 和 ProcFn
类型定义。