1

This question relates to this previous question.

I've implemented the code published there by Richard Hodges. The code published works for me when I use g++ (Debian 4.8.4-1) 4.8.4.

However, the implementation is part of a CUDA library, and I am stuck with CUDA 6.5 which unofficially supported C++11 features.

When I use the code Richard posted:

template <class F>
void submit( F&& f)
{
    std::unique_lock<std::mutex> lock(_cvm);
    ++ _tasks;
    lock.unlock();
    _io_service.post(
    [this, f = std::forward<F>(f)]
                     {
                         f();
                         reduce();
                     });
}

I get an error: error: expected a "]" referring to the lambda line. This makes me think that the header is not being parsed properly. I tried without the template, just passing a reference to my worker class, and without the forwarding.

void submit( trainer & job)
{
    std::unique_lock<std::mutex> lock(_cvm);
    ++ _tasks;
    lock.unlock();
    _io_service.post([this,&]
                     {
                         job();
                         reduce();
                     });
}

And I got error: an enclosing-function local variable cannot be referenced in a lambda body unless it is in the capture list.

So I explicitly added both this and job:

void submit( trainer & job)
{
    std::unique_lock<std::mutex> lock(_cvm);
    ++ _tasks;
    lock.unlock();
    _io_service.post([this,&job]
                     {
                         job();
                         reduce();
                     });
}

At which point, I got stuck at error:

error: could not convert ‘{{((cuANN::trainer_pool*)this)->cuANN::trainer_pool::_io_service}}’ from ‘<brace-enclosed initializer list>’ to ‘boost::asio::io_service::work’ boost::asio::io_service::work _work { _io_service };

FYI, cuANN::trainer_pool is the worker_pool in Richard's example, and the thread pool implementation, and _io_service is simply a member of the class trainer_pool:

class trainer_pool
{   
public:
    trainer_pool ( unsigned int max_threads );
    void start();
    void wait();
    void stop(); 
    void thread_proc();
    void reduce();
    void submit( trainer & job);

private:
    unsigned int _max_threads_;
    boost::asio::io_service _io_service;
    boost::asio::io_service::work _work { _io_service };
    std::vector<std::thread> _threads;
    std::condition_variable _cv;
    std::mutex _cvm;
    size_t _tasks = 0;
};
  1. What am I doing wrong?
  2. How can I do it using std::bind or boost::bind instead of using a lambda?

PS: the code on http://ideone.com/g38Z4H is my skeleton for g++ (which works). The http://ideone.com/d7Nkop saved as host.cu also showcases the issue.

nvcc -std=c++11 host.cu -lboost_thread -lboost_system -lpthread -o host

Community
  • 1
  • 1
Ælex
  • 14,432
  • 20
  • 88
  • 129
  • 2
    Can you provide a short code that is complete (so I don't have to go digging around to try and guess at what you are compiling) along with the exact compile command you are using? – Robert Crovella Nov 24 '15 at 20:44
  • Is this code to be used in device code? If not then separate compilation of host and device code might be an option. – havogt Nov 24 '15 at 20:46
  • @havogt this is to be run on host only, but I'm building it as a library. – Ælex Nov 24 '15 at 20:49
  • @RobertCrovella I'll do that asap. – Ælex Nov 24 '15 at 20:49
  • I can't compile "Richard's code" using `g++ -std=c++11 ...` using gnu 4.8.3. It throws compile errors for me around `string_literals`. Do you need 4.8.4 for that? – Robert Crovella Nov 24 '15 at 20:53
  • @RobertCrovella Yes, the ldone example I've posted uses printf instead so it works with 4.8.3 – Ælex Nov 24 '15 at 20:54
  • I can't compile your code either with g++. boost 1.54 won't work? – Robert Crovella Nov 24 '15 at 20:59
  • That's bizarre, it works with g++ and I am also using boost 1.54. What's the error you get? – Ælex Nov 24 '15 at 21:00
  • The code compiles for me with g++ 4.8.1 and boost 1.53 – havogt Nov 24 '15 at 21:03
  • 1
    Never mind, I made a mistake. I can compile your code with g++ 4.8.3 and boost 1.54, but I do get the error around `]` with nvcc ver. 7.5. So I think it is an nvcc compiler bug. I will file a bug internally at NVIDIA, but I don't have any immediate workaround suggestions for you, other than the one suggested by @havogt Your objection about this being a library is not clear to me as to why that would be a problem. – Robert Crovella Nov 24 '15 at 21:03
  • 1
    @RobertCrovella I just made a new ideone example: http://ideone.com/d7Nkop This one uses the same code I've posted in my question and I got the same error. I am guessing its an issue of nvcc and c++11? I am also compiling seperately in cmake, but I 'll' try and see if I can use g++ without nvcc for this particular file. How can I use std::bind, or boost::bind instead in order to avoid the lambda altogether? – Ælex Nov 24 '15 at 21:04

2 Answers2

2

this construct:

[this, f = std::forward<F>(f)]

is c++14 and won't compile in c++11.

using [this, &] would be a mistake since there's no guarantee that the function object will still exist (you captured it by reference)

in c++11 consider [this, f] to take a copy.

edit: just realised that job is mutable, therefore:

void submit(trainer & job)
{
    std::unique_lock<std::mutex> lock(_cvm);
    ++ _tasks;
    lock.unlock();
    _io_service.post([this,job]()->void mutable
                     {
                         job();
                         reduce();
                     });
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Hi Richard, thanks for the reply. Unfortunately the issue still persists if I copy it by value within the lambda. I think its a problem with cuda 6.5/nvcc. Do you know how I can do the same using bind? – Ælex Nov 24 '15 at 21:09
  • bind would be complicated but you could just define a local structure that has a call operator (basically hand-coding the lambda) – Richard Hodges Nov 24 '15 at 21:11
  • You mean that I need a wrapper for the reference to both `_io_service` and the thread worker? It seems to me like I have no other choice since I can't get lambdas to compile. – Ælex Nov 24 '15 at 21:12
  • ah - i've just realised that your trainer object is non-const. that is a problem. – Richard Hodges Nov 24 '15 at 21:13
  • 1
    see edit. you need to make the lambda mutable if your function object is mutable. – Richard Hodges Nov 24 '15 at 21:15
  • I'm afraid I am still getting the same error: `error: could not convert ‘{{((cuANN::trainer_pool*)this)->cuANN::trainer_pool::_io_service}}’ from ‘’ to ‘boost::asio::io_service::work’ boost::asio::io_service::work _work { _io_service };` This seems to suggest that there is an issue with casting `this` inside the lambda and not the worker object `job`? – Ælex Nov 24 '15 at 21:17
1

I found the problem. NVCC and CUDA works fine with the lambdas.

The offending line was in the header:

boost::asio::io_service::work _work { _io_service };

CUDA 6.5 does not like the initializer list. Moving it to the constructor:

trainer_pool (unsigned int max_threads)
:_max_threads_(max_threads), _work(_io_service)
{}

Seems to have fixed the error and the code now compiles. Many thanks to all who helped.

Ælex
  • 14,432
  • 20
  • 88
  • 129