Skip to content

Instantly share code, notes, and snippets.

@tolgayilmaz86
Last active November 17, 2024 17:16
Show Gist options
  • Save tolgayilmaz86/00b7d9fb73c9b03b71620619bf9da439 to your computer and use it in GitHub Desktop.
Save tolgayilmaz86/00b7d9fb73c9b03b71620619bf9da439 to your computer and use it in GitHub Desktop.
๐—ฆ๐˜๐—ผ๐—ฟ๐—ถ๐—ป๐—ด ๐—”๐—ป๐˜† ๐—ข๐—ฏ๐—ท๐—ฒ๐—ฐ๐˜ ๐—ถ๐—ป ๐—ฎ ๐—–๐—ผ๐—ป๐˜๐—ฎ๐—ถ๐—ป๐—ฒ๐—ฟ ๐—ช๐—ถ๐˜๐—ต๐—ผ๐˜‚๐˜ ๐šŸ๐š˜๐š’๐š* ๐—ผ๐—ฟ ๐—›๐—ถ๐—ฒ๐—ฟ๐—ฎ๐—ฟ๐—ฐ๐—ต๐—ถ๐—ฒ๐˜€
/*
If you're looking to store any type of object in a container without using ๐šŸ๐š˜๐š’๐š*
or creating a new hierarchy, ๐šœ๐š๐š::๐šŠ๐š—๐šข might be a good choice.
It can store any type, but retrieving the stored value requires caution,
as you need to know the type to use ๐šœ๐š๐š::๐šŠ๐š—๐šข_๐šŒ๐šŠ๐šœ๐š effectively.
One useful application for this approach is an object storage class that maintains
a list of singletons. This class, ๐š‚๐š’๐š—๐š๐š•๐šŽ๐š๐š˜๐š—๐š‚๐š๐š˜๐š›๐šŠ๐š๐šŽ,
offers a ๐š๐šŽ๐š method that either returns an existing object or creates
a new one if it doesnโ€™t exist. Internally,
๐š‚๐š’๐š—๐š๐š•๐šŽ๐š๐š˜๐š—๐š‚๐š๐š˜๐š›๐šŠ๐š๐šŽ uses a hash map with ๐šœ๐š๐š::๐š๐šข๐š™๐šŽ_๐š’๐š—๐š๐šŽ๐šก as the key and ๐šœ๐š๐š::๐šŠ๐š—๐šข as the value.
Check the code below for more details.
The example below depicts a SingletonStorage that is created upon the first request for an object.
Note that stored objects should have a copy constructor.
However, if a noexcept move constructor is present, it will be used to move the created value into the ๐‘ ๐‘ก๐‘‘::๐‘Ž๐‘›๐‘ฆ.
*/
#include <iostream>
#include <unordered_map>
#include <any>
#include <typeindex>
#include <memory>
#include <stdexcept>
#include <mutex>
class SingletonStorage {
public:
// Template method to get or create an instance of the specified type
template <typename T>
T& get() {
std::type_index typeIndex(typeid(T));
// Check if the object already exists
auto it = storage.find(typeIndex);
if (it != storage.end()) {
// Retrieve and cast the stored object
return std::any_cast<T&>(it->second);
}
// Create a new instance if not found
// noexcept move constructor will be used, but type should be copieable
auto insert = storage.emplace(typeIndex, std::make_any<T>());
// Return a reference
return std::any_cast<T&>(insert.first->second);
}
// Delete copy and assignment operators to ensure singleton behavior
SingletonStorage(const SingletonStorage&) = delete;
SingletonStorage& operator=(const SingletonStorage&) = delete;
static SingletonStorage& instance() {
static SingletonStorage instance;
return instance;
}
private:
SingletonStorage() = default;
std::unordered_map<std::type_index, std::any> storage; // Map of type-indexed singletons
};
// Example classes to be managed by SingletonStorage
class MyClass1 {
public:
void display() const {
std::cout << "MyClass1 instance\n";
}
};
class MyClass2 {
public:
void show() const {
std::cout << "MyClass2 instance\n";
}
};
int main() {
// Retrieve and use instances of MyClass1 and MyClass2 from SingletonStorage
auto& obj1 = SingletonStorage::instance().get<MyClass1>();
auto& obj2 = SingletonStorage::instance().get<MyClass2>();
obj1.display(); // Output: MyClass1 instance
obj2.show(); // Output: MyClass2 instance
// Retrieve the same instances again to verify singleton behavior
auto& sameObj1 = SingletonStorage::instance().get<MyClass1>();
auto& sameObj2 = SingletonStorage::instance().get<MyClass2>();
// The same instances are used, so no new output
sameObj1.display(); // Output: MyClass1 instance
sameObj2.show(); // Output: MyClass2 instance
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment