Skip to content

Commit

Permalink
add singleton base class
Browse files Browse the repository at this point in the history
  • Loading branch information
K1ngst0m committed Oct 28, 2023
1 parent 93584d6 commit 107d579
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
33 changes: 33 additions & 0 deletions engine/common/singleton.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef APH_SINGLETON_H
#define APH_SINGLETON_H

#include <mutex>
#include <memory>
#include <stdexcept>

namespace aph
{

template <typename Derived>
class Singleton
{
public:
static Derived& GetInstance()
{
// Meyers' Singleton with guaranteed thread-safety since C++11
static Derived instance;
return instance;
}

Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) noexcept = delete;
Singleton& operator=(Singleton&&) noexcept = delete;

protected:
Singleton() = default;
virtual ~Singleton() = default;
};

} // namespace aph
#endif
70 changes: 70 additions & 0 deletions tests/singleton.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include "common/singleton.h"
#include <catch2/catch_test_macros.hpp>
#include <thread>
#include <set>

using namespace aph;

class MySingleton : public Singleton<MySingleton>
{
friend class Singleton<MySingleton>;

public:
void setValue(int v) { value = v; }
int getValue() const { return value; }

private:
MySingleton() = default;
int value{};
};

TEST_CASE("Basic Usage of Singleton", "[Singleton]")
{
SECTION("Ensure we can create an instance")
{
MySingleton& instance1 = MySingleton::GetInstance();
instance1.setValue(10);
REQUIRE(instance1.getValue() == 10);
}

SECTION("Ensure repeated calls return same instance")
{
MySingleton& instance1 = MySingleton::GetInstance();
MySingleton& instance2 = MySingleton::GetInstance();
REQUIRE(&instance1 == &instance2); // Compare addresses
}
}

TEST_CASE("Thread Safety of Singleton", "[Singleton]")
{
std::vector<std::thread> threads;
std::vector<MySingleton*> addresses;

// Mutex to ensure synchronized access to the addresses vector
std::mutex mtx;

const int numThreads = 100;

for (int i = 0; i < numThreads; ++i)
{
threads.push_back(std::thread([&]()
{
MySingleton& instance = MySingleton::GetInstance();
std::lock_guard<std::mutex> lock(mtx);
addresses.push_back(&instance);
}));
}

for (auto& thread : threads)
{
thread.join();
}

SECTION("All threads get the same instance")
{
for (std::size_t i = 1; i < addresses.size(); ++i)
{
REQUIRE(addresses[i] == addresses[0]);
}
}
}

0 comments on commit 107d579

Please sign in to comment.