Skip to content

Instantly share code, notes, and snippets.

@serg06
Last active August 14, 2024 15:39
Show Gist options
  • Select an option

  • Save serg06/3e9ca3eaca0da0559c499fc1c0dc45e5 to your computer and use it in GitHub Desktop.

Select an option

Save serg06/3e9ca3eaca0da0559c499fc1c0dc45e5 to your computer and use it in GitHub Desktop.
Simple header-only C++11 implementation of std::any (tested with GCC)
#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