Apa yang dimaksud dengan delegasi C ++?

147

Apa ide umum delegasi di C ++? Untuk apa mereka, bagaimana mereka digunakan dan untuk apa mereka digunakan?

Pertama-tama saya ingin mempelajarinya dengan cara 'kotak hitam', tetapi sedikit informasi tentang nyali hal-hal ini juga bagus.

Ini bukan C ++ paling murni atau terbersih, tapi saya perhatikan bahwa basis kode tempat saya bekerja memiliki mereka dalam kelimpahan. Saya berharap untuk cukup memahami mereka, jadi saya hanya bisa menggunakannya dan tidak harus menyelidiki keasyikan templat bersarang yang mengerikan.

Dua artikel The Code Project ini menjelaskan apa yang saya maksud tetapi tidak terlalu ringkas:

SirYakalot
sumber
4
Apakah Anda berbicara tentang C ++ yang dikelola di bawah .NET?
dasblinkenlight
2
Apakah Anda melihat halaman Wikipedia untuk Delegasi ?
Matthijs Bierman
5
delegatebukan nama umum dalam bahasa c ++. Anda harus menambahkan beberapa informasi ke pertanyaan untuk memasukkan konteks di mana Anda telah membacanya. Perhatikan bahwa meskipun polanya mungkin umum, jawabannya mungkin berbeda jika Anda berbicara tentang delegasi secara umum, atau dalam konteks C ++ CLI atau perpustakaan lain yang memiliki implementasi delegasi tertentu .
David Rodríguez - dribeas
6
tunggu 2 tahun
?;
6
Masih menunggu ...
Grimm The Opiner

Jawaban:

173

Anda memiliki banyak pilihan untuk mencapai delegasi di C ++. Inilah beberapa yang muncul di pikiran saya.


Opsi 1: functors:

Objek fungsi dapat dibuat dengan mengimplementasikan operator()

struct Functor
{
     // Normal class/struct members

     int operator()(double d) // Arbitrary return types and parameter list
     {
          return (int) d + 1;
     }
};

// Use:
Functor f;
int i = f(3.14);

Opsi 2: ekspresi lambda (khusus C ++ 11 )

// Syntax is roughly: [capture](parameter list) -> return type {block}
// Some shortcuts exist
auto func = [](int i) -> double { return 2*i/1.15; };
double d = func(1);

Opsi 3: pointer fungsi

int f(double d) { ... }
typedef int (*MyFuncT) (double d);
MyFuncT fp = &f;
int a = fp(3.14);

Opsi 4: penunjuk ke fungsi anggota (solusi tercepat)

Lihat Fast C ++ Delegate (pada Proyek Kode ).

struct DelegateList
{
     int f1(double d) { }
     int f2(double d) { }
};

typedef int (DelegateList::* DelegateType)(double d);

DelegateType d = &DelegateList::f1;
DelegateList list;
int a = (list.*d)(3.14);

Opsi 5: std :: function

(atau boost::functionjika perpustakaan standar Anda tidak mendukungnya). Ini lebih lambat, tetapi yang paling fleksibel.

#include <functional>
std::function<int(double)> f = [can be set to about anything in this answer]
// Usually more useful as a parameter to another functions

Opsi 6: mengikat (menggunakan std :: bind )

Memungkinkan pengaturan beberapa parameter terlebih dahulu, mudah untuk memanggil fungsi anggota misalnya.

struct MyClass
{
    int DoStuff(double d); // actually a DoStuff(MyClass* this, double d)
};

std::function<int(double d)> f = std::bind(&MyClass::DoStuff, this, std::placeholders::_1);
// auto f = std::bind(...); in C++11

Opsi 7: template

Terima apa saja asalkan cocok dengan daftar argumen.

template <class FunctionT>
int DoSomething(FunctionT func)
{
    return func(3.14);
}
JN
sumber
2
Daftar hebat, +1. Namun, hanya dua yang benar-benar diperhitungkan sebagai delegasi di sini - menangkap lambda dan objek yang kembali std::bind, dan keduanya benar-benar melakukan hal yang sama, kecuali lambda bukan polimorfik dalam arti bahwa mereka dapat menerima jenis argumen yang berbeda.
Xeo
1
@ JN: Mengapa Anda merekomendasikan untuk tidak menggunakan pointer fungsi tetapi tampaknya baik-baik saja dengan menggunakan metode anggota pointer? Mereka identik!
Matthieu M.
1
@ MatthieuM. : titik adil. Saya sedang mempertimbangkan fungsi pointer menjadi warisan, tapi itu mungkin hanya selera pribadi saya.
JN
1
@ Xeo: ide saya tentang delegasi agak empiris. Mungkin saya mixig terlalu banyak objek fungsi dan mendelegasikan (menyalahkan pengalaman C # saya sebelumnya).
JN
2
@SirYakalot Sesuatu yang berperilaku seperti fungsi, tetapi itu mungkin memegang status pada saat yang sama dan dapat dimanipulasi seperti variabel lainnya. Satu penggunaan misalnya adalah untuk mengambil fungsi yang mengambil dua parameter dan memaksa parameter 1 memiliki nilai tertentu membuat fungsi baru dengan parameter tunggal ( bindingdalam daftar saya). Anda tidak dapat mencapai ini dengan pointer fungsi.
JN
37

Delegasi adalah kelas yang membungkus pointer atau referensi ke instance objek, metode anggota kelas objek yang akan dipanggil pada instance objek itu, dan menyediakan metode untuk memicu panggilan itu.

Ini sebuah contoh:

template <class T>
class CCallback
{
public:
    typedef void (T::*fn)( int anArg );

    CCallback(T& trg, fn op)
        : m_rTarget(trg)
        , m_Operation(op)
    {
    }

    void Execute( int in )
    {
        (m_rTarget.*m_Operation)( in );
    }

private:

    CCallback();
    CCallback( const CCallback& );

    T& m_rTarget;
    fn m_Operation;

};

class A
{
public:
    virtual void Fn( int i )
    {
    }
};


int main( int /*argc*/, char * /*argv*/ )
{
    A a;
    CCallback<A> cbk( a, &A::Fn );
    cbk.Execute( 3 );
}
Grimm The Opiner
sumber
yang menyediakan metode untuk memicu panggilan? delegasi? bagaimana? penunjuk fungsi?
SirYakalot
Kelas delegasi akan menawarkan metode seperti Execute()yang memicu pemanggilan fungsi pada objek yang dibungkus oleh delegasi.
Grimm The Opiner
4
Alih-alih Menjalankan, saya akan sangat menyarankan dalam kasus Anda menimpa operator panggilan - void CCallback :: operator () (int). Alasan untuk ini adalah dalam pemrograman generik, objek yang dapat dipanggil diharapkan disebut seperti fungsi. o.Execute (5) akan tidak kompatibel, tetapi o (5) akan cocok sebagai argumen template yang bisa dipanggil. Kelas ini juga bisa digeneralisasi, tetapi saya berasumsi Anda membuatnya sederhana untuk singkatnya (yang merupakan hal yang baik). Selain itu nitpick, ini adalah kelas yang sangat berguna.
David Peterson
18

Kebutuhan akan implementasi delegasi C ++ adalah penghinaan jangka panjang bagi komunitas C ++. Setiap programmer C ++ akan senang memilikinya, jadi mereka akhirnya menggunakannya terlepas dari fakta bahwa:

  1. std::function() menggunakan operasi tumpukan (dan berada di luar jangkauan untuk pemrograman tertanam yang serius).

  2. Semua implementasi lain membuat konsesi terhadap portabilitas atau kesesuaian standar ke tingkat yang lebih besar atau lebih kecil (harap verifikasi dengan memeriksa berbagai implementasi delegasi di sini dan pada proyek codep). Saya belum melihat implementasi yang tidak menggunakan reinterpret_casts liar, kelas bertingkat "prototipe" yang mudah-mudahan menghasilkan pointer fungsi dengan ukuran yang sama seperti yang dilewati oleh pengguna, trik kompiler seperti pertama maju menyatakan, kemudian ketik kemudian mendeklarasikan lagi, kali ini mewarisi dari kelas lain atau teknik teduh serupa. Meskipun ini adalah pencapaian besar bagi para pelaksana yang membangunnya, itu masih merupakan kesaksian yang menyedihkan tentang bagaimana C ++ berkembang.

  3. Jarang sekali ditunjukkan, bahwa sekarang lebih dari 3 revisi standar C ++, delegasi tidak ditangani dengan baik. (Atau kurangnya fitur bahasa yang memungkinkan implementasi delegasi langsung.)

  4. Dengan cara fungsi C ++ 11 lambda didefinisikan oleh standar (setiap lambda memiliki jenis anonim, berbeda), situasinya hanya membaik dalam beberapa kasus penggunaan. Tetapi untuk kasus penggunaan menggunakan delegasi di (DLL) perpustakaan API, lambdas sendiri masih tidak dapat digunakan. Teknik umum di sini, adalah untuk pertama-tama mengemas lambda menjadi fungsi std :: dan kemudian menyebarkannya ke seluruh API.

BitTickler
sumber
Saya telah melakukan versi panggilan balik umum Elbert Mai di C ++ 11 berdasarkan ide-ide lain yang terlihat di tempat lain, dan tampaknya menghapus sebagian besar masalah yang Anda kutip dalam 2). Satu-satunya masalah yang mengganggu bagi saya adalah saya terjebak menggunakan makro dalam kode klien untuk membuat delegasi. Mungkin ada jalan keluar templat ajaib yang belum saya pecahkan.
kert
3
Saya menggunakan std :: function tanpa heap di embedded system dan masih berfungsi.
prasad
1
std::functiontidak selalu mengalokasikan secara dinamis
Lightness Races di Orbit
3
Jawaban ini lebih merupakan kata-kata kasar daripada jawaban yang sebenarnya
Lightness Races in Orbit
8

Sederhananya, delegasi menyediakan fungsionalitas untuk bagaimana fungsi pointer HARUS bekerja. Ada banyak batasan fungsi pointer di C ++. Seorang delegasi menggunakan beberapa kejelekan templat di belakang layar untuk membuat kelas-jenis-fungsi-pointer-templat yang berfungsi sesuai keinginan Anda.

yaitu - Anda dapat mengatur mereka untuk menunjuk pada fungsi yang diberikan dan Anda dapat menyebarkannya dan memanggil mereka kapanpun dan dimanapun Anda suka.

Ada beberapa contoh yang sangat bagus di sini:

SirYakalot
sumber
4

Opsi untuk delegasi dalam C ++ yang tidak disebutkan di sini adalah melakukannya dengan gaya C menggunakan ptr fungsi dan argumen konteks. Ini mungkin merupakan pola yang sama yang banyak orang menanyakan pertanyaan ini coba hindari. Namun, polanya portabel, efisien, dan dapat digunakan dalam kode tertanam dan kernel.

class SomeClass
{
    in someMember;
    int SomeFunc( int);

    static void EventFunc( void* this__, int a, int b, int c)
    {
        SomeClass* this_ = static_cast< SomeClass*>( this__);

        this_->SomeFunc( a );
        this_->someMember = b + c;
    }
};

void ScheduleEvent( void (*delegateFunc)( void*, int, int, int), void* delegateContext);

    ...
    SomeClass* someObject = new SomeObject();
    ...
    ScheduleEvent( SomeClass::EventFunc, someObject);
    ...
Joe
sumber
1

Windows Runtime setara dengan objek fungsi dalam standar C ++. Satu dapat menggunakan seluruh fungsi sebagai parameter (sebenarnya itu adalah pointer fungsi). Itu sebagian besar digunakan bersama dengan acara. Delegasi tersebut mewakili kontrak yang dipenuhi oleh para penangan acara. Ini memfasilitasi bagaimana fungsi pointer dapat bekerja.

nmati
sumber