tags:

views:

227

answers:

8

Pointer to members are not used very much but they are really powerful, how have you used them and what's the coolest things you've done?

Edit: This is not so much to list things that are possible, for example listing boost::bind and boost::function aren't good. Instead maybe a cool usage with them? I know they're very cool in themselves but that's not what this is about.

+1  A: 

Well I use pointers to member functions regularly with standard algorithms. There is nothing special about them as far as I am concerned.

dirkgently
+3  A: 

Pointers to member function are great for creating psuedo lamba expressions with for_each

vector<SomeClass*> v = getAVector();
for_each(v.begin(), v.end(), mem_fun(&SomeClass::print));
JaredPar
You mean mem_fun_ref, don't you?
dirkgently
@dirkgently I thought mem_fun was more appropriate here. checking ...
JaredPar
@JaredPar: mem_fun won't just work if SomeClass is not a pointer
Mykola Golubyev
@Mykola, that's what I was missing. I intended the example to have a pointer type in the vector (read right past it)
JaredPar
+1  A: 

You can bind member variables and functions with boost::bind and get usual functor.
Next work with them will like on usual functions usage:

  • passing as callback or signal function;
  • using in standard algorithms;
  • using as comparator in std::map/set;
  • using as data accessors;
bb
A: 

In addition to the previous, you can for example use them as callback functions.

Gamecat
+3  A: 

The coolest thing I've done with them, I did a long time ago. There's probably a better way to do it today.

I created a self-generating command-line parser for a network management tool. The classes representing the objects to be managed each had their own tables of sub-classes (name, pointer-to-factory-member), instances (id, pointer to instance from a list), and commands (name, pointer to member function). This allowed the parser to handle things like:

SET NETWORK ROUTE 192.168.0.0 HOPS 1

or

QUERY NETWORK NAMESERVER servername

without knowing anything about routes, or name servers.

John Saunders
+5  A: 

I once was needed to operate with criteria data as pure structure to be able to store the list of all criteria in the queue. And I had to bind the structure with the GUI and other filter elements, etc. So I came up with the solution where pointers to members are used as well as pointers to member functions.

Let's say you have an

struct Criteria
{
    typedef std::string Criteria::* DataRefType;
    std::string firstname;
    std::string lastname;
    std::string website;
};

Than you can wrap criteria field and bind with the string representation of the field with

class Field
{
public:
    Field( const std::string& name,
           Criteria::DataRefType ref ):
        name_( name ),
        ref_( ref )
    {}
    std::string getData( const Criteria& criteria )
    {
        return criteria.*ref_;
    }
    std::string name_;
private:
    Criteria::DataRefType ref_;
};

Then you can register all the fields to use whenever you want: GUI, serialization, comparison by field names, etc.

class Fields
{
public:
    Fields()
    {
        fields_.push_back( Field( "First Name", &Criteria::firstname ) );
        fields_.push_back( Field( "Last Name", &Criteria::lastname ) );
        fields_.push_back( Field( "Website", &Criteria::website ) );
    }
    template < typename TFunction >
    void forEach( TFunction function )
    {
        std::for_each( fields_.begin(), fields_.end(),
                       function );
    }
private:
    std::vector< Field > fields_;
};

By calling for instance fields.forEach( serialization ); or

GuiWindow( Criteria& criteria ):
    criteria_( criteria )
{
    fields_.forEach( std::bind1st( 
                         std::mem_fun( &GuiWindow::bindWithGui ),
                         this ) );
}
void bindWithGui( Field field )
{
    std::cout << "name " << field.name_
              << " value " << field.getData( criteria_ ) << std::endl;
};
Mykola Golubyev
A: 

I did it in a "DomainEditor" class for this huge application I wrote. All my type (domain) tables in the database could be edited by the admins of the program and since the clients called some of the types by different names than others did, I made a dialog that allowed you to edit them. Well, I didn't want to make an editor for the 15+ domain types, so I wrote a super-class that I could cast each class to, and using pointers I could make simple calls to each domain table. Each one supported all the same properties, a Description (name), an ID, an Inactive flag, and a Required flag. So, the code started with a Macro to setup my calls:

#define DomainList(Class, Description, First, Next, Item, UpdateItem, DeleteItem, IsItemRequired, MaxLength) { \
   CWFLHandler *handler = new CWFLHandler; \
   handler->pWFL = new Class;\
   handler->LoadFirstType = (LoadFirst)&Class::First;\
   handler->LoadNextType  = (LoadNext)&Class::Next;\
   handler->LoadType      = (Load)&Class::Item;\
   handler->UpdateType    = (Update)&Class::UpdateItem;\
   handler->DeleteType    = (Delete)&Class::DeleteItem;\
   handler->IsRequiredType= (IsRequired)&Class::IsItemRequired; \
   handler->MAX_LENGTH    = MaxLength;\
   PopulateListBox(m_Domain, Description, (long)handler); }\

Then, lots of calls to the Macro: (Here is just a single one)

   DomainList(CConfigWFL,   "Application Parameter Types",        LoadFirstParameterType,                   LoadNextParameterType,                 LoadParameterTypeByTypeId,                                        UpdateParameterType,                DeleteParameterType,                IsParameterTypeRequired,            LEN_APPL_PARAMETER_DESC);

Then, the calls to edit the data were all common, and I didn't have to duplicate any code at all...

For example, to populate the list with the selected item in the DropDownList (populated by the Macro), the code would look like this:

 if((pWFLPtr->pWFL->*pWFLPtr->LoadFirstType)(true))
  {
     do
     {
        m_Grid.AddGridRow();
        m_Grid.SetCheck(COLUMN_SYSTEM,         (pWFLPtr->pWFL->*pWFLPtr->IsRequiredType)(pWFLPtr->pWFL->TypeId));
        m_Grid.SetCheck(COLUMN_STATUS,      pWFLPtr->pWFL->InactiveIndc == false);
        m_Grid.AddTextToGrid(COLUMN_NAME,   pWFLPtr->pWFL->TypeDesc);
        m_Grid.AddTextToGrid(COLUMN_DEBUG,  pWFLPtr->pWFL->TypeId);
        m_Grid.AddTextToGrid(COLUMN_ID,     pWFLPtr->pWFL->TypeId);
     }
     while((pWFLPtr->pWFL->*pWFLPtr->LoadNextType)());

Of, course, this was all stored in a class that was part of the dialog. And I simply created new instances of the class, stored them in the ListBox's ItemData member. So, I did have to clean all that up when the dialog closed.. I left that code out of this message however.

The class to store all this stuff in was defined as:

   typedef bool (CMyWFL::*LoadFirst)(bool);
   typedef bool (CMyWFL::*LoadNext)();
   typedef bool (CMyWFL::*Load)(long);
   typedef bool (CMyWFL::*Update)(long, const char*, bool);
   typedef bool (CMyWFL::*Delete)(long);
   typedef bool (CMyWFL::*IsRequired)(long);

   class CWFLHandler {
   public:
      CWFLHandler()  {};
      ~CWFLHandler() { if(pWFL) delete pWFL; }

      CMyWFL    *pWFL;
      LoadFirst   LoadFirstType;
      LoadNext    LoadNextType;
      Load        LoadType;
      Update      UpdateType;
      Delete      DeleteType;
      IsRequired  IsRequiredType;
      int         MAX_LENGTH;
   };
   CWFLHandler *pWFLPtr;

All this work made it really nice to be able to add new domains to the application with very little work to add them to the domain editor... There may have been a better way, I don't know. But this is the way I went, and it worked VERY well for me, and IMHO, it was very creative... :)

LarryF
A: 

I used them as part of a StructSerlialiser to populate C++ POD structures from SAX Parser events, i.e. to map XML onto a C++ data model.

template<class STRUCT, typename FIELDTYPE>
struct FieldBinderImpl : public FieldBinder<STRUCT>
{
    typedef FIELDTYPE (STRUCT::*MemberPtr);

    FieldBinderImpl (const std::string& tag, MemberPtr memberPtr)
        : FieldBinder (tag)
        , memberPtr_ (memberPtr)
    {
    }

    virtual SerialiserBase* createSerialiser (STRUCT& data) const
    {
        return new Serialiser<FIELDTYPE> (&(data.*memberPtr_));
    }

private:
    MemberPtr memberPtr_;
};

template<class T>
class StructSerialiser : public SerialiserData<T>
{
public:
    typedef std::vector<FieldBinder<T>*> FieldBinderList;

private:
    static FieldBinderList fieldBinderList_;

protected:
    template<typename FIELDTYPE>
    static void bind (const std::string& tag, FIELDTYPE (T::* member))
    {
        fieldBinderList_.push_back (new FieldBinderImpl<T, FIELDTYPE> (tag, member));
        if (tag.empty ())
            fieldBinderList_.back ()->tags_ = Serialiser<FIELDTYPE>::getTags ();
    }

    // ...
}

// ...

Also had Serialisers for double, strings, vectors, etc. To use it you would just bind struct members to names, e.g.:

class Index
{
public:
    std::string         currency;
    std::string         name;
};

template<>
class Serialiser<Index> : public StructSerialiser<Index>
{
public:
    Serialiser (Index* data) : StructSerialiser<Index> (data) {}

    static void initialise ()
    {
        bind ("currency", &Index::currency);
        bind ("name", &Index::name);
    }
};
jon hanson