Kay
Kay

Reputation: 69

std::thread() and std::ref() cause build errors when used inside of a class

I posted a question yesterday but I didn't have a runnable example to give other users. I do now, so I deleted my old post.

I have some code that fails to build when I attempt to make and use std::threads() and std::ref() inside of a class. I do not understand the error messages that are produced, which all start with the line error: no matching function for call or error: no type named ‘type’.

I am using Clion and CMake, incase that matters. My file structure is:

Personal
--include
----main.h
--src
----main.cpp
----CMakeLists.txt
--CMakeLists.txt

CMakeLists.txt

# CMAKE version requirement
cmake_minimum_required(VERSION 3.12)

# Project name
project(scrap CXX)

# Configure the build
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_BUILD_TYPE Debug)
add_compile_options(-W -Wall -ggdb)
include_directories(include)
include_directories(${CMAKE_SOURCE_DIR}/include)

# What is being built
add_executable(scrap)
add_subdirectory(src)

# Add external dependencies
find_package(Threads REQUIRED)
target_link_libraries(scrap ${CMAKE_THREAD_LIBS_INIT})

src/CMakeLists.txt

# Add targets
target_sources(scrap PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
        )

main.h

#ifndef PERSONAL_MAIN_H
#define PERSONAL_MAIN_H

#include <future>
#include <iostream>
#include <unistd.h>

class ScrapPaper
{
public:
    ScrapPaper();
    static void SpinUpThreads();
    void ThreadFunction1(std::promise<bool> &prom);
    void ThreadFunction2(std::promise<bool> &prom);

private:

};

#endif //PERSONAL_MAIN_H

main.cpp

#include "../include/main.h"

using namespace std;

void ScrapPaper::ThreadFunction1(promise<bool> &prom)
{
    cout << "Thread " << this_thread::get_id() << " working in ThreadFunction1!" << endl;
    sleep(10);

    cout << "Thread " << this_thread::get_id() << " finished sleeping in Function1" << endl;
    prom.set_value_at_thread_exit(true);
}

void ScrapPaper::ThreadFunction2(promise<bool> &prom)
{
    cout << "Thread " << this_thread::get_id() << " working in ThreadFunction2!" << endl;
    sleep(2);
    cout << "Thread " << this_thread::get_id() << " finished sleeping in Function2" << endl;
    prom.set_value_at_thread_exit(true);
}

void ScrapPaper::SpinUpThreads()
{
    promise<bool> promise1;
    future<bool> future1 = promise1.get_future();
    std::thread (&ScrapPaper::ThreadFunction1, ref(promise1)).detach();

    promise<bool> promise2;
    future<bool> future2 = promise2.get_future();
    std::thread (&ScrapPaper::ThreadFunction2, ref(promise2)).detach();

    if (future1.get() && future2.get())
    {
        cout << "Everything was a-okay" << endl;
    }
    else
    {
        cout << "Whoops, there was an error..." << endl;
    }
}

int main(int argc, char *argv[])
{
    cout << "In Main..." << endl;
    ScrapPaper::SpinUpThreads();

} // end main

Some of the errors that I am getting are:

error: no matching function for call to ‘std::thread::_Invoker<std::tuple<void (ScrapPaper::*)(std::promise<bool>&), std::reference_wrapper<std::promise<bool> > > >::_M_invoke(std::thread::_Invoker<std::tuple<void (ScrapPaper::*)(std::promise<bool>&), std::reference_wrapper<std::promise<bool> > > >::_Indices)’
  operator()()
  ^~~~~~~~

and

error: no type named ‘type’ in ‘struct std::__invoke_result<void (ScrapPaper::*)(std::promise<bool>&), std::reference_wrapper<std::promise<bool> > >’

When the class is taken away and there is just a main() and the ThreadFunction1(...) and ThreadFunction2(...), the code builds and runs. Am I having a scope problem? Any advice or help is greatly appreciated!

Upvotes: 2

Views: 258

Answers (3)

Pete Becker
Pete Becker

Reputation: 76305

You can do one of two things. As written, the two thread functions are non-static member functions. You must have an object to call them on, and that's what the other answers have addressed.

But neither of those functions uses any data from a ScrapPaper object, so you can change them to static member functions and the rest of your code should work without changes. That is, change

class ScrapPaper
{
public:
    ScrapPaper();
    static void SpinUpThreads();
    void ThreadFunction1(std::promise<bool> &prom);
    void ThreadFunction2(std::promise<bool> &prom);

private:

};

to

class ScrapPaper
{
public:
    ScrapPaper();
    static void SpinUpThreads();
    static void ThreadFunction1(std::promise<bool> &prom);
    static void ThreadFunction2(std::promise<bool> &prom);

private:

};

Of course, with those changes, the natural question is why ScrapPaper is a class, since it has no data and no object-specific behavior. That suggests possibly making ScrapPaper a namespace rather than a class. But that's a subject for a different question.

Upvotes: 2

NathanOliver
NathanOliver

Reputation: 180594

The issue here is that since you are using member functions, you need an instance of the class to call the member functions on. If SpinUpThreads wasn't static you could use

std::thread (&ScrapPaper::ThreadFunction1, this, ref(promise1)).detach();
std::thread (&ScrapPaper::ThreadFunction2, this, ref(promise2)).detach();

but since it is static you have to create an instance of the class to pass to threads constructor. You can share one object for both calls or give each thread it's own object. That woulds look like

ScrapPaper common;
std::thread (&ScrapPaper::ThreadFunction1, common, ref(promise1)).detach();
std::thread (&ScrapPaper::ThreadFunction2, common, ref(promise2)).detach();

//or

std::thread (&ScrapPaper::ThreadFunction1, ScrapPaper{}, ref(promise1)).detach();
std::thread (&ScrapPaper::ThreadFunction2, ScrapPaper{}, ref(promise2)).detach();

You can also use a lambda to make the call syntax easier. If you use a lambda you can write the function call in more natural manner and it would look like

ScrapPaper common;
std::thread ([&](){ common.ThreadFunction1(promise1); }).detach();
std::thread ([&](){ common.ThreadFunction2(promise2); }).detach();

//or

std::thread ([&](){ ScrapPaper{}.ThreadFunction1(promise1); }).detach();
std::thread ([&](){ ScrapPaper{}.ThreadFunction2(promise2); }).detach();

Upvotes: 2

SergeyA
SergeyA

Reputation: 62573

When using std::thread to call the member function, the second argument should be an instance of the class (or the pointer to it).

In your case, the code would be

 static ScrapPaper p;
 std::thread (&ScrapPaper::ThreadFunction2, p, ref(promise2)).detach();

Upvotes: 2

Related Questions