Rantai Tanggung Jawab dalam templat variadic C ++

Ini akan tentang suatu pola yang sederhana, tetapi sering digunakan sebagai rantai tanggung jawab. Inti dari polanya adalah bahwa kami menggunakan beberapa penangan untuk memproses suatu peristiwa, yang masing-masing menentukan apa dan kapan untuk beralih ke yang berikutnya. Ada banyak contoh implementasi C ++ di internet, tetapi saya ingin menunjukkan implementasinya hanya pada ekspresi lambda. Dalam implementasi ini akan dimungkinkan untuk melihat sedikit templat-sulap jalanan .

Jadi katakanlah kita memiliki objek:

class Elephant { public: std::string touch_leg() { return "it's like a pillar"; } std::string touch_trunk() { return "it's like a snake"; } std::string touch_tail() { return "it's like a rope"; } void run_away() { m_is_gone = true; std::cout << "*** Sound of running out elephant ***\n"; } bool is_elephant_here() { return !m_is_gone; } private: bool m_is_gone = false; }; 

Dan kita akan memiliki 3 penangan, yang masing-masing akan melewati objek lebih lanjut:

 //        auto blind_sage3 = ChainOfRepsonsibility::start_new([](Elephant& e) { std::cout << "Third blind sage: " << e.touch_tail() << "\n"; }); // ""    ,     auto blind_sage2 = blind_sage3.attach([](Elephant& e, auto& next) { std::cout << "Second blind sage: " << e.touch_trunk() << "\n"; next(e); }); //    ,     ,    auto blind_sage1 = blind_sage2.attach([](Elephant& e, auto& next) { if (!e.is_elephant_here()) { std::cout << "First blind sage: So empty... so true\n"; } else { std::cout << "First blind sage: " << e.touch_leg() << "\n"; next(e); } }); //      Elephant e; blind_sage1(e); 

Dalam contoh ini, ada 3 penangan, yang masing-masing adalah ekspresi lambda, yang digabungkan ke dalam rantai tanggung jawab menggunakan kelas ChainOfRepsonsibility.

Berikut ini adalah implementasi kelas itu sendiri:

 #include <functional> struct ChainOfRepsonsibility { template<typename... Args> struct Chain { template<typename Callee, typename Next> Chain(const Callee c, const Next& n) { m_impl = c; m_next = n; } template<typename Callee> decltype(auto) attach(Callee c) { return Chain(c, *this); } void operator()(Args... e) { m_impl(e..., m_next); } std::function<void(Args..., std::function<void(Args...)>)> m_impl; std::function<void(Args...)> m_next; }; template<typename... Args> struct ChainTail { template<typename Callee> ChainTail(Callee c) { m_impl = c; } template<typename Callee> decltype(auto) attach(Callee c) { return Chain<Args...>(c, m_impl); } void operator()(Args... e) { m_impl(e...); } std::function<void(Args... e)> m_impl; }; template<typename> struct StartChain; template<typename C, typename... Args> struct StartChain<void (C::*)(Args...) const> { using Type = ChainTail<Args...>; }; template<typename Callee> static decltype(auto) start_new(Callee c) { return StartChain<decltype(&Callee::operator())>::Type(c); } }; 

Ini berfungsi seperti ini:

  • Pertama, kami membuat rantai tanggung jawab menggunakan fungsi start_new. Masalah utama pada tahap ini adalah untuk mendapatkan daftar argumen dari lambda yang lulus dan membuat "prototipe" dari pawang berdasarkan pada mereka.
  • Untuk mendapatkan argumen lambda, kelas StartChain dan tema rumit dengan spesialisasi templat digunakan. Pertama, kita mendeklarasikan versi generik kelas, dan kemudian struct spesialisasi StartChain <void (C :: *) (Args ...) const> . Konstruksi ini memungkinkan kita untuk mengakses daftar argumen oleh anggota kelas yang lulus.
  • Memiliki daftar argumen, kita sudah bisa membuat kelas ChainTail dan Chain, yang akan menjadi pembungkus untuk penangan. Tugas mereka adalah untuk menyimpan lambda di std :: function dan memanggilnya pada waktu yang tepat, serta mengimplementasikan metode attach (mengatur handler dari atas).

Kode telah diuji untuk visual studio 2015, untuk gcc mungkin perlu untuk memperbaiki spesialisasi StartChain dengan menghapus const dari sana, jika selain lambda Anda hanya ingin melewati fungsi, Anda dapat menambahkan spesialisasi StartChain lainnya. Anda masih dapat mencoba untuk menghapus penyalinan dalam lampiran, jangan bergerak, tetapi agar tidak menyulitkan contoh, saya hanya meninggalkan kasus paling sederhana.

Source: https://habr.com/ru/post/id413765/


All Articles