Narzędzia użytkownika

Narzędzia witryny


Pasek boczny

sk2:memberptrs_bind

Wskaźnik na metodą z klasy

QLineEdit * le = new QLineEdit;
 
// wskaźnik na funkcję z klasy:
void (QLineEdit::*leMethod1) () = &QLineEdit::clear;
// wywołanie takiego wskaźnika na rzeczy obiektu le:
(le->*leMethod1)();
 
// to samo, z argumentem:
void (QLineEdit::*leMethod2) (bool) = &QLineEdit::setEnabled;
(le->*leMethod2)(true);

Wyjaśnienie:

  • <wynik>          (*)      (<argumenty>), np: void    (*)  (int) ← typ dla zwykłej funkcji
  • <wynik> (<Klasa>::*)      (<argumenty>), np: void (A::*)  (int) ← typ dla metody z obiektu
  • <wynik> (<Klasa>::*nazwa) (<argumenty>), np: void (A::*m) (int) ← zmienna powyższego typu
  • dla zwykłych (nie wirtualnych) metod kompilator wyznacza którą metodę (znajdującą się pod znanym na etapie kompilacji adresem) ma wywołać na rzecz obiektu.
  • dla wirtualnych metod kompilator wylicza pozycję w tabeli metod wirtualnych którą należy wywołać.

Ręczne połączenie metody z obiektem

template <typename T>
struct opakowanie {
    T * obiekt;
    void (T::*metoda) (bool);
    void operator()(bool arg){
        (obiekt->*metoda)(arg);
    }
};
 
void someCode(){
    QLineEdit * le = new QLineEdit;
    opakowanie<QLineEdit> box {le, &QLineEdit::setEnabled};
    box(true);
}

Bind - połączenie metody na rzecz obiektu

#include <functional>
 
void someCode(){
    QLineEdit * le = new QLineEdit;
 
    auto box = std::bind(&QLineEdit::setEnabled, le, std::placeholders::_1);
    box(true);
 
    std::function<void (bool)> func = std::bind(&QLineEdit::setEnabled, le, std::placeholders::_1);
    func(true);
 
    // box i func są różnych typów (func opakowuje box) 
}

Formalnie bind to częściowe wykonanie metody
Dostępny w boost (boost::bind) od 2001 roku, włączony w standard C++11

W środku std::bind w GCC działa podobnie jak powyższy przykład; identyfikacja typu przez gdb:

(gdb) ptype box
type = struct std::_Bind<std::_Mem_fn<void (QWidget::*)(bool)>(QLineEdit*, std::_Placeholder<1>)> [with _Signature = std::_Mem_fn<void (QWidget::*)(bool)> (QLineEdit *, std::_Placeholder<1>)] : public std::_Weak_result_type<std::_Mem_fn<void (QWidget::*)(bool)> > {
  private:
    std::_Mem_fn<void (QWidget::*)(bool)> _M_f;
    std::tuple<QLineEdit*, std::_Placeholder<1> > _M_bound_args;

  public:
    _Bind(const std::_Bind<std::_Mem_fn<void (QWidget::*)(bool)>(QLineEdit*, std::_Placeholder<1>)> &);
    _Bind(<unknown type in /home/user/build-test-Desktop_qt5-Debug/test, CU 0x42cc0, DIE 0x4e548>);
    void operator()<bool, void>(<unknown type in /home/user/build-test-Desktop_qt5-Debug/test, CU 0x42cc0, DIE 0x4e57b>);
    void _Bind<QLineEdit*&, std::_Placeholder<1> const&>(<unknown type in /home/user/build-test-Desktop_qt5-Debug/test, CU 0x42cc0, DIE 0x4e5ae>, QLineEdit *&, const std::_Placeholder<1> &);
  private:
    void __call<void, bool&&, 0ul, 1ul>(<unknown type in /home/user/build-test-Desktop_qt5-Debug/test, CU 0x42cc0, DIE 0x4e605>, std::_Index_tuple<0ul, 1ul>);
}

Bind z ustalenie wartości części argumentów (bez metody z obiektu):

#include <unistd.h>
#include <functional>
using namespace std::placeholders;
void someCode(){
    char buf[255];
 
    std::function<ssize_t (int)> simpleRead =  std::bind(read, std::placeholders::_1, buf, 255);
    simpleRead(0);
 
    std::function<ssize_t (void*, size_t)> stdinRead =  std::bind(read, 0, std::placeholders::_1, std::placeholders::_2);
    stdinRead(buf, 255);
}

Argumenty można podawać przez wartość lub jako referencja opakowane w std::ref / std::cref.

Dlaczego connect był złym przykładem na wskaźnik do funkcji z klasy:

QLineEdit *le = new QLineEdit;
 
// 1) stara składnia connect jest static, nie ma potrzeby definiowania wskaźnika na metodę z klasy:
QMetaObject::Connection (*method1) (const QObject *, const char *, const QObject *, const char *, Qt::ConnectionType) = &QObject::connect;
method1(le, SIGNAL(returnPressed()), le, SLOT(clear()), Qt::AutoConnection);
 
// 2) nowa składnia poza tym ze też jest static, to jest oparta na template'ach, więc trzeba jakiś zrealizować: 
QMetaObject::Connection (*method2) (const QLineEdit *, void (QLineEdit::*)(), const QLineEdit *, void (QLineEdit::*)(), Qt::ConnectionType) = &QObject::connect;
method2(le, &QLineEdit::returnPressed, le, &QLineEdit::clear, Qt::AutoConnection);
 
// 3) lepiej to wygląda z typedef:
typedef QMetaObject::Connection (*newtype) (const QLineEdit *, void (QLineEdit::*)(), const QLineEdit *, void (QLineEdit::*)(), Qt::ConnectionType);
newtype method3 = &QObject::connect;
method3(le, &QLineEdit::returnPressed, le, &QLineEdit::clear, Qt::AutoConnection);
sk2/memberptrs_bind.txt · ostatnio zmienione: 2016/11/22 21:48 przez jkonczak