Soleil
Soleil

Reputation: 7457

Can I initialize a std::thread in a subclass constructor while being declared in base class ? How?

I have a base using std::thread _threadCall in its destructor, but I need to initialize it from the subclass (imagine I have several subclasses, all initializing differently _threadCall).

class QueryBase
{
    std::condition_variable _cv;
    bool _run{ true };
    std::thread _threadCall;
    virtual ~QueryBase();
    void virtual CallRunner()=0;
}

QueryBase::~QueryBase()
{
    _run = false;
    _cv.notify_one();
    _threadCall.join();
}

subclass:

class __declspec(dllexport) AsyncQuery: public QueryBase
{
protected:
    MessageChangedCallback _log{};
public:
    AsyncQuery(MessageChangedCallback fn);
    void Add(const std::string& msg);
    void CallRunner() override;
}

AsyncQuery::AsyncQuery(MessageChangedCallback fn)
    : QueryBase(), _log(fn),
      _threadCall([&] { CallRunner(); }) //E0292 _threadCall is a non-static member
                                         // or base class of class AsyncQuery
{
}

Can I initialize it from the subclass constructor or in some way ? if not I have the obligation to write the same dtor in every single sublass ?

As alternative, I thought about declaring a delegate in the base-class, and I "define" it in the subclasses:

class QueryBase
{
    std::function<void()> f;
    void virtual CallRunner()=0;

QueryBase::QueryBase() : _threadCall(f)
{
}

AsyncQuery::AsyncQuery(MessageChangedCallback fn)
    : _log(fn)
{
    f = [&] {CallRunner();};
}

Where CallRunner() is defined (overrides a pure virtual) only in subclasses. Any pros/cons about this ?

Upvotes: 1

Views: 110

Answers (2)

NoSenseEtAl
NoSenseEtAl

Reputation: 30128

It is usually bad idea to use inheritance in this way, maybe you should use std::jthread, but design discussions aside...

Way to do delayed construction is to use std::optional member.

struct B {
  std::optional<std::thread> t;
  ~B() {
    assert(t.has_value());
    std::cout << "joining" << std::endl;
    t->join();
    std::cout << "joined" << std::endl;
  }
};

struct D : public B {
  D() {
    t.emplace([] { std::cout << "hello from derived" << std::endl; });
  }
};

int main() { D d; }

godbolt

Upvotes: 1

Red.Wave
Red.Wave

Reputation: 4249

Just use C++20 std::jthread with a std::stop_token:

std::jthread my_async_q{ 
     [](std::stop_token finish){
        while(not finish.stop_requested())
        {/*repeat the job*/};
     };//lambda
};//my_async_q

All your dll or any other extension code needs provide is a function foo or a function object that satisfies std::invocable<decltype (foo),std::stop_token>. Then you can restart a thread like:

//if (my_async_q.joinable())
my_async_q.request_stop();

//Join before renewal:
my_async_q = std::jthread{foo};

The join member is automatically invoked upon destruction. This single standard class encapsulate all the functionality you are trying to achieve, without needing dynamic polymorphism.

Upvotes: -1

Related Questions