Search code examples
c++multithreadinglambda

How to pass class variable inside lambda expression?


I am trying to learn threading in c++.

I have implemented Bounded Buffer - producer/consumer Simulation, but I am facing some error in passing class variable in the lambda expression.

I am facing issue in passing one of the class variable in lambda expression inside one of the class member functions. I have tried passing my variable in capture clause and parameter , but I am still getting error.

class example {
     private : 
         vector<int> storage;
     public :
         void outsideCallFunction(int x) {
               auto lambdaFun = [&storage] () {
                    if(storage.size() > 50) return true; // FACING ISSUE HERE , NOT ABLE TO ACCESS storage variable in side the lambda function, I have tried many different ways of passing as parameter, as 
                    else return false;
              // using lambdaFun ahead in the code
               }
         }
}

Please go ahead and copy paste the code in your editor. I am facing issue in lineNumber 21 and 30 ( both are same issue actually ).

I am new to c++ lambda expressions, I would be grateful if someone can help me understand the problem and explain the solution. Thank you in advance.

#include <iostream>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <thread> 


using namespace std;

class ProducerConsumerSimulation {
    private :
        vector<int> storage; // common storage, where we store data, which is critical section, so we need to use mutex.
        mutex storageMutex;
        condition_variable storageConditionVariable;
    public :
        void publish(int x) {
            /*
                if Storage is having 50, dont push any more elements, wait for consumer to consume some data. 
            */
            unique_lock<mutex> storageUniqueLock(storageMutex);
            // how to pass storage in lambda
            storageConditionVariable.wait( storageUniqueLock, []() {return storage.size() > 50 ? false : true; } ); // if the storage is full , then wait for consumer to consume it
            storage.push_back(x);
            storageConditionVariable.notify_one();
        }
        int consume() {
            /*
                consume data, until there is some data in storage, otherwise wait for producer to push data. 
            */
            unique_lock<mutex> storageUniqueLock(storageMutex);
            // how to pass "storage" in lambda 
            storageConditionVariable.wait( storageUniqueLock , []() { return storage.size() > 0 ? true : false ; });
            int returnVal = storage.back();
            storage.pop_back();
            storageConditionVariable.notify_one();
            return returnVal;
        }
};

void publishMethod(ProducerConsumerSimulation &simulationObject) { // publisher is one thread that is publishing data object
    for(int i = 0 ; i < 100 ; i++) {
        int randomNumber = rand() % 20;
        simulationObject.publish(randomNumber);
    }
}

void consumeMethod(ProducerConsumerSimulation &simulationObject) { // consumer is one thread which is consuming data from object
    for(int i = 0 ; i < 100 ; i++) {
        int result = simulationObject.consume();
        cout << " consumer thread got result " << result << endl;
    }
}

int main() {
    ProducerConsumerSimulation simulationObject;

    thread publisherThread(publishMethod, std::ref(simulationObject));
    thread consumerThread(consumeMethod, std::ref(simulationObject));

    publisherThread.join();

}

I have tried passing class variable in CAPTURE CLAUSE [] and in PARAMETER () . But didn't work out.


Solution

  • Here's the working code:

    #include <iostream>
    #include <mutex>
    #include <condition_variable>
    #include <vector>
    #include <thread> 
    
    
    using namespace std;
    
    class ProducerConsumerSimulation {
        private :
            vector<int> storage; // common storage, where we store data, which is critical section, so we need to use mutex.
            mutex storageMutex;
            condition_variable storageConditionVariable;
        public :
            void publish(int x) {
                /*
                    if Storage is having 50, dont push any more elements, wait for consumer to consume some data. 
                */
                unique_lock<mutex> storageUniqueLock(storageMutex);
                // how to pass storage in lambda
                storageConditionVariable.wait( storageUniqueLock, [&]() {return storage.size() > 50 ? false : true; } ); // if the storage is full , then wait for consumer to consume it
                storage.push_back(x);
                storageConditionVariable.notify_one();
            }
            int consume() {
                /*
                    consume data, until there is some data in storage, otherwise wait for producer to push data. 
                */
                unique_lock<mutex> storageUniqueLock(storageMutex);
                // how to pass "storage" in lambda 
                storageConditionVariable.wait( storageUniqueLock , [&]() { return storage.size() > 0 ? true : false ; });
                int returnVal = storage.back();
                storage.pop_back();
                storageConditionVariable.notify_one();
                return returnVal;
            }
    };
    
    void publishMethod(ProducerConsumerSimulation &simulationObject) { // publisher is one thread that is publishing data object
        for(int i = 0 ; i < 100 ; i++) {
            int randomNumber = rand() % 20;
            simulationObject.publish(randomNumber);
        }
    }
    
    void consumeMethod(ProducerConsumerSimulation &simulationObject) { // consumer is one thread which is consuming data from object
        for(int i = 0 ; i < 100 ; i++) {
            int result = simulationObject.consume();
            cout << " consumer thread got result " << result << endl;
        }
    }
    
    int main() {
        ProducerConsumerSimulation simulationObject;
    
        thread publisherThread(publishMethod, std::ref(simulationObject));
        thread consumerThread(consumeMethod, std::ref(simulationObject));
    
        publisherThread.join();
    
    }
    

    So why wasn't it working?

    C++ lambdas require that you specify the captures, if you don't then the lambda won't be able to access variables outside its scope but only its provided arguments.

    There are many ways to use captures with Lambdas, I suggest checking the C++ documentation on Lambdas.

    But in this case, you needed to add the & symbol inside the [] (the captures of the lambda) to gain access to the storage variable by reference.

    Or you could also use the = symbol since you don't need to modify the storage variable but I still suggest using references since doing a copy of the storage variable every time could negatively influence the program speed.

    Hope this solves your issue.