Възможно ли е да се запази указател към функционален член от производен в друг клас, използван от базов клас

По принцип имам клас, да кажем Parameter, който има променлива get и set.

Също така имам базов клас, да кажем Vehicle, който има метод registerParameter(...), който взема указател към функционален член като getter и указател към функционален член като setter. След това този метод трябва да запише тези два указателя в обект от параметърния клас и да хвърли този обект във вектор.

И накрая, но не на последно място, имаме производен клас, да речем Car и извикваме registerParameter(...) с низ "color" като име на параметър и получател и сетер от този производен клас.

Пример в кода:

Файл с параметри

#ifndef BASE_H
#define BASE_H
#include "base.h"

class Parameter
{
    std::string (Base::*get)();
void (Base::*set)(std::string);
};

#endif

Базов файл

#ifndef PARAMETER_H
#define PARAMETER_H
#include <vector>
#include "parameter.h"

class Base
{
  public:
std::vector<Parameter> list;
void registerNew(std::string (Base::*get)(), void (Base::*set)(std::string))
    {
        Parameters parameter;
        parameter.get = get;
        parameter.set = set;
        list.push_back(parameter);
}
};

#endif

Изведен файл

class Derived
{
  public:
    Derived derived() 
    {
        registerNew(&getColor, &setColor);
    }

    std::string getColor()
    {
        return this->color;
    }

    std::string setColor(std::string newColor)
    {
        this->color = newColor;
    }
  private:
    std::string color;
};

Мисля за това от дни и наистина се нуждая от решението до петък вечерта.


person Pascal Neubert    schedule 03.07.2014    source източник
comment
Значи проблемът ви е, че getColor и setColor не са приемливи в registerNew? Винаги можете да разхлабите малко правилата и вместо да получите строг указател на функция, просто да накарате функцията да получи void*, но тогава, когато искате да я използвате, ще трябва да я преобразувате правилно   -  person in need of help    schedule 03.07.2014
comment
Въпреки липсващия въпросителен знак в заглавието на въпроса, той казва какво ми трябва.   -  person Pascal Neubert    schedule 03.07.2014
comment
@inneedofhelp: Това би било много лоша идея. Много по-добре е да вземете std::function<void(std::string)> и std::bind Derived*.   -  person MSalters    schedule 03.07.2014


Отговори (2)


Не можете да направите това, което се опитвате:

Типовете std::string (Base::*)() и std::string (Derived::*)() са много различни. std::string (Derived::*)() не може да се преобразува автоматично в std::string (Base::*)().

Вземете следния сценарий.

struct Base
{
    int foo() { return 10; }
};

struct Derived : Base
{
    int bar() { return 20; }
};

int main()
{
    Base base;

    int (Base::*bf)() = &Base::foo;
    (base.*bf)(); // Should be able to call Base:foo(). No problem.

    bf = &Derived::bar; // This is a compiler error. However, if this were allowed....
    (base.*bf)(); // Call Derived::bar()?? That will be a problem. base is not an
                  // instance of Derived.
}

Актуализация

Можете да направите нещо като:

#include <string>
#include <vector>

class Base;

// Create a base class Functor that provides the interface to be used by
// Base.
struct Functor
{
   virtual ~Functor() {}
   virtual std::string get(Base& base) = 0;
   virtual void set(Base& base, std::string) = 0;
};

// Create a class template that implements the Functor interface.
template <typename Derived> struct FunctorTemplate : public Functor
{
   // typedefs for get and set functions to be used by this class.
   typedef std::string (Derived::*GetFunction)();
   typedef void (Derived::*SetFunction)(std::string);

   // The constructor that uses the get and set functions of the derived
   // class to do itw work.
   FunctorTemplate(GetFunction get, SetFunction set) : get_(get), set_(set) {}

   virtual ~FunctorTemplate() {}

   // Implement the get() function.
   virtual std::string get(Base& base)
   {
      return (reinterpret_cast<Derived&>(base).*get_)();
   }

   // Implement the set() function.
   virtual void set(Base& base, std::string s)
   {
      (reinterpret_cast<Derived&>(base).*set_)(s);
   }

   GetFunction get_;
   SetFunction set_;
};

class Base
{
   public:
      std::vector<Functor*> functorList;

      void registerFunctor(Functor* functor)
      {
         functorList.push_back(functor);
      }
};

class Derived : public Base
{
  public:
    Derived() 
    {
       // Register a FunctorTemplate.
       registerFunctor(new FunctorTemplate<Derived>(&Derived::getColor, 
                                                    &Derived::setColor));
    }

    std::string getColor()
    {
        return this->color;
    }

    void setColor(std::string newColor)
    {
        this->color = newColor;
    }
  private:
    std::string color;
};
person R Sahu    schedule 03.07.2014
comment
Има ли алтернатива? Или решение за заобикаляне на този проблем? - person Pascal Neubert; 03.07.2014

Вашият базов клас трябва да знае производния клас. Звучи сложно, но проблемът вече е решен:

template<typename DERIVED> class Base
{
public:
   class Parameter {
     std::string (DERIVED::*get)();
     void (DERIVED::*set)();
   };
private:
   std::list<Parameter> list;
   // ...
};

class Derived : public Base<Derived> // !!!
{
   registerNew(&Derived::getColor, &Derived::setColor);
};

Това решение е известно като странно повтарящ се шаблонен шаблон (CRTP).

person MSalters    schedule 03.07.2014
comment
@PascalNeubert: Не е необходимо, просто е удобно (спестява повтарянето на параметъра на шаблона). - person MSalters; 03.07.2014