Created
June 13, 2024 05:59
-
-
Save DrkWithT/07768afd56accd456095d4902a8eb83f to your computer and use it in GitHub Desktop.
A toy C++ Any implementation for educational purposes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef MY_ANY_HPP | |
#define MY_ANY_HPP | |
#include <stdexcept> | |
#include <type_traits> | |
#include <utility> | |
#include <memory> | |
#include <typeinfo> | |
/* | |
GitHub: DrkWithT | |
Replit: D3rkPlusPlus | |
A remake of std::any from the standard library of C++17. :) | |
Inspired by: https://codereview.stackexchange.com/questions/219075/implementation-of-stdany | |
*/ | |
class Any | |
{ | |
private: | |
/* Polymorphic storage classtypes with wrapped value and type info. */ | |
struct IStorage | |
{ | |
virtual ~IStorage() = default; | |
virtual const std::type_info& getTypeInfo() const = 0; | |
virtual std::unique_ptr<IStorage> getClone() = 0; | |
virtual const void* getRawPtr() const = 0; | |
}; | |
template <typename ValueType> | |
struct StorageImpl : public IStorage | |
{ | |
ValueType value; | |
constexpr StorageImpl() | |
: value {} {} | |
StorageImpl(ValueType& arg) | |
: value (std::forward<const ValueType&>(arg)) {} | |
StorageImpl(ValueType&& arg) | |
: value (std::forward(arg)) {} | |
const std::type_info& getTypeInfo() const override | |
{ | |
return typeid(ValueType); | |
} | |
std::unique_ptr<IStorage> getClone() override | |
{ | |
return std::make_unique<StorageImpl>(std::forward<ValueType&>(value)); | |
} | |
const void* getRawPtr() const override | |
{ | |
return &value; | |
} | |
}; | |
/* Storage smart ptr. does RAII for easy cleanup of whatever value within no matter its type! */ | |
std::unique_ptr<IStorage> storage_ptr; | |
public: | |
/* Helper Methods */ | |
[[nodiscard]] bool hasValue() const { return storage_ptr != nullptr; } | |
template <typename CheckType> | |
[[nodiscard]] bool hasType() const | |
{ | |
if (!hasValue()) | |
return false; | |
return typeid(CheckType) == storage_ptr->getTypeInfo(); | |
} | |
void swapWith(Any& other) noexcept | |
{ | |
this->storage_ptr.swap(other.storage_ptr); | |
} | |
/* Constructors */ | |
constexpr Any() | |
: storage_ptr {nullptr} {} | |
template <typename ArgType> | |
Any(ArgType& arg) | |
: storage_ptr (std::make_unique<StorageImpl<ArgType>>(arg)) {} | |
template <typename ArgType> | |
Any(ArgType&& arg) | |
: storage_ptr(std::make_unique<StorageImpl<ArgType>>(std::forward<ArgType>(arg))) {} | |
Any(const Any& other) | |
{ | |
if (&other == this) | |
return; | |
storage_ptr = (other.hasValue()) | |
? other.storage_ptr->getClone() | |
: nullptr; | |
} | |
Any& operator=(const Any& other) | |
{ | |
if (&other == this) | |
return *this; | |
storage_ptr = (other.hasValue()) | |
? other.storage_ptr->getClone() | |
: nullptr; | |
return *this; | |
} | |
Any(Any&& x_other) noexcept | |
: storage_ptr (std::move(x_other.storage_ptr)) {} | |
Any& operator=(Any&& x_other) noexcept | |
{ | |
storage_ptr = std::move(x_other.storage_ptr); | |
return *this; | |
} | |
/* Helper Functions */ | |
template <typename ResultType> | |
friend ResultType unpackAny(const Any& arg) | |
{ | |
if (arg.hasType<ResultType>()) | |
{ | |
if (arg.hasValue()) | |
{ | |
const ResultType* data_ptr = static_cast<const ResultType*>(arg.storage_ptr->getRawPtr()); | |
return *data_ptr; | |
} | |
} | |
throw std::invalid_argument {"Any: No value or invalid type!"}; | |
} | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment