Programming using C++, perhaps my mind is still stuck in the 90s, can't get around some concept please help me out.
So was developing using gtkmm, and came across some of the multithreading example, goes as this
class ExampleWorker {
public:
void do_work(ExampleWindow* caller);
private:
mutable std::mutex m_Mutex;
};
ExampleWindow::ExampleWindow(){
ExampleWorker m_Worker;
std::thread* m_WorkerThread;
void ExampleWindow::on_start_button_clicked() {
if (blablabla){
blablabla
} else {
// Start a new worker thread.
m_WorkerThread = new std::thread(/////////////lambda function
[this] // <==== let this this be "this-A"
{
m_Worker.do_work(this); // let this this be "this-B"
}///////////////end of lambda function /////////////
);
}
Main thing I am having trouble understand is the lambda part. So first of all the lambda concept is quite new to me, was looking up the concept of "capture" in C++ but did not find much, so my best guess is that, in this case, it allows me to "capture" the "[this]" that was returned, according to document, seems to be the [this] captures the this pointer by value, the pointer to the worker casted as a thread pointer, since the worker contained mutex.
However what I do not understand is that, to me, the "this-B" seems to indicate that the "this" is passing the ExampleWindow object itself to the function, as defined by the Worker class that it should be passing its caller into it; whislt the "this-A" seems to be able to refer to the worker thread itself. Question, does this means that the lambda function capture [this] in this case, superseds the normal "this" which refers to the calling object as in "this-B"?
And a bit more on the lambda thing, another example which I have always struggled to understand came from boost asio.
//definition
void async_read_some(
const MutableBufferSequence & buffers,
ReadHandler handler);
// async_tcp_echo_server.cpp
class session : public std::enable_shared_from_this<session>{
private:
void do_read()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length) //<===this is where it is very troubling
{
if (!ec)
{
do_write(length);
}
});
}
So suppose that part is a handler, why is it, or, is it 2 functions? does it invokes both this->do_write and self->do_write? How are they different? In this case what exactly is the difference amongst [this, self] / [this] / [self]? Is it that the first of this / self get executed?
More on lambda, is it only a syntax thing that simplified something in pre c++11? is there anything equivalent pre c++11? Or does it provides new features such as accessing some previously inaccessible variable in a function? Sorry that seemed to be trivial but I think I read through the documents and some other online articles and tried but still failed to grasp the whole concept.
For me understanding lambdas at first helped when I looked at them like this.
struct <closure>
{
<something> operator()(<params>) const { ... <function>; }
<init-capture-as-members>
};
so
[someInt](double a, double b) { return 5; }
struct <closure>
{
int operator()(double a, double b) const { return 5; }
int someInt;
};
everything in the capture list are like "members" and when you execute the lambda you call the operator()
It is more complex (even has const and mutable etc.) but it's just a simple abstraction.
So now for your specific problems:
this is the pointer to the object you are in. So it is not a pointer to the std::thread object but to a ExampleWindow object.
this in the capture list also has kind of a special case.
When you add [this] to the capture list you "allow" the lambda to call everything of the current object. It also allows you to use member functions without using this-> as prefix though.
So your call do_write(...) can be seen as this->do_write(...)
self is just a shared pointer so it will be treated like every other captured object. You have to call explicitly self->... to do anything on it. I assume this is needed in the init-capture so the object will be kept alive as long as the thread is going. Just using this in the init-capture does not increase the ref-count so you may accidentally delete the object while calling do_write without it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With