-2

TL;DR How can I get the Base::Choose to accept Derived::CustomUserFunction as an argument? The error I'm getting, and I do understand why I get it, but I don't know how to resolve it is: 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);
    }

Full version

I'm writing a Discreet Event Simulation Engine (DESEngine) that uses custom User Functions. Since these Custom User Functions are problem specific, I want to be able to call them without knowing their signature name. The engine reads TURN_ON in a text file and looks for the function pointer mapped to the TURN_ON string alias in an unordered_map.

Basically DESEngine would call upon UserEvents::Choose(std::string Name) that would call a function given by it's event name (string).

Everything was working great, but manageability is getting tough since I have to use the engine for lots of different problems (from differential equation solving to simulating simple computers). (And I had all sorts of different problem specific functions inside my UserEvents class)

Because of that, I've decided to make my class UserEvents an abstract class and inherit it for each kind of problem I have, thus, custom user functions would be stored in the derived class, not in the base, but I want the "Choose" method to reside in the base class. How can I call the functions within the derived classes from the base class?

The error I'm getting, and I do understand why I get it, but I don't know how to resolve it is: 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;
    };

Here's the implementation of 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;
        }
    }

An example of a derived class

#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();
};

And how I build the index of function pointers (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);
}
Pedro
  • 74
  • 1
  • 11
  • What? You are trying to store pointers as pointers to members of `UserEvents`, but those functions are not members of that class. – Bo Persson Nov 07 '17 at 20:53
  • @BoPersson you are correct. I want the base class to handle the unordered_map that stores the function pointers but I want them to be of functions of it's derived classes. How can I work around this? – Pedro Nov 07 '17 at 20:59
  • Would be great to know why it was down voted so I could do better next time :) – Pedro Nov 22 '17 at 23:54

2 Answers2

0

One way to resolve the problem is define virtual member functions in UserEvents and use them in building the map.

Example:

class UserEvents
{   
  public:

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

 ...
};

and then override the virtual functin in UserEvents_MVN.

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

  protected:

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

  ...
};

and build the map using:

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

Update, in response to OP's comment

The other option is to use std::function in the map.

Let's say your map is defined as:

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

Then, you can use lambda functions to flesh out myFunctionMap.

void UserEvents_MVN::BuildUFPAliasMap()
{
    UserFunctionPointerMap_AddFunction("MEMDUMP_LOAD",
                                        [](UserEvent* event, std::vector<boost::any>& parameters))
                                        { dynamic_cast<UserEvents_MVN*>(event)->MEMDUMP_LOAD(parameters);});
    ...
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • But that would invalidate the reason why I made the class abstract. Since `UserEvents` can represent MANY different custom user events (It could be to simulate a simple computer or a differential equation solver or a city traffic system or ...) I want it as general as possible, leaving problem specific functions for the derived classes. – Pedro Nov 07 '17 at 21:01
0

I sought out help in IRC to which someone pointed me to CRTP - Curiously Recurring Template Pattern which allows methods within Base to access members of Derived and that is what I aim for.

// 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>
{
    // ...
};

And they kindly sent me an 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");
}
Pedro
  • 74
  • 1
  • 11