C++ lambda, not seeing function and argument?

314 Views Asked by At

I have some code using a wrapper class around std::thread, which is using a timer struct (based upon boost::asio), to call methodToCallEachIteration() every 5000 milliseconds:

class OurThreadWrapperClass{
    OurThreadWrapperClass(boost::asio::io_service& = generic_timer_queue_s());
};

class A {
    A() : thread1(_TimerIOService){
        thread1.setInterval(5000);
        // This sets the callback function, to be called every INTERVAL ms.
        thread1.start([this](OurThreadWrapperClass&) {
            this->methodToCallEachIteration();
        });
    }

    void callAFunctionHere(std::bitset<10> arg) {
        // ...
    }

    void methodToCallEachIteration() {
        // ...
    }

    struct TimerService {
        constexpr static const size_t num_threads{2};

        TimerService(){
            for(auto& t: _threads){
                t = std::thread([this](){
                    boost::asio::io_service::work keepalive{_ioservice};

                    callAFunctionHere(_anArgument);  // The method and arg not recognised

                    (void)keepalive;
                    _ioservice.run();
                });
            }
        }

        operator boost::asio::io_service&() {
            return _ioservice;
        }

        boost::asio::io_service _ioservice{num_threads};
        std::thread _threads[num_threads];
    };

    OurThreadWrapperClass thread1;
    TimerService _TimerIOService;
    std::bitset<10> _anArgument;
};

The problem I am having is that I would like to call callAFunctionHere(), from within the TimerService which prepares the threads. I cannot move this function inside TimerService. However, the compiler is complaining that it cannot find callAFunctionHere() or _anArgument:

error: cannot call member function callAFunctionHere(std::bitset<10>) without object

error: 'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = TimerService::TimerService()::__lambda19; _Args = {}]', declared using local type TimerService::TimerService()::__lambda19', is used but never defined [-fpermissive]
       thread(_Callable&& __f, _Args&&... __args)

I think I need to alter the lambda in A::A() so that the compiler can "see" the method and argument but I am not too sure how?

2

There are 2 best solutions below

14
On BEST ANSWER

In the lambda that is calling callAFunctionHere, the this that is captured is the one for the instance of class A::TimerService, but you are trying to implicitly use members of an class A instance. You need a reference to an object of type A, and use that object's members.

class A {
    A() : _TimerIOService(this), thread1(_TimerIOService){
        //...
    }
    //...
    struct TimerService {
        A *a_;
        constexpr static const size_t num_threads{2};

        TimerService(A *a) : a_(a) {
            //...
    //...
    TimerService _TimerIOService;
    OurThreadWrapperClass thread1;
    std::bitset<10> _anArgument;
};

Notice that _TimerIOService needs to come before thread1. And now, the lambda in A::TimerService uses a_ to access the desired members.

4
On

As always, break the problem down to the MVCE:

class A {
    void callAFunctionHere() {}

    struct TimerService {
        TimerService() {
            callAFunctionHere();
        }
    };
};

int main()
{
    A a;
    A::TimerService ts;
}

http://ideone.com/lEUCvO

prog.cpp: In constructor 'A::TimerService::TimerService()':
prog.cpp:6:35: error: cannot call member function 'void A::callAFunctionHere()' without object
             callAFunctionHere();

This particular error is telling you that, due to name resolution, the compiler can tell that you're trying to access the function/member of the outer scope (class A), but you're doing it from the context of an instance of the inner (class A::TimerService).

This line of code:

 callAFunctionHere(_anArgument);

is written inside A::TimerService::TimerService(), so the this pointer is (A::TimerService*) this and the code expands to:

 A::callAFunctionHere(A::_anArgument);

this can't be applied to either of those, since there is no direct inheritance or conversion between A and A::TimerService.

The classes are nested for naming purposes, like a namespace, but they are otherwise discrete: just because TimerService is declared inside of A does not connect their instances outside of name scoping: it does not establish an inheritance or conversion relationship.

Perhaps what you were trying to do was create a generic TimerService class which could describe re-usable functionality via inheritance:

#include <iostream>

struct TimerService
{
    virtual void handleTimerEvent() = 0;

    TimerService()
    {
    }

    void trigger()
    {
        handleTimerEvent();
    }
};

class A : public TimerService
{

    const char* m_name;

public:
    A(const char* name_) : TimerService(), m_name(name_) {}

    void handleTimerEvent() override
    {
        std::cout << "Handling timer event in " << m_name << '\n';
    }
};

void triggerEvent(TimerService& service)
{
    service.trigger();
}

int main()
{
    A a("A instance 'a'"), b("A instance 'b'");
    triggerEvent(b);
    triggerEvent(a);
}

http://ideone.com/92aszw