0

I would like to control threads, so each new issued thread will go through my code first.
This way, each new thread issued with runThread() in the example below, will first call the function runInnerThread() below (in which I do some kind of initializations) and then call the desired function.

I've tried putting something like this:

#include <iostream>
#include <thread>

template<typename _Callable, typename... _Args>
void runThreadInner(_Callable&& __f, _Args&&... __args) {
    // My initializations...
    __f(__args...);
    // My finishing...
};

template<typename _Callable, typename... _Args>
bool runThread(_Callable&& __f, _Args&&... __args) {
    std::thread t(std::bind(&runThreadInner,
                            std::forward<_Callable>(__f),
                            std::forward<_Args>(__args)...));
}

int main() {
    runThread([]() {
        std::cout << std::this_thread::get_id() << "Threading...\n";
    });
    return 0;
}

I get an error from the compiler which complains about deducing runThreadInner() templates

main.cpp: In instantiation of ‘bool runThread(_Callable&&, _Args&& ...) [with _Callable = main()::__lambda0; _Args = {}]’:
main.cpp:43:3:   required from here
main.cpp:27:38: error: no matching function for call to ‘bind(<unresolved overloaded function type>, main()::__lambda0)’
        std::forward<_Args>(__args)...));
                                      ^
main.cpp:27:38: note: candidates are:
In file included from /usr/include/c++/4.8/memory:79:0,
                 from main.cpp:2:
/usr/include/c++/4.8/functional:1655:5: note: template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__or_<std::is_integral<typename std::decay<_Tp>::type>, std::is_enum<typename std::decay<_Tp>::type> >::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...)
     bind(_Func&& __f, _BoundArgs&&... __args)
     ^
/usr/include/c++/4.8/functional:1655:5: note:   template argument deduction/substitution failed:
main.cpp:27:38: note:   couldn't deduce template parameter ‘_Func’
        std::forward<_Args>(__args)...));
                                      ^
In file included from /usr/include/c++/4.8/memory:79:0,
                 from main.cpp:2:
/usr/include/c++/4.8/functional:1682:5: note: template<class _Result, class _Func, class ... _BoundArgs> typename std::_Bindres_helper<_Result, _Func, _BoundArgs>::type std::bind(_Func&&, _BoundArgs&& ...)
     bind(_Func&& __f, _BoundArgs&&... __args)
     ^
/usr/include/c++/4.8/functional:1682:5: note:   template argument deduction/substitution failed:
main.cpp:27:38: note:   couldn't deduce template parameter ‘_Result’
        std::forward<_Args>(__args)...));

I tried explicitly defining the template, without any success:

template<typename _Callable, typename... _Args>
bool runThread(_Callable&& __f, _Args&&... __args) {
    std::thread t(std::bind(&runThreadInner<_Callable, _Args>,
                            std::forward<_Callable>(__f),
                            std::forward<_Args>(__args)...));
}

Is it possible? Thanks.

hudac
  • 2,584
  • 6
  • 34
  • 57
  • 1
    Why the `bind` call? Furthermore, your version of GCC is bordering on ancient, and it doesn't have full C++11 support. – Some programmer dude Jan 15 '18 at 17:58
  • 1
    Also, I do hope your `runThread` have some kind of book-keeping of the threads it starts, so you can `join` them later. – Some programmer dude Jan 15 '18 at 18:00
  • 4
    This isn’t the problem, but names that begin with an underscore followed by a capital letter (`_Callable`, etc.) and names that contain two consecutive underscores (`__f`, etc.) are reserved for use by the implementation. Don’t use them in your code. – Pete Becker Jan 15 '18 at 18:07
  • See https://stackoverflow.com/q/30968573/4074081 for explanation of std::bind behavior. It would be easier not to use it at all and create thread with `std::thread t(&runThreadInner<_Callable, _Args...>, std::forward<_Callable>(__f), std::forward<_Args>(__args)...);` instead. – dewaffled Jan 15 '18 at 19:01
  • 1
    Probs with the code: Missing #includes. Simple syntax error (redundant close parenthesis). Identifiers beginning with an underscore and capital letter are reserved for system use. https://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier#228797 – Jive Dadson Jan 15 '18 at 19:07
  • 3
    Every line of code in this question, except close braces and most of the body of main, has problems that would make me reject it in a code review. That is impressive. – Yakk - Adam Nevraumont Jan 15 '18 at 19:18
  • @Someprogrammerdude it was supposed to be a simple example. Maybe I simplified it too much; the actual code is a member function of a class, that's why I need the `bind()` Yes. I'm not proud of this `gcc` version, hope it will be changed soon. – hudac Jan 15 '18 at 20:11
  • @PeteBecker thanks. Actually I copied it from `std::thread` - that's why it uses these terms. I won't use it - thanks. – hudac Jan 15 '18 at 20:11
  • @Yakk JiveDadson, sorry - I've tried taking out code and change on-the-fly for this example. I've updated the example with the actual code. Thanks. – hudac Jan 15 '18 at 20:12
  • Even if `runThreadInner` is a (non-static) member function you don't need to use `std::bind`. Just make sure that the first argument passed to the thread-function is the object. Like e.g. `std::thread myThread(&SomeClass::runThreadInner, this /* or whatever object you want to call runThreadInner on */, ... (rest of arguments) ...)`. – Some programmer dude Jan 16 '18 at 04:32
  • @Someprogrammerdude doesn't itself do `std::__bind_simple()` in the `ctor()` anyway? – hudac Jan 16 '18 at 07:00

3 Answers3

2

Easy peasy.

#include <thread>
#include <iostream>

template<typename Callable, typename... Args>
void runThreadInner(Callable&& f, Args&&... args) {
    // My initializations...
    f(args...);
    // My finishing...
};

template<typename Callable, typename... Args>
std::thread runThread(Callable&& f, Args&&... args) {
    std::thread t(&runThreadInner<Callable, Args...>, 
         std::forward<Callable> (f), std::forward<Args>(args)...);
    return t;
}
int main() {
    auto t = runThread([]() {
        std::cout << "Threading..." << std::endl;
    });
    t.join();
    return 0;
}
Jive Dadson
  • 16,680
  • 9
  • 52
  • 65
  • I would highly recommend changing the names leading with two underscores to remove them, since those are reserved for use by the compiler. There be velociraptors. – Alex Huszagh Jan 15 '18 at 19:20
  • @Alexander Hey, I removed the ones beginning with underscore capital. But yeah. I hate leading underscores and camelCase. Feel free to edit. My job is done here. :-) – Jive Dadson Jan 15 '18 at 19:22
  • 1
    Any variable with consecutive underscores, or a leading underscore follow by a capital letter is reserved for use by the compiler. I hate camelCase too, but at least it won't lead to my code suddenly failing with a new compiler or compiler version. https://stackoverflow.com/a/228797/4131059 – Alex Huszagh Jan 15 '18 at 19:23
  • 1
    @Alexander I only knew about the _Capital thing. But then I never do anything remotely like that. Wanna fix it? – Jive Dadson Jan 15 '18 at 19:25
  • Wouldn't your solution require copy constructor of `std::thread` before C++17? You initialize return value by an lvalue `t`. – Daniel Langr Jan 15 '18 at 19:37
  • @Daniel - Every pre-17 compiler I know of elides the copy via return-value optimization. Feel free to edit if you like. – Jive Dadson Jan 15 '18 at 19:46
  • @JiveDadson Sure, but I thought NRVO still requires copy constructor to exist. See here: https://stackoverflow.com/questions/42645939/why-doesnt-nrvo-work-without-copy-constructor. – Daniel Langr Jan 15 '18 at 19:50
  • @Daniel std::thread has a copy constructor. You done gone plumb circular. It's a vish, – Jive Dadson Jan 15 '18 at 19:53
  • @JiveDadson It's deleted according to http://en.cppreference.com/w/cpp/thread/thread/thread. But I can compile your case in C++11: https://wandbox.org/permlink/JGL6ldTbAo4uHf0D. What's wrong here? – Daniel Langr Jan 15 '18 at 19:55
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/163211/discussion-between-jive-dadson-and-daniel-langr). – Jive Dadson Jan 15 '18 at 19:56
2

You're probably missing an #include <functional> (where std::bind is defined).

However, std::bind isn't really needed in your case because you have lambda's, which are better in many ways.

Here's a working example without std::bind:

#include <iostream>
#include <functional>
#include <thread>
#include <utility>

template <typename F>
struct MyTaskWrapper {
  F f;

  template <typename... T>
  void operator()(T&&... args) {
    std::cout << "Stuff before...\n";
    f(std::forward<T>(args)...);
    std::cout << "Stuff after...\n";
  }

};

template <typename F, typename... Args>
void runThread(F&& f, Args&&... args) {
  std::thread trd(MyTaskWrapper<F>{std::forward<F>(f)}, std::forward<Args>(args)...);
  trd.join();
}

int main() {
  runThread([] {
    std::cout << "Threading...\n";
  });
}

Output:

Stuff before...
Threading...
Stuff after...

Live demo

rustyx
  • 80,671
  • 25
  • 200
  • 267
0

Your code has issues. I'd reject nearly every line in a code review.

_Capital is not legal C++. __lower is not either. Stop duplicating what system and std headers do, they are allowed you are not. They are doing it exactly because you are now allowed to guarantee their code doesn't overlap with you doing stupid preprocessor things (in any legal way).

template<typename Callable>
std::thread runThread(Callable&& f) {
  std::thread t([=]{
    // My initializations...
    f();
    // My finishing...
  });
  return t;
}

int main() {
  auto t = runThread([]() {
    std::cout << std::this_thread::get_id() << "Threading...\n";
  });
  t.join();
}

and if someone wants to bind arguments, let them do it when calling runThread themselves.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Actually it's really simple. It makes `runThreadInner()` redundant because its code can run before and after, in the `lambda` of `runThread()`. – hudac Jan 15 '18 at 21:48