Last active
August 14, 2024 15:39
-
-
Save serg06/3e9ca3eaca0da0559c499fc1c0dc45e5 to your computer and use it in GitHub Desktop.
Simple header-only C++11 implementation of std::any (tested with GCC)
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
| #include <functional> | |
| #include <typeinfo> | |
| #include <type_traits> | |
| namespace std17 | |
| { | |
| class bad_any_cast : public std::bad_cast | |
| { | |
| public: | |
| bad_any_cast() noexcept = default; | |
| const char* what() const noexcept override { return "Unexpected type."; } | |
| }; | |
| // Don't need this outside of std17::any, I just think it looks cleaner defined outside. Might be smarter to move it inside. | |
| struct obj_modifiers | |
| { | |
| obj_modifiers() | |
| { | |
| reset(); | |
| } | |
| template<typename T> | |
| void set_modifiers() | |
| { | |
| deleter = [](void* ptr){ delete static_cast<T*>(ptr); }; | |
| copy_constructor = [](void*& ptr, void* other){ ptr = new T(*static_cast<T*>(other)); }; | |
| move_constructor = [](void*& ptr, void* other){ ptr = new T(std::move(*static_cast<T*>(other))); }; | |
| copy_assigner = [](void*& ptr, void* other){ *static_cast<T*>(ptr) = *static_cast<T*>(other); }; | |
| move_assigner = [](void*& ptr, void* other){ *static_cast<T*>(ptr) = std::move(*static_cast<T*>(other)); }; | |
| } | |
| void reset() | |
| { | |
| deleter = [](void* ptr){}; | |
| copy_constructor = [](void*& ptr, void* other){}; | |
| move_constructor = [](void*& ptr, void* other){}; | |
| copy_assigner = [](void*& ptr, void* other){}; | |
| move_assigner = [](void*& ptr, void* other){}; | |
| } | |
| std::function<void(void*)> deleter; | |
| std::function<void(void*&, void*)> copy_constructor; | |
| std::function<void(void*&, void*)> move_constructor; | |
| std::function<void(void*&, void*)> copy_assigner; | |
| std::function<void(void*&, void*)> move_assigner; | |
| }; | |
| class any | |
| { | |
| template<class T> | |
| friend T any_cast(const any& operand); | |
| template<class T> | |
| friend T any_cast(any& operand); | |
| template<class T> | |
| friend T any_cast(any&& operand); | |
| template<typename T> | |
| friend const T* any_cast(const any* operand) noexcept; | |
| template<typename T> | |
| friend T* any_cast(any* operand) noexcept; | |
| public: | |
| any() : obj(nullptr), type(&typeid(void)) | |
| { | |
| } | |
| any(const any& other) : obj(nullptr), type(other.type), modifiers(other.modifiers) | |
| { | |
| modifiers.copy_constructor(obj, other.obj); | |
| } | |
| any(any&& other) noexcept : obj(nullptr), type(&typeid(void)) | |
| { | |
| std::swap(obj, other.obj); | |
| std::swap(type, other.type); | |
| std::swap(modifiers, other.modifiers); | |
| } | |
| template< | |
| typename T, | |
| typename = typename std::enable_if< | |
| !std::is_same< | |
| any, | |
| typename std::remove_cv< | |
| typename std::remove_reference<T>::type | |
| >::type | |
| >::value | |
| >::type | |
| > | |
| any(T&& t) : obj(nullptr), type(&typeid(void)) | |
| { | |
| using U = typename std::remove_cv<typename std::remove_reference<T>::type>::type; | |
| emplace<U>(std::forward<T>(t)); | |
| } | |
| ~any() | |
| { | |
| reset(); | |
| } | |
| any& operator=(const any& rhs) | |
| { | |
| if (this != &rhs) | |
| { | |
| // if types match, just do normal copy, no allocation | |
| if (type == rhs.type) | |
| { | |
| modifiers.copy_assigner(obj, rhs.obj); | |
| } | |
| // otherwise, clear and recreate | |
| else | |
| { | |
| reset(); | |
| modifiers.copy_constructor(obj, rhs.obj); | |
| } | |
| type = rhs.type; | |
| modifiers = rhs.modifiers; | |
| } | |
| return *this; | |
| } | |
| any& operator=(any&& rhs) noexcept | |
| { | |
| if (this != &rhs) | |
| { | |
| std::swap(obj, rhs.obj); | |
| std::swap(type, rhs.type); | |
| std::swap(modifiers, rhs.modifiers); | |
| } | |
| return *this; | |
| } | |
| template< | |
| typename T, | |
| typename = typename std::enable_if< | |
| !std::is_same< | |
| any, | |
| typename std::remove_cv< | |
| typename std::remove_reference<T>::type | |
| >::type | |
| >::value | |
| >::type | |
| > | |
| any& operator=(T&& t) | |
| { | |
| using U = typename std::remove_cv<typename std::remove_reference<T>::type>::type; | |
| emplace<U>(std::forward<T>(t)); | |
| } | |
| void reset() | |
| { | |
| if (obj) | |
| { | |
| modifiers.deleter(obj); | |
| obj = nullptr; | |
| type = &typeid(void); | |
| modifiers.reset(); | |
| } | |
| } | |
| template<class T, class ...Args> | |
| void emplace(Args&&... args) | |
| { | |
| // if types match | |
| if (type == &typeid(T)) | |
| { | |
| // assign | |
| *static_cast<T*>(obj) = T(std::forward<Args>(args)...); | |
| } | |
| // otherwise reset and create fresh | |
| else | |
| { | |
| reset(); | |
| obj = new T(std::forward<Args>(args)...); | |
| type = &typeid(T); | |
| modifiers.set_modifiers<T>(); | |
| } | |
| } | |
| private: | |
| void* obj; | |
| const std::type_info* type; | |
| obj_modifiers modifiers; | |
| }; | |
| template<class T> | |
| T any_cast(const any& operand) | |
| { | |
| using U = typename std::remove_cv<typename std::remove_reference<T>::type>::type; | |
| if (&typeid(U) == operand.type) | |
| return *static_cast<T*>(operand.obj); | |
| else | |
| throw bad_any_cast(); | |
| } | |
| template<class T> | |
| T any_cast(any& operand) | |
| { | |
| using U = typename std::remove_cv<typename std::remove_reference<T>::type>::type; | |
| if (&typeid(U) == operand.type) | |
| return *static_cast<T*>(operand.obj); | |
| else | |
| throw bad_any_cast(); | |
| } | |
| template<class T> | |
| T any_cast(any&& operand) | |
| { | |
| using U = typename std::remove_cv<typename std::remove_reference<T>::type>::type; | |
| if (&typeid(U) == operand.type) | |
| return std::move(*static_cast<T*>(operand.obj)); | |
| else | |
| throw bad_any_cast(); | |
| } | |
| template<typename T> | |
| const T* any_cast(const any* operand) noexcept | |
| { | |
| if (&typeid(T) == operand.type) | |
| return static_cast<T*>(operand->obj); | |
| else | |
| return nullptr; | |
| } | |
| template<typename T> | |
| T* any_cast(any* operand) noexcept | |
| { | |
| if (&typeid(T) == operand.type) | |
| return static_cast<T*>(operand->obj); | |
| else | |
| return nullptr; | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment