Closing Channels in C++

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

// In this example we'll use a `jobs` queue to communicate work to be done 
// from the main thread to a worker thread. When we have no more jobs for
// the worker we'll signal the end of work.

int main() {
    std::queue<int> jobs;
    std::mutex mtx;
    std::condition_variable cv;
    bool done = false;
    bool all_jobs_sent = false;

    // Here's the worker thread. It repeatedly receives from `jobs`.
    // We use a condition variable to notify when we've worked all our jobs.
    std::thread worker([&]() {
        while (true) {
            std::unique_lock<std::mutex> lock(mtx);
            cv.wait(lock, [&]{ return !jobs.empty() || all_jobs_sent; });

            if (!jobs.empty()) {
                int j = jobs.front();
                jobs.pop();
                lock.unlock();
                std::cout << "received job " << j << std::endl;
            } else if (all_jobs_sent) {
                std::cout << "received all jobs" << std::endl;
                done = true;
                cv.notify_one();
                return;
            }
        }
    });

    // This sends 3 jobs to the worker over the `jobs` queue,
    // then signals that all jobs are sent.
    for (int j = 1; j <= 3; ++j) {
        {
            std::lock_guard<std::mutex> lock(mtx);
            jobs.push(j);
        }
        cv.notify_one();
        std::cout << "sent job " << j << std::endl;
    }

    {
        std::lock_guard<std::mutex> lock(mtx);
        all_jobs_sent = true;
    }
    cv.notify_one();
    std::cout << "sent all jobs" << std::endl;

    // We await the worker using the condition variable.
    {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, [&]{ return done; });
    }

    // In C++, we don't have a direct equivalent to checking if a channel is closed.
    // Instead, we can check if the queue is empty and all jobs have been sent.
    bool more_jobs = !jobs.empty() || !all_jobs_sent;
    std::cout << "received more jobs: " << (more_jobs ? "true" : "false") << std::endl;

    worker.join();
    return 0;
}

To compile and run this program:

$ g++ -std=c++11 -pthread closing_channels.cpp -o closing_channels
$ ./closing_channels
sent job 1
received job 1
sent job 2
received job 2
sent job 3
received job 3
sent all jobs
received all jobs
received more jobs: false

In this C++ version, we use a std::queue to represent the channel of jobs. We use a mutex and a condition variable to synchronize access to the queue and to signal between threads.

The close operation on a channel is simulated by setting a flag all_jobs_sent to true. The worker thread checks this flag to know when to stop waiting for new jobs.

The concept of receiving from a closed channel is approximated by checking if the queue is empty and all jobs have been sent. This is not a perfect analogy, but it serves a similar purpose in this context.

Note that C++ doesn’t have built-in constructs that directly correspond to Go’s channels and goroutines. Instead, we use standard library components like std::thread, std::mutex, and std::condition_variable to achieve similar functionality.