[:tr]Merhaba sevgili yazılımperver dostlarım. Yine bir haftalık C++ yazısı ile sizlerle birlikteyim. Bu yazımda sizler ile birlikte C++ 17 ile birlikte sunulmaya başlanan std::invoke() metoduna bir göz atacağız.
std::invoke() metodu <functional> kütüphanesi ile sunulmakta. Bu metot sayesinde farklı çağrılabilir nesneler (serbest fonksiyon işaretçileri, sınıf metodu işaretçileri, lambda metotları, fonksiyon nesneler), tek bir mekanizma ile çağrılabilecek. Bu sayede metot çağrılma mekanizması daha esnek ve standart bir şekilde gerçekleştirilebilecek. Peki bunları normalde nasıl yapıyorduk aşağıdaki kod örneğinde bunları görebilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <iostream> #include <functional> using namespace std; // normal fonksiyon isaretcisi kullanimi // Ornek metodumuz int exampleSquare(int arg) { return arg*arg; } // Fonksiyon isaretcimizi tanimlayalim typedef int (*SquareFuncPtr)(int); // sinif metodu isaretcisi kullanimi class ExampleClass { public: int exampleSquare(int arg) { return arg*arg; } }; // Fonksiyon isaretcimizi tanimlayalim typedef int (ExampleClass::*MemberSquareFuncPtr)(int); int main() { // Normal fonksiyon isaretcisi kullanimi // C++ 17 oncesi kullanim SquareFuncPtr plainFuncPtr = &exampleSquare; cout << "Ilgili sayinin karesi: " << plainFuncPtr(2) << '\n'; // C++ 17 sonrasi kullanim cout << "Ilgili sayinin karesi: " << invoke(&exampleSquare, 2) << '\n'; // Sinif uyesi metot isaretcisi kullanimi ExampleClass classInstance; // C++ 17 oncesi kullanim MemberSquareFuncPtr memberFuncPtr = &ExampleClass::exampleSquare; cout << "Ilgili sayinin karesi: " << (classInstance.*memberFuncPtr)(3) << '\n'; // C++ 17 sonrasi kullanim cout << "Ilgili sayinin karesi: " << invoke(&ExampleClass::exampleSquare, classInstance, 3) << '\n'; return 0; } |
Yukarıdaki örnek kodta da görebileceğiniz üzere normal metotlar için geleneksel yöntem çok karmaşık olmasa da, özellikle sınıf üye metot işaretçileri işin içine girince karmaşıklık artıyor. Bunların yanında hem normal fonksiyon işaretçileri hem de sınıf metot işaretçileri ile çalışma ihtiyacınız olduğu durumda, her iki işi de kotarabilecek, basit ve anlaşılır bir arayüz sunuluyor.
Sizlerin de anlayacağı üzere, aslında bu metodun sunduğu yeteneği bir şekilde mevcut mekanizmalar ile (ilgili cpp referans sayfasında örneği var) gerçekleştirebiliyoruz. Tabiki bu std::invoke() kadar anlaşılabilir ve basit görünmüyor 🙂
Aşağıda bu metodun kullanımı ile ilgili referans kaynakta verilen örneği görebilirsiniz. Örnek içerisinde std::invoke()‘un kullanılabileceği olası durumlar güzel bir şekilde özetlenmiş.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <functional> #include <iostream> struct Foo { Foo(int num) : num_(num) {} void print_add(int i) const { std::cout << num_+i << '\n'; } int num_; }; void print_num(int i) { std::cout << i << '\n'; } struct PrintNum { void operator()(int i) const { std::cout << i << '\n'; } }; int main() { // Serbest metot cagrilmasi std::invoke(print_num, -9); // Lambda cagrilmasi std::invoke([]() { print_num(42); }); // Sinif metodu cagrilmesi const Foo foo(314159); std::invoke(&Foo::print_add, foo, 1); // Sinif verisine erisim std::cout << "num_: " << std::invoke(&Foo::num_, foo) << '\n'; // Fonksiyon nesnesinin cagrilmesi std::invoke(PrintNum(), 18); } |
Yukarıdaki örneğe benzer bir şekilde özellikle “observer” ya da “callback” ya da “signal/slot” gibi mekanizmalar için de kullanışlı olabilir.
Kaynaklar:
- https://en.cppreference.com/w/cpp/utility/functional/invoke
- https://www.murrayc.com/permalink/2018/04/15/c17-in-libsigc-invoke-apply-and-constexpr-if/
[:en]Hello my dear yazılımperver friends. We are together again with another weekly C++ post. In this post, we are going to explore the std::invoke() method which is provided with C++ 17.
std::invoke() method is provided witjh <functional> library. Via this method all different kind of callable objects (such as free function pointers, class member function pointers, lambda functions and function objects) can be executed/called with same syntax. Thank to this method, generic function call can be perfomed in a flexible and standard fashion. Before delving into details, let me summarize callable objects in C++ via sample code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | #include <iostream> #include <functional> using namespace std; // Standard function pointer usage // Example method int exampleSquare(int arg) { return arg*arg; } // Define (let say define :) function pointer typedef int (*SquareFuncPtr)(int); // Class member function usage example class ExampleClass { public: int exampleSquare(int arg) { return arg*arg; } }; // Again define function pointer typedef int (ExampleClass::*MemberSquareFuncPtr)(int); int main() { // Standard function pointer usage // Pre C++ 17 SquareFuncPtr plainFuncPtr = &exampleSquare; cout << "Square of given number: " << plainFuncPtr(2) << '\n'; // With C++ 17 cout << "Square of given number: " << invoke(&exampleSquare, 2) << '\n'; // Class member function pointer usage example ExampleClass classInstance; // Pre C++ 17 MemberSquareFuncPtr memberFuncPtr = &ExampleClass::exampleSquare; cout << "Square of number: " << (classInstance.*memberFuncPtr)(3) << '\n'; // With C++ 17 cout << "Square of given number: " << invoke(&ExampleClass::exampleSquare, classInstance, 3) << '\n'; return 0; } |
As you can see in the above sample code, although regular function pointer usage is not complex, the one used for class member functions become very complex. Moreover, if you required to work with both class member and regular function pointers then this new method will provide a simple and clear usage which is also possible before but not very simple and intuitive. This means that we can already achieve std::invoke() capability by existing mechanism (as illustrated in cpp referans sayfasında).
Below you can find the std::invoke() sample given in reference page which summarizes nicely all possible usages of this method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <functional> #include <iostream> struct Foo { Foo(int num) : num_(num) {} void print_add(int i) const { std::cout << num_+i << '\n'; } int num_; }; void print_num(int i) { std::cout << i << '\n'; } struct PrintNum { void operator()(int i) const { std::cout << i << '\n'; } }; int main() { // Regular method call std::invoke(print_num, -9); // Lambda call std::invoke([]() { print_num(42); }); // Class member function call const Foo foo(314159); std::invoke(&Foo::print_add, foo, 1); // Class member data access std::cout << "num_: " << std::invoke(&Foo::num_, foo) << '\n'; // Function object call std::invoke(PrintNum(), 18); } |
Similar to above example, this method can also be used to implement “observer“, “callback“, “signal/slot” or similar mechanisms.
References:
- https://en.cppreference.com/w/cpp/utility/functional/invoke
- https://www.murrayc.com/permalink/2018/04/15/c17-in-libsigc-invoke-apply-and-constexpr-if/
[:]
