Wednesday, April 13, 2011

Store pointers to member function in the map.

I'd like to map string to an instance member functions, and store each mapping in the map.

What is the clean way of doing something like that?

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;      
      }
 }

let me know if you see a problem with this approach and what are common idioms for that?

Perhaps, I should use if-else-if statement chain, given that I have a limited number of member functions, instead of a confusing map of func pointers

BTW, I found some of the useful info here in the c++-faq-lite

From stackoverflow
  • I'd change

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

    to

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

    Don't see any bad side effect for now. Probably with boost::function as a map value it will be easier in the future.

    Mykola Golubyev : Maps are ok for me.
  • Looks fine to me, but for the fact that descrToFuncMap needs to be declared static if you intend to initialise it from inside the static function Initialize().

    If you want to make sure that Initialize() gets called, and gets called just once, you can use the Singleton pattern. Basically, if you aren't doing multithreading, that just means wrapping descrToFuncMap inside its own class (called say FuncMap) with a private constructor that calls Initialize(). Then you add a static local variable of type FuncMap to Processor::Process() -- because the variable is static, it persists and is only initialised once.

    Example code (I now realise that friend isn't really necessary here):

    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;      
        }
    }
    

    This is not the "true" Singleton pattern as different functions could create their own, separate instances of FuncMap, but it's enough for what you need. For "true" Singleton, you would declare FuncMap's constructor private and add a static method, say getInstance(), which defined the one-and-only instance as a static variable and returned a reference to that. Processor::Process() would then use this with

    FuncMap& fm = FuncMap::getInstance();
    
  • Avoid using 'virtual' if you are using maps of function pointers. In this context, using 'virtual' keyword will not help much. For example

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

    will always call 'MyClass::GetX' function even if GetX is overridden by the derived class of MyClass.

    Usually you won't have large number of functions in class, rather than using map you can create simple struct array and use a for loop. If the number of functions are small, there won't be any big performance difference in map and array. Something similar to code below will work

    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 comments:

Post a Comment