How to write an anonymous function / lambda that passes itself as a callback?

I am learning boost :: asio and C ++ 11 at the same time. One of my test programs, which is actually an adaptation of one of the examples given in the boost :: asio manual , is this:

#include <iostream> #include <boost/asio.hpp> #include <boost/date_time/posix_time/posix_time.hpp> class printer { // Static data members private: const static boost::posix_time::seconds one_second; // Instance data members private: boost::asio::deadline_timer timer; int count; // Public members public: printer(boost::asio::io_service& io) : timer(io, one_second), count(0) { std::function<void(const boost::system::error_code&)> callback; callback = [&](const boost::system::error_code&) { // critical line if (count < 5) { std::cout << "Current count is " << count++ << std::endl; timer.expires_at(timer.expires_at() + one_second); timer.async_wait(callback); } }; timer.async_wait(callback); } ~printer() { std::cout << "Final count is " << count << std::endl; } }; const boost::posix_time::seconds printer::one_second(1); int main() { boost::asio::io_service io; printer p(io); io.run(); return 0; } 

When I run this program, I get a segmentation error. I understand why I get a segmentation error. After the constructor completes, the callback constructor variable goes out of scope, and the lambda callback variable, which is a reference to the callback constructor variable, becomes a link.

Therefore, I modify the curve with:

  callback = [callback, &](const boost::system::error_code&) { // critical line 

Then compile it, run it and get a function error message. Again, I understand why I get a function error error. Within the lambda region, the callback constructor variable has not yet been assigned any value, so for all practical purposes this is a dangling function pointer. Therefore, the callback lambda variable, which is a copy of the callback constructor variable, is also a pointer to dangling functions.


After thinking about this problem for a while, I realized that I really need the callback to be able to refer to itself using a function pointer, not a reference to a function pointer. The sample achieved this by using the named function as a callback rather than anonymous. However, passing these functions as callbacks is not very elegant. Is there a way to get an anonymous function, is there a pointer to the function as a local variable?

+6
source share
5 answers

There are several alternatives:

  • Stop using lambda. You should not use them for everything, you know. They cover a lot of cases, but they do not cover everything. Just use a regular old functor.
  • The lambda store has a smart pointer to the dynamically allocated std::function , which stores the lambda. For instance:

     auto pCallback = std::make_shared<std::function<void(const boost::system::error_code&)>>(); auto callback = [=](const boost::system::error_code&) { // critical line if (count < 5) { std::cout << "Current count is " << count++ << std::endl; timer.expires_at(timer.expires_at() + one_second); timer.async_wait(pCallback.get()); } }; *pCallback = callback; 
+8
source

To learn about Asio and C ++ 11, I recommend saying that "why C ++ 0x is the Awesomest network programming language" by asio himself. (Christopher Kochhoff)

https://blip.tv/boostcon/why-c-0x-is-the-awesomest-language-for-network-programming-5368225 http://github.com/chriskohlhoff/awesome

In this conversation, CK takes a typical small asio application and starts adding the C ++ 11 function one at a time. In the middle of the conversation there is a part about lambda. The kind of problem you have with lambda lifetime is a workaround using the following pattern:

 #include <iostream> #include <boost/asio.hpp> #include <boost/date_time/posix_time/posix_time.hpp> #include <memory> class printer { // Static data members private: const static boost::posix_time::seconds one_second; // Instance data members private: boost::asio::deadline_timer timer; int count; // Public members public: printer(boost::asio::io_service& io) : timer(io, one_second), count(0) { wait(); } void wait() { timer.async_wait( [&](const boost::system::error_code& ec) { if (!ec && count < 5) { std::cout << "Current count is " << count++ << std::endl; timer.expires_at(timer.expires_at() + one_second); wait(); } }); } ~printer() { std::cout << "Final count is " << count << std::endl; } }; const boost::posix_time::seconds printer::one_second(1); int main() { boost::asio::io_service io; printer p(io); io.run(); return 0; } 
+3
source

Only theory: you can do such things with so-called combinators (for example, I, S, K).

Before using your anonymous lambda expression e type F, you can first define functions such as doubleF ; (F) โ†’ (F, F) or applyToOneelf : (F f) โ†’ F = {return f (f); }.

+1
source

It is currently proposed to add Y-combinator to the C ++ Standard Library (P0200R0) to solve this problem.

The main idea here is that you pass lambda to yourself as the first argument. An invisible helper class takes a recursive call in the background, storing the lambda in the named member.

An example implementation from the proposal is as follows:

 #include <functional> #include <utility> namespace std { template<class Fun> class y_combinator_result { Fun fun_; public: template<class T> explicit y_combinator_result(T &&fun): fun_(std::forward<T>(fun)) {} template<class ...Args> decltype(auto) operator()(Args &&...args) { return fun_(std::ref(*this), std::forward<Args>(args)...); } }; template<class Fun> decltype(auto) y_combinator(Fun &&fun) { return y_combinator_result<std::decay_t<Fun>>(std::forward<Fun>(fun)); } } // namespace std 

What could be used to solve the problem from the question:

 timer.async_wait(std::y_combinator([](auto self, const boost::system::error_code&) { if (count < 5) { std::cout << "Current count is " << count++ << std::endl; timer.expires_at(timer.expires_at() + one_second); timer.async_wait(self); } })); 

Note the self argument, which is passed to lambda. This will be due to the result of calling y_combinator , which is a function object that is equivalent to a lambda with the self argument already bound (i.e., its signature is void(const boost::system::error_code&) ).

+1
source

The lambda captures the local variable "callback" by reference when the lambda works, which will not be valid.

0
source

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


All Articles