Common Idioms / Design Patterns
References
- https://en.wikibooks.org/wiki/C%2B%2B_Programming/Idioms
- https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms
Background Thread Periodic Activity
Home-grown, but commonly used
class MyService {
public:
Status Start();
void Stop();
private:
void BackgroundThreadEntryPoint();
std::unique_ptr<std::thread> background_thread_;
absl::Notification stopping_notification_;
};
Status MyService::Start() {
background_thread_ = std::make_unique<std::thread>(
&MyService::BackgroundThreadEntryPoint, this);
return Status::OK();
}
void MyService::Stop() {
stopping_notification_.Notify();
if (background_thread_) {
background_thread_->join();
background_thread_.reset();
}
}
void MyService::BackgroundThreadEntryPoint() {
while (!stopping_notification_.WaitWithTimeout(
absl::Milliseconds(kLoopInterval))) {
// ...
}
}
Thread-safe Lazy-initialized Singleton
- https://source.chromium.org/chromium/chromium/src/+/main:base/lazy_instance.h;l=6;drc=7b5337170c1581e4a35399af36253f767674f581
- https://www.modernescpp.com/index.php/thread-safe-initialization-of-a-singleton
- Imperfect C++ by Matthew Wilson
Note
Unless we truly need a global object (for example GlobalBackgroundThreadPool
), we should avoid the Singleton pattern. Prefer passing required objects through constructors or setter functions to achieve better composability and testability.
Factory& Factory::GetInstance() {
static base::NoDestructor<Factory> instance;
return *instance;
}
Assume for now we ignore what base::NoDestructor
actually is and pretend it looks like this:
Factory& Factory::GetInstance() {
static Factory instance;
return instance;
}
- This only initializes the static
Factory
instance the first timeFactory::GetInstance()
is called. - Concurrent multi-threaded calls to
Factory::GetInstance()
are safe; the static instance is initialized exactly once.
TODO
- Explain why (thread-safe initialization semantics of function-local statics since C++11)
- Explain what
NoDestructor
does
Implementing Copy-On-Write using std::shared_ptr
From the book Linux Multithreaded Server Programming: Using the muduo C++ Network Library.
class MyQueue {
public:
// Omitted: constructor, destructor, other methods.
void Push(int v);
void Print();
private:
std::shared_ptr<std::vector<int>> queue_ ABSL_GUARDED_BY(mutex_);
absl::Mutex mutex_;
};
void MyQueue::Push(int v) {
absl::MutexLock lock(&mutex_);
if (!queue_.unique()) {
// Copy if other people reading the queue.
queue_ = std::make_shared<std::vector<int>>(*queue_);
}
// Now we ensure no other people accessing the queue.
DCHECK(queue_.unique());
queue_->emplace_back(v);
}
void MyQueue::Print() {
std::shared_ptr<std::vector<int>> the_queue;
{
absl::MutexLock lock(&mutex_);
the_queue = queue_;
}
for (int v : *the_queue) {
absl::PrintF("%d\n", v);
}
}