【问题标题】:Class Hierarchy with down cast function pointers具有向下转换函数指针的类层次结构
【发布时间】:2017-11-07 20:40:21
【问题描述】:

TL;DR 如何让Base::Choose 接受Derived::CustomUserFunction 作为参数? 我得到的错误,我明白为什么我得到它,但我不知道如何解决它: Argument of type "Derived::* (...)" is incompatible with parameter of type "Base::* (...)"

#include <iostream>
    class Base {
    public:
        void Base::Choose(void(Base::*FctPtr)()) {
            (this->*FctPtr)();
        }

        virtual void Base::CustomUserFunction() = 0;
};

    class Derived : public Base {
    public:
        virtual void Derived::CustomUserFunction() {
            std::cout << "CustomFunction\n";
        }
 };

    int main()  {
        Derived D;
        D.Choose(&D.CustomUserFunction);
    }

完整版

我正在编写一个使用自定义用户函数的离散事件模拟引擎 (DESEngine)。由于这些自定义用户函数是特定于问题的,我希望能够在不知道其签名名称的情况下调用它们。引擎在文本文件中读取TURN_ON 并查找映射到 unordered_map 中 TURN_ON 字符串别名的函数指针。

基本上 DESEngine 会调用 UserEvents::Choose(std::string Name),它会调用由它的事件名称(字符串)给出的函数。

一切都运行良好,但可管理性变得越来越困难,因为我必须使用引擎解决许多不同的问题(从微分方程求解到模拟简单计算机)。 (而且我的 UserEvents 类中有各种不同的特定于问题的函数)

因此,我决定将我的类 UserEvents 设为一个抽象类并为我遇到的各种问题继承它,因此,自定义用户函数将存储在派生类中,不在基类中,但我希望"Choose" 方法驻留在基类中。 如何从基类调用派生类中的函数?

我得到的错误是,我明白为什么会得到它,但我不知道如何解决它: Argument of type "UserEvents_MVN::* (...)" is incompatible with parameter of type "UserEvents::* (...)"

class UserEvents
{   
    public:
    // Receives an event name and parameters
    struct EventWithParams { std::string Name; std::vector<boost::any> Params; };

    // Lets an outside class (DESEngine) configure which event to run before calling UserEvents::Choose()
    // TODO: Make getters/setters so Events can be passed through a better way
        EventWithParams Event;

        // Select which function(parameters) to call based on the EventWithParams Event (above)
        int UserEvents::Choose();

    protected:
        void UserEvents::UserFunctionPointerMap_AddFunction
        (std::string Alias, void(UserEvents::*FunctionPointer)(const std::vector<boost::any>&));

        virtual void UserEvents::BuildUFPAliasMap() = 0;

    private:

        // Here we have an unordered map that assigns User Function (pointer) to each Key (string or Alias or Event Name)
        std::unordered_map<std::string, void(UserEvents::*)(const std::vector<boost::any>&)> UserFunctionPointerAliasMap;
    };

下面是Choose的实现

int UserEvents::Choose()
    {
        try
        {
            (this->*UserFunctionPointerAliasMap.at(Event.Name))(Event.Params);
            return 0;
        }
        catch (const std::out_of_range e)
        {
            throw std::exception::exception("Unknown User Event");
            return -1;
        }
    }

派生类示例

#pragma once
#include "..\\UserEvents.h"

class UserEvents_MVN : public UserEvents
{
public:
    UserEvents_MVN();
    ~UserEvents_MVN();

protected:

    void UserEvents_MVN::MEMDUMP_LOAD(const std::vector<boost::any> &Parameters);

    void UserEvents_MVN::MEMDUMP(const std::vector<boost::any> &Parameters);

    void UserEvents_MVN::InstructionDecode(const std::vector<boost::any> &Parameters);


private:

    virtual void UserEvents_MVN::BuildUFPAliasMap();
};

以及我如何建立函数指针的索引(UserFunctionPointerAliasMap)

void UserEvents_MVN::BuildUFPAliasMap()
{
    UserFunctionPointerMap_AddFunction("MEMDUMP_LOAD",  &UserEvents_MVN::MEMDUMP_LOAD );
    UserFunctionPointerMap_AddFunction("MEMDUMP",   &UserEvents_MVN::MEMDUMP );
    UserFunctionPointerMap_AddFunction("DECODE",    &UserEvents_MVN::InstructionDecode);
}

【问题讨论】:

  • 什么?您正在尝试将指针存储为指向 UserEvents 成员的指针,但这些函数不是该类的成员。
  • @BoPersson 你是对的。我希望基类处理存储函数指针的 unordered_map,但我希望它们是其派生类的函数。我该如何解决这个问题?
  • 很高兴知道为什么它被否决,这样我下次可以做得更好:)

标签: c++ abstract-class member-function-pointers virtual-inheritance incompatibletypeerror


【解决方案1】:

解决问题的一种方法是在UserEvents 中定义virtual 成员函数,并在构建地图时使用它们。

例子:

class UserEvents
{   
  public:

    virtual void MEMDUMP_LOAD(const std::vector<boost::any> &Parameters) = 0;

 ...
};

然后覆盖UserEvents_MVN中的virtual函数。

class UserEvents_MVN : public UserEvents
{
  public:
    UserEvents_MVN();
    ~UserEvents_MVN();

  protected:

    void MEMDUMP_LOAD(const std::vector<boost::any> &Parameters) override;

  ...
};

并使用以下方法构建地图:

void UserEvents_MVN::BuildUFPAliasMap()
{
    UserFunctionPointerMap_AddFunction("MEMDUMP_LOAD",  &UserEvents::MEMDUMP_LOAD );
    ...
}

更新,以回应 OP 的评论

另一种选择是在地图中使用std::function

假设您的 map 定义为:

using FunctionMap = std::map<std::string, std::function<void(UserEvent*, std::vector<boost::any>&)>>;
FunctionMap myFunctionMap;

然后,您可以使用lambda 函数来充实myFunctionMap

void UserEvents_MVN::BuildUFPAliasMap()
{
    UserFunctionPointerMap_AddFunction("MEMDUMP_LOAD",
                                        [](UserEvent* event, std::vector<boost::any>& parameters))
                                        { dynamic_cast<UserEvents_MVN*>(event)->MEMDUMP_LOAD(parameters);});
    ...
}

【讨论】:

  • 但这会使我将类抽象化的原因无效。由于UserEvents 可以表示许多不同的自定义用户事件(可以是模拟简单的计算机或微分方程求解器或城市交通系统或......)我希望它尽可能通用,将问题特定函数留给派生类。
【解决方案2】:

我在 IRC 中寻求帮助,有人将我指向 CRTP - Curiously Recurring Template Pattern,它允许 Base 中的方法访问 Derived 的成员,这就是我的目标。

// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
    // methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
    // ...
};

他们好心地给我发了example

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

template <typename T>
class Dispatch
{
    std::unordered_map<std::string, void (T::*)()> commands;

    protected: // used by application type only
    void Add(std::string name, void (T::*fptr)())
    {
        commands[name] = fptr;
    }

    public: // becomes part of the public interface for application type
    /* This part is the only reason to use CRTP/inheritance. The application
       type could simply create this method itself and just call functions of
       a Dispatch<App> member to do all the work, but this approach saves us
       from typing that out. */
    void Choose(std::string s)
    {
        auto fptr = commands.at(s);
        auto target = static_cast<T*>(this);
        (target->*fptr)();
    }
};

class App : public Dispatch<App>
{
public:
    App()
    {
        // for demo purposes
        Add("foo", &App::Test);
    }

    void Test()
    {
        std::cout << "dispatched!\n";
    }
};

int main()
{
    App a;
    a.Choose("foo");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-04
    • 2014-12-15
    • 2021-01-29
    • 1970-01-01
    • 2013-06-24
    • 2012-01-17
    相关资源
    最近更新 更多