How can I minimize boilerplate code associated with std::thread?

I have several classes like this in my C++ code:

class ThreadRunner {
public:
    void start() {
        m_thread = std::thread(&ThreadRunner::runInThread, this);
    }
    void stop() {
        m_terminate = true;
    }
    ~ThreadRunner() {
        m_terminate = true;
        if (m_thread.joinable()) {
            m_thread.join();
        }
    }

private:
    void runInThread() {
        size_t i = 0;
        while (!m_terminate) {
            std::cout << "i: " << i << "\n";
            i++;
        }
    };
    std::thread          m_thread;
    std::atomic<bool> m_terminate{ false };
};

Some of my runInThread functions take arguments, and some do not. There is quite a bit of boilerplate code (m_terminate, join) that I have to repeat for each class. All this is of course motivated by that I do not want terminate to get called inside destructor of std::thread:

~thread() noexcept {
   if (joinable()) {
      _STD terminate();
   }
}

A colleague has made a class encapsulating this boilerplate. The way it works is that it has a virtual void runInThread() = 0 method. So each of my classes would inherit this baseclass and override this method. Also there is an implicit assumption that each override checks m_terminate occasionally.

I would prefer to use composition instead of inheritance. That is MyClass uses a MyThread not is a MyThreadObject. How can I create a start method that takes a "function pointer" and arbitrary arguments and passes them on to std::thread constructor?

If it is too much hassle I guess I could will go with my colleague's approach instead. From what I understand Java has a Runnable interface where one must override void Run().

If I were to use std::jthread is this how it should be done?

class ThreadRunner {
public:
    void start() {
        m_thread = std::jthread(&ThreadRunner::runInThread, this, m_stop_source.get_token());
    }
    void stop() {
        m_stop_source.request_stop();
    }
    ~ThreadRunner() {
        stop(); // otherwise ~jthread waits forever for runInThread to finish
    }
private:
    void runInThread(std::stop_token stoken) {
        size_t i = 0;
        while (!stoken.stop_requested()) {
            std::cout << "i: " << i << "\n";
            i++;
        }
    };
    std::jthread          m_thread;
    std::stop_source m_stop_source;
};

What are the benefits of using a std::stop_source over a std::atomic<bool> m_terminate?

If I were to use std::async is this how it would be done?:

class ThreadRunner {
public:
    void start() {
        m_future = std::async(std::launch::async, &ThreadRunner::runInThread, this);
    }
    void stop() {
        m_terminate = true;
    }
    ~ThreadRunner() {
        stop();
    }
private:
    void runInThread() {
        size_t i = 0;
        while (!m_terminate) {
            std::cout << "i: " << i << "\n";
            i++;
        }
    };
    std::future<void> m_future;
    std::atomic<bool> m_terminate{ false };
};

Solution 1:

Your std::jthread code can be simplified to:

class ThreadRunner {
public:
    void start() {
        m_thread = std::jthread(&ThreadRunner::runInThread, this);
    }
    void stop() {
        m_thread.request_stop();
    }
private:
    void runInThread() {
        size_t i = 0;
        auto stopToken = m_thread.get_stop_token();
        while (!stopToken.stop_requested()) {
            std::cout << "i: " << i << "\n";
            i++;
        }
    };
    std::jthread          m_thread;
};

Or as your wrapper isn't doing much just using std::jthread directly might be simpler:

std::jthread thread([](std::stop_token stoken){
    size_t i = 0;
    while (!stoken.stop_requested()) {
        std::cout << "i: " << i << "\n";
        i++;
    }
});