1

I would like to start a thread with a class which contains non-copyable members. In order to communicate with the thread I would like to create a shared pointer from the object before I move it into the thread.

Does the move constructor invalidate the shared pointer? If so, what is the elegant c++ way of doing this? One solution would be to wrap MessageClient into a shared pointer, but that hacks around the move constructor.

class MessageClient : public std::enable_shared_from_this<MessageClient> {
private:
    boost::asio::io_context io_context;

    void send() {
      // code
    }

    void operator() () {
      // code
    }
};

int main () {
   MessageClient client;
   auto pclient = client.shared_from_this();
   std::thread thread(std::move(client));

   pclient->send(); // terminate called after throwing an instance of  'std::bad_weak_ptr'
                    // what():  bad_weak_ptr
                    // Aborted (core dumped)
   thread.join();
}

EDIT

I got my answer. I understand now that I have used the enable_shared_from_this incorrectly, but real answer is, that there is no way of solving the issue which does not involve wrapping the object into another object, such as smart pointer or lambda function (which is wrapping the object into a functor). Personally I have found the second solution simpler, that is why I have selected it.

MessageClient client;
std::thread thread([&client](){client.run();});

client.send();

EDIT2

I have found a more obvious solution. If I create a reference from the object I do not have to wrap it:

MessageClient client;
std::thread thread(std::ref(client));

client.send();
user1396055
  • 295
  • 1
  • 12
  • 1
    `shared_from_this` can be called only inside member function of `MessageClient` when there is at least one instance of `shared_ptr` which manges it. – rafix07 Jun 07 '19 at 09:59

2 Answers2

1

In main you could create two instances of shared_ptr which shares the same MessageClient object. One shared ptr can be moved into the body of thread, another stays in main and you can use it to communicate with the thread:

int main () 
{
   std::shared_ptr<MessageClient> client = std::make_shared<MessageClient>();
   std::shared_ptr<MessageClient> pclient = client;

   std::thread thread(
        [client = std::move(client)]()
        {
           (*client)(); // invoke body of thread
        });

   pclient->send(); // works fine
   thread.join();
}

demo


Your code with shared_from_this doesn't work because shared_from_this can be called only inside member function of MessageClient when you are sure that there is at least one instance of shared_ptr which manages this.

rafix07
  • 20,001
  • 3
  • 20
  • 33
  • At the point where I create pclient, the client instance is valid. – user1396055 Jun 07 '19 at 11:27
  • @user1396055 See [reference about shared_from_this](https://en.cppreference.com/w/cpp/memory/enable_shared_from_this/shared_from_this). In your case client is LOCAL VARIABLE. You can call `shared_from_this` only if there is shared_ptr which manages it. See examples at the link I attached. `shared_from_this` is searching *control block* to find smart pointer which refers to `this`, then increase its reference counter. In your case there is nothing to find, because no shared ptr pointing `this` exists. – rafix07 Jun 07 '19 at 11:30
  • I got it now, it is much less useful than I thought – user1396055 Jun 07 '19 at 11:38
1

Deriving from enable_shared_from_this hints that every instance is owned by some shared_ptrs. Your problem is that client isn't, and is unrelated to moving it into the thread.

You have undefined behaviour on this line:

auto pclient = client.shared_from_this();

Just use a std::shared_ptr<MessageClient> from the start.

class MessageClient {
private:
    boost::asio::io_context io_context;

    void send() {
      // code
    }

    void operator() () {
      // code
    }
};

int main () {
   auto pclient = std::make_shared<MessageClient>();
   std::thread thread([pclient]() { (*pclient)(); });

   pclient->send(); 

   thread.join();
}
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • I do not understand this answer. What does shared_from_this exactly mean? In my understanding it should mean that even if the instance goes out of scope, the shared pointer derived from it should remain valid. – user1396055 Jun 07 '19 at 11:20
  • client is not managed by a shared pointer, calling shared_from_this on it has undefined behaviour. In C++17 it becomes defined as throwing. You can't magic shared ownership of an object with automatic storage duration. – Caleth Jun 07 '19 at 11:23
  • what does enable_shared_from_this do? – user1396055 Jun 07 '19 at 11:29
  • It allows you to get a `shared_ptr` that shares ownership with other existing `shared_ptr`s, from a method on the pointed-to object – Caleth Jun 07 '19 at 11:30
  • does the shared pointer prevent the instance to be destroyed? if not then how is it different from simply taking `this` – user1396055 Jun 07 '19 at 11:32
  • There has to be existing shared ownership. You call `shared_from_this` and get a new `shared_ptr`, it points to the existing use count (which has been incremented). If you call it on an object not managed by `shared_ptr`, your program is ill formed – Caleth Jun 07 '19 at 11:35