2

I found this code online explaining how to use a std::condition_variable to solve the Producer-Consumer Problem: Producer-Consumer Problem Using Condition Variable in C++

#include <condition_variable> // std::condition_variale
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

std::mutex g_mutex;
std::condition_variable g_cv;

bool g_ready = false;
int g_data = 0;

int produceData() {
  int randomNumber = rand() % 1000;
  std::cout << "produce data: " << randomNumber << "\n";
  return randomNumber;
}

void consumeData(int data) { std::cout << "receive data: " << data << "\n"; }

void consumer() {
  int data = 0;
  while (true) {
    std::unique_lock<std::mutex> ul(g_mutex);
    g_cv.wait(ul, []() { return g_ready; });
    consumeData(g_data);
    g_ready = false;
    ul.unlock();
    g_cv.notify_one();
  }
}

void producer() {
  while (true) {
    std::unique_lock<std::mutex> ul(g_mutex);
    g_data = produceData();
    g_ready = true;
    ul.unlock();
    g_cv.notify_one();
    ul.lock();
    g_cv.wait(ul, []() { return g_ready == false; });
  }
}

void consumerThread(int n) { consumer(); }

void producerThread(int n) { producer(); }

int main() {
  int times = 100;
  std::thread t1(consumerThread, times);
  std::thread t2(producerThread, times);
  t1.join();
  t2.join();
  return 0;
}

My code works, but my coding standard says I'm not allowed to use lambdas. How can I use this code without using lambdas in wait()?

I've tried the following, but it didn't work:

  1. use a normal bool
wait(g_ready)
6
  • 4
    Just pass some other callable, such as a function pointer or an object with a defined and compatible operator(). Or improve your coding standards. Commented Jun 8, 2023 at 19:02
  • Yes you can use a free function too. Commented Jun 8, 2023 at 19:02
  • 4
    I agree get your coding standards upgraded. Unless you are in an environment where you have to interface with "C" code that needs function pointers (then lambdas with captures might be a challenge). If you are not on embedded system pass this on to whoever is responsible for your coding standards : isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines Commented Jun 8, 2023 at 19:03
  • 7
    What is the justification for disallowing lambdas? That is absolutely the first question I would ask. Commented Jun 8, 2023 at 19:04
  • 5
    "but for coding standards I'm not allowed to use lambdas" - What a stupid rule. You should challenge that. Commented Jun 8, 2023 at 19:13

2 Answers 2

8

As commenters have pointed out, disallowing lambdas is a dubious decision for a coding standard. Regardless, lambdas are just convenience syntax for creating types with a call operator, which you could do by hand.

Solution with callable struct

The lambda expression:

[]() { return g_ready; }

roughly translates into

struct g_ready_checker {
    bool operator()() const noexcept {
        return g_ready;
    }
};

You could then do the following:

g_cv.wait(ul, g_ready_checker{});

We can also turn g_ready_checker into a class template with a <bool DesiredValue> parameter, or we could store the value g_ready is meant to have as a class member.

Solution with free function

Or alternatively, since this lambda isn't capturing anything, you could also write a free function, possibly as a template:

template <bool DesiredValue>
bool is_g_ready() {
    return g_ready == DesiredValue;
}

/* ... */
g_cv.wait(ul, &is_g_ready<true>);
/* ... */
g_cv.wait(ul, &is_g_ready<false>);

Here, we provide wait with a predicate which is a function pointer. Note that the Predicate template parameter of std::condition_variable_wait is not restricted to lambdas, it accepts anything callable which returns something convertible bool.

Sign up to request clarification or add additional context in comments.

Comments

4

Here's one way to avoid a lambda.

Given that std::condition_variable::wait offers both these overloads:

void wait( std::unique_lock<std::mutex>& lock );

template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate stop_waiting );

If you have some std::condition_variable cv, then calling cv.wait(lock, stop_waiting); is equivalent to:

while (!stop_waiting()) {
    cv.wait(lock);
}

So just don't provide the second argument stop_waiting (where one might provide a lambda) and instead call wait(lock) and check your condition manually in a loop. Changing g_cv.wait(ul, []() { return g_ready; }); to not use a lambda would simply look like this:

while(!g_ready) {
    g_cv.wait(ul);
}

refer to the docs

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.