gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > Pointer to member function problem

#103379 - Mucca - Thu Sep 21, 2006 2:00 pm

Having taken a look recently at more powerful languages such as Haskell and OCaml, I began to yearn for such generic programming capabilities in C++, and decided to see what I could muster up with templates. So anyway, I have a situation where I need to iterate across an array of numbers, locate an object based on that number, and call a member function passing the located object as a parameter. Seeing as I would probably need roughly the same functionality in a number of classes, I decided to try to factor the code into a templatized static function, taking a template class, the array of numbers, and, crucially a pointer to a member function of the templatized class. Basically what I was trying to achieve was a c++ version of Haskell's map function, albeit with limitations on the function parameter.

Code:

class EntityFactory{
         ..

   // Map a member function of an object of class TYPE taking an Entity* parameter to all
   // entity ids in list. Level is used to find entities
   template<class TYPE>
   static void
   MapMethodToEntityIDList            (   TYPE * caller,
                                 void (TYPE::*Method)(Entity*) method,
                                 const ID* idList,
                                 Level* level )
   {
      for( int i=0; i<refList->numItems; i++ )
      {
         Entity* e = level->FindEntity( idList[i] );
         caller->*method(e);
      }
   }

};


I subsequently tried to call the method

Code:


SyncEntity::Init()
{
    /// Call AddObject for each id in list
    EntityFactory::MapMethodToEntityIDList<SyncEntity>( this, SyncEntity::*AddObject, this->idList, this->level );

}


Sadly, as you may have guessed by now, it failed to compile, complaining about an error before the comma in the line containing the member function parameter to MapMethodToEntityIDList.

Anyway, Im using, and am stuck using, gcc 2.9.5, and I was just wondering if the problem lies with my compiler, or with my syntax, or with the idea itself? Are pointers to member functions supported at all? Or are they just a myth? Of course I could make it a pointer to a static function, pass my object, cast, and call my member function, but that just doesnt look very nice. Obviously this is a trivial example, its every bit as easy to write the loop everywhere its needed, but its definitely a useful technique, especially if more complicated processing of the idList is required.

#103406 - poslundc - Thu Sep 21, 2006 5:08 pm

You can do pointers-to-members, but I couldn't say if they are bug-free in GCC 2.

Pretty much the best guide to function pointers around.

Dan.

#103411 - Mucca - Thu Sep 21, 2006 5:44 pm

A nice tutorial, but it doesnt even mention the member pointer operators, ::* , .*, and ->*. Its solution for such circumstances is to have a static caller function, and pass a pointer to that, which, as I mentioned previously is less elegant, and frankly not worth the hassle.

Hmm, if I write the function as such:

Code:

MapMethodToEntityIDList            (   TYPE * caller,
                                 void (TYPE::*Method)(Entity*),
                                 IDList* idList,
                                 Level* level )
   {
      
      for( int i=0; i<idList->numItems; i++ )
      {
         Entity* e = level->FindEntity( idList[i] );
         caller->*(TYPE::*Method)(e);
      }
   }


ie, not naming the member function pointer parameter, the compiler accepts it, but reports a parse error before * in the line where I call the member function, which I consider progress :), esp. as thats the last real line in the function

Reading in The C++ Programming Language, Stroustrap presents this technique, albeit not combined with templates. He does however show a nice call-by-name technique using member-function pointers, and surprisingly says that even the address of virtual member functions can be taken. Cool.

#103413 - tepples - Thu Sep 21, 2006 6:08 pm

Mucca wrote:
Its solution for such circumstances is to have a static caller function, and pass a pointer to that, which, as I mentioned previously is less elegant, and frankly not worth the hassle.

It is worth it if you're trying to interface a library in the C language with C++ classes.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#103416 - sajiimori - Thu Sep 21, 2006 7:46 pm

Check out boost::bind. In fact, check out Boost in general. You may find that many of these problems are already solved.

Edit: Quick example. I didn't name the function "map" because map returns the new list and has no side-effects.
Code:
template<class List, class Function>
void for_each(List* list, const Function& f)
{
  for(List::iterator it = list->begin(); it != list->end(); ++it)
    f(&*it);
}

void set_to_product(int* dest, int rv)
{
  *num = *num * rv;
}

void print_num(int i)
{
  std::cout << i << ' ';
}

void test()
{
  std::vector<int> v;

  v.push_back(5);
  v.push_back(12);

  int multiply_by = 2;
  for_each(&v, boost::bind(set_to_product, _1, multiply_by));

  // Outputs "10 24 ".
  for_each(&v, print_num);
}

With boost::lambda, the two for_each calls can be:
Code:
  for_each(&v, *_1 = *_1 * _2);
  for_each(&v, cout << _1 << ' ');

#103431 - Mucca - Thu Sep 21, 2006 10:52 pm

Boost is indeed very interesting, it seems to bring much of the power of other higher level languages to C++, and if I were developing for PC I would definitely use it (although for a PC I might just use another language). Unfortunately I forsee major difficulties getting the libraries to compile for, let alone run on GBA. I dont use the STL, I fear what it would do to my poor little heap, and boost I imagine is even more aggressive.

#103440 - sajiimori - Fri Sep 22, 2006 12:03 am

GCC has no trouble with Boost, so if you're using GCC, then Boost will compile.

The bind class doesn't do dynamic allocation. If you're concerned about what a particular utility does, the best thing to do is read the source or connect a debugger to it -- no point in being afraid of the dark just because you're wearing sunglasses indoors. ;)

Edit: Just noticed you're using a pretty old version of GCC. I don't know how well it'll get along with Boost.