c++11:使用中央命令映射器调用虚拟基类方法
c++11: call virtual base-class method using a central command-mapper
我想制作一个命令映射器,它接受某种类型的命令并将它们移交给一个公共 Bindable
[=] 的各种子 class 的运行时注册成员23=].
由于 sub-class 成员的类型不同,我很难编写一个有效的 Mapper
class。我需要如何实施才能使其发挥作用?
#include <iostream> // std::cout
#include <functional> // std::bind
#include <map> // std::map
#include <vector> // std::vector
struct Command {
int cmdNum;
int numArgs;
std::vector<int> args;
};
struct Invocation {
enum Source {
SOURCE_X = 0, SOURCE_Y, SOURCE_Z,
SOURCE_END
};
Source src;
Command cmd;
};
struct Bindable {
virtual void handleCmd(Command Cmd) = 0;
};
struct A : Bindable {
void handleCmd (Command cmd) {
std::cout << "called handler-method of class A" <<std::endl;
std::cout << "cmdNum: " << cmd.cmdNum <<std::endl;
}
};
struct B : Bindable {
void handleCmd (Command cmd) {
std::cout << "called handler-method of class B" <<std::endl;
std::cout << "cmdNum: " << cmd.cmdNum <<std::endl;
}
};
有问题的映射器:
struct Mapper {
void bindCmd(Command cmd, Bindable* mBindable) {
//Fill a multimap with cmd.cmdNum as keys and mBindable as values
}
//Send cmd to each registered Bindable for the respective cmdNum
void handleInv(Invocation inv) {
auto mMatches = mBinds.equal_range(inv.cmd.cmdNum);
for(auto mMatch : mMatches) {
mMatch.second()->handleCmd(inv.cmd);
}
}
private:
std::multimap<int, Bindable*> mBinds;
};
所需的用法应为:
int main() {
A a;
B b;
Command cmdA = {200, 4, {1,2,3,4}};
Command cmdB = {400, 3, {3,2,1}};
Command cmdC = {600, 2, {8,9}};
Invocation invA = {Invocation::SOURCE_X, cmdA};
Invocation invB = {Invocation::SOURCE_Z, cmdB};
Invocation invC = {Invocation::SOURCE_Z, cmdC};
Mapper mMapper;
//Register Commands
mMapper.bindCmd(cmdA, &a);
mMapper.bindCmd(cmdB, &a);
mMapper.bindCmd(cmdA, &b);
mMapper.bindCmd(cmdC, &b);
//React to incoming Invocations
mMapper.handleInv(invA); //Call handleCmd of a and b
mMapper.handleInv(invB); //Call handleCmd of a
mMapper.handleInv(invC); //Call handleCmd of b
}
据我所知,OP 中的代码在修复两个小错误时有效:
std::multimap<int, Bindable*> mBinds;
void handleInv(Invocation inv) {
auto mMatches = mBinds.equal_range(inv.cmd.cmdNum);
for(auto mMatch : mMatches) { // 1
mMatch.second()->handleCmd(inv.cmd); // 2
}
}
1
std::multimap<K,V>::equal_range
returns 一个 std::pair
迭代器,其中成员 first
指定迭代器的开始,成员 second
指定迭代器的结束 -范围。
基于范围的 for 循环期望在 :
的右侧有一些东西可以提供迭代器范围的开始和结束,但是搜索具有名称的自由函数或成员函数begin
和 end
。因此,我们必须翻译 std::pair::first
-> begin()
和 std::pair::second
-> end()
.
当然有图书馆的解决方案(例如boost)。最小的解决方案可能是:
template<typename It>
struct iterator_pair_range
{
It b;
It e;
It begin() const { return b; }
It end() const { return e; }
};
template<typename It>
auto make_iterator_pair_range(std::pair<It, It> const& p)
-> iterator_pair_range<It>
{ return {p.first, p.second}; }
for(auto mMatch : make_iterator_pair_range(mMatches)) {
2
mMatch.second()->handleCmd(inv.cmd); // 2
std::pair
的成员second
是public数据成员,不是成员函数:
mMatch.second->handleCmd(inv.cmd); // 2
我会建议你 post 你在 CodeReview.SE, since there are more general, safer (e.g. lifetime issues) and possibly easier solutions to this general problem. For example, there is the boost.signals2 library; also there is the std::function
包装器上的代码允许存储任意类型的对象,只要它们可以用特定的签名调用。
我想制作一个命令映射器,它接受某种类型的命令并将它们移交给一个公共 Bindable
[=] 的各种子 class 的运行时注册成员23=].
由于 sub-class 成员的类型不同,我很难编写一个有效的 Mapper
class。我需要如何实施才能使其发挥作用?
#include <iostream> // std::cout
#include <functional> // std::bind
#include <map> // std::map
#include <vector> // std::vector
struct Command {
int cmdNum;
int numArgs;
std::vector<int> args;
};
struct Invocation {
enum Source {
SOURCE_X = 0, SOURCE_Y, SOURCE_Z,
SOURCE_END
};
Source src;
Command cmd;
};
struct Bindable {
virtual void handleCmd(Command Cmd) = 0;
};
struct A : Bindable {
void handleCmd (Command cmd) {
std::cout << "called handler-method of class A" <<std::endl;
std::cout << "cmdNum: " << cmd.cmdNum <<std::endl;
}
};
struct B : Bindable {
void handleCmd (Command cmd) {
std::cout << "called handler-method of class B" <<std::endl;
std::cout << "cmdNum: " << cmd.cmdNum <<std::endl;
}
};
有问题的映射器:
struct Mapper {
void bindCmd(Command cmd, Bindable* mBindable) {
//Fill a multimap with cmd.cmdNum as keys and mBindable as values
}
//Send cmd to each registered Bindable for the respective cmdNum
void handleInv(Invocation inv) {
auto mMatches = mBinds.equal_range(inv.cmd.cmdNum);
for(auto mMatch : mMatches) {
mMatch.second()->handleCmd(inv.cmd);
}
}
private:
std::multimap<int, Bindable*> mBinds;
};
所需的用法应为:
int main() {
A a;
B b;
Command cmdA = {200, 4, {1,2,3,4}};
Command cmdB = {400, 3, {3,2,1}};
Command cmdC = {600, 2, {8,9}};
Invocation invA = {Invocation::SOURCE_X, cmdA};
Invocation invB = {Invocation::SOURCE_Z, cmdB};
Invocation invC = {Invocation::SOURCE_Z, cmdC};
Mapper mMapper;
//Register Commands
mMapper.bindCmd(cmdA, &a);
mMapper.bindCmd(cmdB, &a);
mMapper.bindCmd(cmdA, &b);
mMapper.bindCmd(cmdC, &b);
//React to incoming Invocations
mMapper.handleInv(invA); //Call handleCmd of a and b
mMapper.handleInv(invB); //Call handleCmd of a
mMapper.handleInv(invC); //Call handleCmd of b
}
据我所知,OP 中的代码在修复两个小错误时有效:
std::multimap<int, Bindable*> mBinds;
void handleInv(Invocation inv) {
auto mMatches = mBinds.equal_range(inv.cmd.cmdNum);
for(auto mMatch : mMatches) { // 1
mMatch.second()->handleCmd(inv.cmd); // 2
}
}
1
std::multimap<K,V>::equal_range
returns 一个 std::pair
迭代器,其中成员 first
指定迭代器的开始,成员 second
指定迭代器的结束 -范围。
基于范围的 for 循环期望在 :
的右侧有一些东西可以提供迭代器范围的开始和结束,但是搜索具有名称的自由函数或成员函数begin
和 end
。因此,我们必须翻译 std::pair::first
-> begin()
和 std::pair::second
-> end()
.
当然有图书馆的解决方案(例如boost)。最小的解决方案可能是:
template<typename It>
struct iterator_pair_range
{
It b;
It e;
It begin() const { return b; }
It end() const { return e; }
};
template<typename It>
auto make_iterator_pair_range(std::pair<It, It> const& p)
-> iterator_pair_range<It>
{ return {p.first, p.second}; }
for(auto mMatch : make_iterator_pair_range(mMatches)) {
2
mMatch.second()->handleCmd(inv.cmd); // 2
std::pair
的成员second
是public数据成员,不是成员函数:
mMatch.second->handleCmd(inv.cmd); // 2
我会建议你 post 你在 CodeReview.SE, since there are more general, safer (e.g. lifetime issues) and possibly easier solutions to this general problem. For example, there is the boost.signals2 library; also there is the std::function
包装器上的代码允许存储任意类型的对象,只要它们可以用特定的签名调用。