0

This is related to my last post that you can find here: Creating an unordered_map of std::functions with any arguments. I have now gone ahead and extended this out to classes. So let's say I have three different classes. And these classes all have different methods except for getVariable() and setVariable(int). So for this example we will class them ClassA, ClassB, and ClassC.

I also have a base class which I want to use as my driver. Essentially, if I want to set the variable between ClassA and ClassC I would call the base class' setVariable function.

#ifndef BASE_CLASS_HPP
#define BASE_CLASS_HPP

#include <unordered_map>
#include <functional>
#include <utility>
#include <string>
#include <any>

template<class A, class B>
class BaseClass
{
  public:
    BaseClass() { bindThem(); }

    std::pair<int,int> getValue()
    {
      // return classA and ClassB's values
    }

    void setValue(int newVal)
    {
      auto iter = functions.equal_range("setValue");
      std::any_cast<void(*)(int)>(mapIter->second)(newVal);
    }
  private:
    std::unordered_multimap<std::string,std::any> functions;

    void bindThem()
    {
      functions.emplace("setValue",&A::setValue);
      functions.emplace("setValue",&B::setValue);
      functions.emplace("getValue",&A::getValue);
      functions.emplace("getValue",&B::getValue);
    }

};

I then have in main:

#include <iostream>

#include "baseClass.hpp"
#include "classA.hpp"
#include "classB.hpp"
#include "classC.hpp"

int main()
{
  ClassA a;
  ClassB b;
  ClassC c;

  c.setValue(20);

  BaseClass<ClassA,ClassB> base1;
  BaseClass<ClassA,ClassC> base2;

  base1.setValue(15);

  auto values = base1.getValues();


}

I can place the functions withing my map, however, when I try to any_cast I don't get anything in return. I also tried:

std::any_cast<void(A::*)(int)>(mapIter->second)(newVal);

But that also gives me a compiler error of must use .* or ->* and I have tried everything to get it to compile and I don't really know what I am doing wrong. I also realized, if I called it that way, then I wouldn't be able to access B's setVariable function since I am using A's namespace.

Is there anyway I can get this to work how I want it to? I am essentially trying to modify those class values without having to make any copies of those classes and instead directly modify them from within this driver.

Sailanarmo
  • 1,139
  • 15
  • 41
  • You're trying to call member functions but I can't understand on what objects they are supposed to be called. – Dmitry Gordon Apr 05 '19 at 15:47
  • @DmitryGordon I am trying to call the member functions of the classes that I pass in as templates. I think that is what you are asking? – Sailanarmo Apr 05 '19 at 15:49
  • To call a member function you need to specify an instance of the object to be called. For example: to call `ClassA::setValue` you need and instance of `ClassA` class. `a.setValue(15)` under the hood is converted to A::setValue(&a, 15). So to call this value you need to give two arguments to a function pointer – Dmitry Gordon Apr 05 '19 at 15:56
  • @DmitryGordon I understand that. However, I was tying to figure out how to access the member function in this specific way with `std::any_cast` The goal, theorectically, is to use the multimap to access the key, and modify the values that belong to that key in one swoop. However, the cast isn't working and I don't know how to get it to work. – Sailanarmo Apr 05 '19 at 15:59

1 Answers1

0

I still don't quite understand the purpose of such structure, but here an option how to make it at least compile:

#include <unordered_map>
#include <functional>
#include <utility>
#include <string>
#include <any>

template<class A, class B>
class BaseClass
{
  public:
    BaseClass() { bindThem(); }

    std::pair<int,int> getValue()
    {
      auto range = functions.equal_range("getValue");
      return 
        {
          (a.*std::any_cast<int(A::*)()>(*range.first))(),
          (b.*std::any_cast<int(B::*)()>(*range.second))()
        };
    }

    void setValue(int newVal)
    {
      auto range = functions.equal_range("setValue");
      (a.*std::any_cast<void(A::*)(int)>(*range.first))(newVal);
      (b.*std::any_cast<void(B::*)(int)>(*range.second))(newVal);
    }
  private:
    std::unordered_multimap<std::string,std::any> functions;

    void bindThem()
    {
      functions.emplace("setValue",&A::setValue);
      functions.emplace("setValue",&B::setValue);
      functions.emplace("getValue",&A::getValue);
      functions.emplace("getValue",&B::getValue);
    }

    A a;
    B b;
};

class ClassA
{
public:
    void setValue(int){}
    int getValue() {return 0;}
};

class ClassB
{
public:
    void setValue(int){}
    int getValue() {return 1;}
};

int main()
{
    BaseClass<ClassA, ClassB> x;
    x.setValue(3);
    auto i = x.getValue();
}

Please note several things:

  1. I've added members to BaseClass since to call member functions you need an object to be called on.
  2. I'm using first and last iterators of the range from equal_range, but the order of elements in that range is implementation defined. So to make things work you need to take care of distinguishing which container element corresponds to class A and which to class B.
Dmitry Gordon
  • 2,229
  • 12
  • 20