Как лучше всего использовать указатель на члены и функции-члены? - PullRequest
2 голосов
/ 08 апреля 2009

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

Edit: Это не так много, чтобы перечислить возможные варианты, например, перечисление boost :: bind и boost :: function не очень хорошо. Вместо этого может быть классное использование с ними? Я знаю, что они очень крутые сами по себе, но дело не в этом.

Ответы [ 8 ]

5 голосов
/ 08 апреля 2009

Мне когда-то нужно было работать с данными критериев как с чистой структурой, чтобы иметь возможность хранить список всех критериев в очереди. И мне пришлось связать структуру с графическим интерфейсом и другими элементами фильтра и т. Д. Поэтому я нашел решение, в котором используются указатели на элементы, а также указатели на функции-члены.

Допустим, у вас есть

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

Чем можно обернуть поле критериев и связать со строковым представлением поля с помощью

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

Затем вы можете зарегистрировать все поля для использования в любое время: графический интерфейс, сериализация, сравнение по именам полей и т. Д.

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

Позвонив, например, fields.forEach( serialization ); или

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;
};
3 голосов
/ 08 апреля 2009

Самая крутая вещь, которую я сделал с ними, я сделал очень давно. Возможно, сегодня есть лучший способ сделать это.

Я создал самогенерирующий парсер командной строки для инструмента управления сетью. Классы, представляющие объекты, которыми нужно управлять, имеют свои собственные таблицы подклассов (имя, указатель на элемент-фабрика), экземпляры (идентификатор, указатель на экземпляр из списка) и команды (имя, указатель на функцию-член ). Это позволило парсеру обрабатывать такие вещи, как:

SET NETWORK ROUTE 192.168.0.0 HOPS 1

или

QUERY NETWORK NAMESERVER servername

, не зная ничего о маршрутах или серверах имен.

3 голосов
/ 08 апреля 2009

Указатели на функцию-член отлично подходят для создания выражений psuedo lamba с помощью for_each

vector<SomeClass*> v = getAVector();
for_each(v.begin(), v.end(), mem_fun(&SomeClass::print));
1 голос
/ 08 апреля 2009

Вы можете связать переменные и функции-члены с помощью boost :: bind и получить обычный функтор.
Следующая работа с ними понравится при использовании обычных функций:

  • передача в качестве функции обратного вызова или сигнала;
  • использование в стандартных алгоритмах;
  • использование в качестве компаратора в std :: map / set;
  • использование в качестве средств доступа к данным;
1 голос
/ 08 апреля 2009

Ну, я регулярно использую указатели на функции-члены со стандартными алгоритмами. Насколько я понимаю, в них нет ничего особенного.

0 голосов
/ 09 апреля 2009

Я использовал их как часть StructSerlialiser для наполнения структур C ++ POD из событий SAX Parser, то есть для сопоставления XML с моделью данных C ++.

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

    // ...
}

// ...

Также есть Serialisers для double, строк, векторов и т. Д. Чтобы использовать его, вы просто привязываете элементы структуры к именам, например ::1004*

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);
    }
};
0 голосов
/ 08 апреля 2009

Я сделал это в классе "DomainEditor" для этого огромного приложения, которое я написал. Все таблицы моего типа (домена) в базе данных могут редактироваться администраторами программы, и, поскольку клиенты называют некоторые типы по именам, отличным от имен других, я создал диалог, который позволил вам их редактировать. Ну, я не хотел создавать редактор для 15+ типов доменов, поэтому я написал суперкласс, к которому я мог бы привести каждый класс, и используя указатели, я мог бы делать простые вызовы для каждой таблицы доменов. Каждый из них поддерживает одинаковые свойства, описание (имя), идентификатор, флаг неактивности и флаг обязательности. Итак, код начался с макроса для настройки моих вызовов:

#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); }\

Затем много обращений к макросу: (вот только один)

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

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

Например, чтобы заполнить список выбранным элементом в DropDownList (заполняется макросом), код должен выглядеть следующим образом:

 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)());

Конечно, все это было сохранено в классе, который был частью диалога. И я просто создал новые экземпляры класса, сохранил их в элементе ItemData ListBox. Поэтому мне пришлось все это очистить, когда диалоговое окно закрылось ... Однако я оставил этот код вне этого сообщения.

Класс для хранения всего этого был определен как:

   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;

Благодаря всей этой работе было действительно приятно иметь возможность добавлять новые домены в приложение с очень небольшим трудом, чтобы добавить их в редактор доменов ... Возможно, был лучший способ, я не знаю. Но я пошел этим путем, и он работал ОЧЕНЬ хорошо для меня, и ИМХО, это было очень креативно ...:)

0 голосов
/ 08 апреля 2009

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

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