views:

1732

answers:

5

How do i pass a pointer to a member function to std::list.sort()?

Is this possible? Thanks

struct Node {
       uint32_t ID;
       char * Value;
};

class myClass {
          private:
            uint32_t  myValueLength;
          public:
            list<queueNode *> MyQueue;
            bool compare(Node * first, Node * second);
            bool doStuff();
}

bool myClass::compare(Node * first, Node * second) {
    unsigned int ii =0;
    while (ii < myValueLength)
    {
     if (first-> Value[ii] < second-> Value[ii]) 
     {
      return true;
     } else if (first-> Value[ii] > second-> Value[ii])
     {
      return false;
     }

     ++ii;
    }

    return false;
}

bool myClass::doStuff()
{
    list.sort(compare);
}

I want to use a length variable from within the class instead of doing strlen() within the compare function (The Value will always be the same length)

Edit: The myValueLength was not the only variable i wanted to access from within the comparison function I just simplified it to make the example shorter.

+3  A: 

You may want to use a functor.

http://www.newty.de/fpt/functor.html

grieve
+1 for having the right idea first. :)
Mr.Ree
+1  A: 

Note that std::list sorts the element according to operator< defined for that element. You need to change your compare function to use a global operator< defined for Node objects:

bool operator<(Node const& first, Node const& second) {
unsigned int ii =0;
while (ii < length)
{
    if (first.Value[ii] < second.Value[ii]) 
    {
            return true;
    } else if (first.Value[ii] > second.Value[ii])
    {
            return false;
    }

    ++ii; 
}

return false;

}

A suggested improvement will be:

bool operator<(Node const& first, Node const& second) {
    for (size_t ii =0; first.Value[ii] == second.Value[ii]; ++ii) ; // note ;
    return (first.Value[ii] < second.Value[ii]);
}

If char *Value really represents a C-style string, and you want lexicographic sorting, further improvements are possible:

bool operator<(Node const& first, Node const& second) {
     return (strcmp(first.Value, second.Value) < 0);
}

and if they are really strings, I suggest you use std::string and you can write:

bool operator<(Node const& first, Node const& second) {
     return first.Value < second.Value;
}
dirkgently
Ben Reeves
My bad -- copy/paste error :P Use global operator< for Node. See updated post. Also, the length should be part of the Node class, it doesn't make sense otherwise.
dirkgently
You can always use strncmp which compares at most the first n chars.
Greg Rogers
@Ben: Greg just beat me to my second suggestion :)
dirkgently
+6  A: 

It is possible. Did you consider using boost::function?

list.sort( boost::bind( &myClass::compare, this, _1, _2 ) );

Is your 'compare' function will rely on this data? If not - you may simpy make 'compare' function to be static. And then it will be

list.sort( &myClass::compare );

You can add helper struct to do your comparison and then

list.sort( Comparer( myValueLength ) );

struct Comparer
{
    Comparer( uint32_t myValueLength ):
        length( myValueLength )
    {}

    bool operator() (Node * first, Node * second)
    {
        unsigned int ii =0;
        while (ii < length)
        {
            if (first-> Value[ii] < second-> Value[ii]) 
            {
                    return true;
            } else if (first-> Value[ii] > second-> Value[ii])
            {
                    return false;
            }

            ++ii;
        }

        return false;
    }


    uint32_t length;
};
Mykola Golubyev
@Dustin Getz: yes :)
Mykola Golubyev
Thanks, but i don't want to rely on boost as a dependancy
Ben Reeves
+3  A: 

Elaborating on grieve's response, why not use a functor? E.g.:

struct Functor
{
  bool operator()( char * a, char * b )
    { return strcmp(a,b) < 0; }
};

Then you could just use:

Functor f;
myList.sort(f);

You could even use your class as the Functor by defining operator()...

class myClass {
  ...
  bool operator()( queueNode * a, queueNode * b )
  { return compare( a, b ); }

  void doStuff() { MyQueue.sort(*this); }
};


Simple example code:

#include <iostream>
#include <list>
using namespace std;

  // Assumes  TYPE t; cout << t;  is valid.
template<class TYPE>
inline ostream & operator<< ( ostream & theOstream,
                              const list<TYPE> & theList )
{
  typename list<TYPE>::const_iterator listIterator = theList.begin();
  for ( int i = 0;   listIterator != theList.end();  listIterator ++, i ++ )
    theOstream << "    [" << i << "]:   \"" << (*listIterator) << "\"" << endl;
  return theOstream;
}

struct Functor
{
  bool operator()( const char * a, const char * b )
    { return strcmp(a,b) < 0; }
};

int
main()
{
  list<char*>  l;

    /* Load up some example test data... */
  char  s[3];
  s[2] = '\0';
  for (   s[0]='c'; s[0]>='a'; s[0]-- )
    for ( s[1]='c'; s[1]>='a'; s[1]--  )
      l.push_back(strdup(s));

    /* Show us that test data... */
  cout << l << endl;

    /* Sort list. */
  Functor f;
  l.sort(f);

    /* Show us what we have now... */
  cout << l << endl;
}
Mr.Ree
+1 for being less lazy than I. :)
grieve
A: 

As grieve & mrree suggested

simply overloading the () operator works

Thanks everyone who answered

struct Node {
       uint32_t ID;
       char     *       Value;
};

class myClass {
          private:
            uint32_t  myValueLength;
          public:
            list<queueNode *> MyQueue;
            bool operator()(Node * first, Node * second);
            bool doStuff();
}

bool myClass::operator()(Node * first, Node * second) {
    unsigned int ii =0;
    while (ii < myValueLength)
    {
        if (first-> Value[ii] < second-> Value[ii]) 
        {
                return true;
        } else if (first-> Value[ii] > second-> Value[ii])
        {
                return false;
        }

        ++ii;
    }

    return false;
}

bool myClass::doStuff()
{
    list.sort(*this);
}
Ben Reeves