Хранить указатели на функцию-член на карте - PullRequest
8 голосов
/ 10 марта 2009

Я хотел бы отобразить строку на функции-члены экземпляра и сохранить каждое отображение на карте.

Как правильно сделать что-то подобное?

class  MyClass
{
   //........
   virtual double GetX();
   virtual double GetSomethingElse();
   virtual double GetT();
   virtual double GetRR();
   //........
};


class Processor
{
 private:
      typedef double (MyClass::*MemFuncGetter)();
      static map<std::string, MemFuncGetter> descrToFuncMap;

 public:
        static void Initialize();
        void Process(Myclass m, string);
};

void Processor::Initialize()
{

     descrToFuncMap["X"]=&MyClass::GetX;
     descrToFuncMap["SomethingElse"]=&MyClass::GetSomethingElse;
     descrToFuncMap["RR"]=&MyClass::GetRR;
     descrToFuncMap["T"]=&MyClass::GetT;
};
void Processor::Process(MyClass ms, const std::string& key)
{
     map<std::string, Getter>::iterator found=descrToFuncMap.find(key);
     if(found!=descrToFuncMap.end())
     {
        MemFuncGetter memFunc=found->second;
        double dResult=(ms).*memFunc();    
        std::cout<<"Command="<<key<<", and result="<<result<<std::end;      
      }
 }  

дайте мне знать, если вы видите проблему с этим подходом и каковы общие идиомы для этого?

Возможно, мне следует использовать цепочку операторов if-else-if, учитывая, что у меня ограниченное число функций-членов, вместо запутанной карты функциональных указателей

Кстати, я нашел некоторую полезную информацию здесь в c ++ - faq-lite

Ответы [ 3 ]

6 голосов
/ 10 марта 2009

выглядит хорошо для меня, но тот факт, что descrToFuncMap необходимо объявить static, если вы собираетесь инициализировать его из статической функции Initialize().

Если вы хотите убедиться, что Initialize() вызывается и вызывается только один раз, вы можете использовать шаблон Singleton. По сути, если вы не выполняете многопоточность, это просто означает, что descrToFuncMap оборачивается внутри своего собственного класса (например, скажем FuncMap) с помощью закрытого конструктора, который вызывает Initialize(). Затем вы добавляете static локальную переменную типа FuncMap к Processor::Process() - поскольку переменная static, она сохраняется и инициализируется только один раз.

Пример кода (теперь я понимаю, что friend здесь на самом деле не нужен):

class Processor {
private:
    typedef double (MyClass::*MemFuncGetter)();

    class FuncMap {
    public:
        FuncMap() {
            descrToFuncMap["X"]=&MyClass::GetX;
            descrToFuncMap["SomethingElse"]=&MyClass::GetSomethingElse;
            descrToFuncMap["RR"]=&MyClass::GetRR;
            descrToFuncMap["T"]=&MyClass::GetT;
        }

        // Of course you could encapsulate this, but its hardly worth
        // the bother since the whole class is private anyway.
        map<std::string, MemFuncGetter> descrToFuncMap;
    };

public:
    void Process(Myclass m, string);
};

void Processor::Process(MyClass ms, const std::string& key) {
    static FuncMap fm;      // Only gets initialised on first call
    map<std::string, Getter>::iterator found=fm.descrToFuncMap.find(key);
    if(found!=fm.descrToFuncMap.end()) {
        MemFuncGetter memFunc=found->second;
        double dResult=(ms).*memFunc();    
        std::cout<<"Command="<<key<<", and result="<<result<<std::end;      
    }
}

Это не «истинный» шаблон Singleton, поскольку различные функции могут создавать свои собственные, отдельные экземпляры FuncMap, но этого достаточно для того, что вам нужно. Для «истинного» Singleton вы бы объявили конструктор FuncMap закрытым и добавили статический метод, скажем getInstance(), который определил единственный экземпляр как переменную static и вернул ссылку на него. Processor::Process() будет использовать это с

FuncMap& fm = FuncMap::getInstance();
0 голосов
/ 11 марта 2009

Избегайте использования 'virtual', если вы используете карты указателей функций. В этом контексте использование виртуального ключевого слова мало чем поможет. Например

descrToFuncMap["X"]=&MyClass::GetX;

всегда будет вызывать функцию ' MyClass :: GetX ', даже если GetX переопределено производным классом MyClass.

Обычно у вас не будет большого количества функций в классе, вместо использования map вы можете создать простой массив struct и использовать цикл for. Если количество функций невелико, не будет большой разницы в производительности карты и массива. Нечто похожее на код ниже будет работать

class  MyClass
{
   //........
   double GetX();
   double GetSomethingElse();
   double GetT();
   double GetRR();
   //........
};

typedef double (MyClass::*MemFuncGetter)();

struct FuncTable
{
    const char* m_pFuncName;
    MemFuncGetter m_pFuncPtr;
};

class Processor
{          
 public:
        void Process(Myclass& m, string);
};

static FuncTable descrToFuncMap[]
{
    { "X",  &MyClass::GetX},
    { "SomethingElse", &MyClass::GetSomethingElse },
    { "RR", &MyClass::GetRR},
    { "T", &MyClass::GetT}
};

void Processor::Process(MyClass& ms, const std::string& key)
{
    int functablesize = sizeof(descrToFuncMap)/sizeof(descrToFuncMap[0])

    for(int i=0; i< functablesize; ++i)
    {   
        if( strcmp(key.c_str(), descrToFuncMap[i].m_pFuncName)==0)
        {
            MemFuncGetter memFunc=descrToFuncMap[i].m_pFuncPtr;
            double dResult=(ms).*memFunc();    
            std::cout<<"Command="<<key<<"result="<<result<<std::end;
            break;
        }
    }     
 }
0 голосов
/ 10 марта 2009

Я бы изменил

void Processor::Process(MyClass ms, std::string key)

до

void Processor::Process(const MyClass& ms, const std::string& key)

Пока не вижу побочных эффектов. Возможно, с boost :: function в качестве значения карты это будет легче в будущем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...