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 循环期望在 : 的右侧有一些东西可以提供迭代器范围的开始和结束,但是搜索具有名称的自由函数或成员函数beginend。因此,我们必须翻译 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 包装器上的代码允许存储任意类型的对象,只要它们可以用特定的签名调用。