From: Mikko Rasa Date: Fri, 23 Dec 2022 12:27:01 +0000 (+0200) Subject: Rewrite the Variant class X-Git-Url: http://git.tdb.fi/?a=commitdiff_plain;h=e42c69a8d3416be5071637cd4e241593458cb941;p=libs%2Fcore.git Rewrite the Variant class It now uses type erasure and small object optimization --- diff --git a/source/core/variant.h b/source/core/variant.h index eaf5a34..04c372b 100644 --- a/source/core/variant.h +++ b/source/core/variant.h @@ -17,109 +17,159 @@ public: class Variant { -private: - struct StoreBase - { - virtual ~StoreBase() { } +public: + static constexpr unsigned INTERNAL_SIZE = 2*sizeof(void *); - virtual const std::type_info &type_id() const = 0; - virtual StoreBase *clone() const = 0; - virtual bool type_equals(const StoreBase &) const = 0; - virtual bool value_equals(const StoreBase &) const = 0; + struct Functions + { + const std::type_info &(*get_type)(); + bool (*compare)(const char *, const char *); + void (*clone)(char *, const char *); + void (*destroy)(char *); }; +private: + const Functions *funcs = nullptr; + alignas(void *) char storage[INTERNAL_SIZE]; + +public: + Variant() = default; template - struct Store: public StoreBase - { - T data; + Variant(const T &v) { assign(v); } + Variant(const Variant &v) { copy_from(v); } + ~Variant() { if(funcs) funcs->destroy(storage); } - Store(const T &d): data(d) { } + template + Variant &operator=(const T &v) { assign(v); return *this; } - const std::type_info &type_id() const override { return typeid(T); } - StoreBase *clone() const override { return new Store(data); } - bool type_equals(const StoreBase &s) const override { return dynamic_cast *>(&s); } - bool value_equals(const StoreBase &s) const override { return _value_equals(s); } + Variant &operator=(const Variant &v) { if(&v!=this) copy_from(v); return *this; } - template - typename std::enable_if::value, bool>::type _value_equals(const StoreBase &s) const - { const Store *t = dynamic_cast *>(&s); return (t && t->data==data); } +private: + template + void assign(const T &); - template - typename std::enable_if::value, bool>::type _value_equals(const StoreBase &) const - { return false; } - }; + void copy_from(const Variant &); - StoreBase *store = nullptr; + template + T &get(); public: - Variant() = default; template - Variant(const T &v): store(new Store::type>(v)) { } - Variant(const Variant &v): store(v.store ? v.store->clone() : nullptr) { } - ~Variant() { delete store; } + T &value() { return get(); } template - Variant &operator=(const T &v) - { - delete store; - store = new Store::type>(v); - return *this; - } + const T &value() const { return const_cast(this)->get(); } - Variant &operator=(const Variant &v) - { - if(&v==this) - return *this; + template + bool check_type() const { return funcs==get_functions::type>(); } - delete store; - store = (v.store ? v.store->clone() : nullptr); - return *this; - } + bool check_same_type(const Variant &v) const { return (funcs && funcs==v.funcs); } + + bool operator==(const Variant &v) const { return (has_same_type(v) && funcs->compare(storage, v.storage)); } + bool operator!=(const Variant &v) const { return !(operator==(v)); } + + template + operator T() const { return value(); } private: template - Store::type> *get_typed_store() const - { - typedef typename std::remove_cv::type NCT; - Store *s = dynamic_cast *>(store); - if(!s) - throw type_mismatch(typeid(T), (store ? store->type_id() : typeid(void))); - return s; - } + static constexpr bool is_small() { return (sizeof(T)<=INTERNAL_SIZE && alignof(T)<=alignof(void *)); } + + template + using EnableSmall = typename std::enable_if(), U>::type; + + template + using EnableLarge = typename std::enable_if(), U>::type; -public: template - T &value() - { - return get_typed_store()->data; - } + static const Functions *get_functions(); template - const T &value() const - { - return get_typed_store()->data; - } + static const std::type_info &get_type() { return typeid(T); } template - bool check_type() const - { - return dynamic_cast::type> *>(store); - } + static EnableSmall create(char *s, const T &v) + { new(s) T(v); } - bool check_same_type(const Variant &v) const - { return store && v.store && store->type_equals(*v.store); } + template + static EnableLarge create(char *s, const T &v) + { *reinterpret_cast(s) = new T(v); } - bool operator==(const Variant &v) const - { return store && v.store && store->value_equals(*v.store); } + template + static typename std::enable_if::value, bool>::type compare(const char *, const char *) + { return false; } - bool operator!=(const Variant &v) const - { return !(operator==(v)); } + template + static typename std::enable_if::value, EnableSmall>::type compare(const char *s1, const char *s2) + { return *reinterpret_cast(s1)==*reinterpret_cast(s2); } template - operator T() const - { return value(); } + static typename std::enable_if::value, EnableLarge>::type compare(const char *s1, const char *s2) + { return **reinterpret_cast(s1)==**reinterpret_cast(s2); } + + template + static EnableSmall clone(char *s, const char *v) + { new(s) T(*reinterpret_cast(v)); } + + template + static EnableLarge clone(char *s, const char *v) + { *reinterpret_cast(s) = new T(**reinterpret_cast(v)); } + + template + static EnableSmall destroy(char *s) + { reinterpret_cast(s)->~T(); } + + template + static EnableLarge destroy(char *s) + { delete *reinterpret_cast(s); } }; + +template +inline void Variant::assign(const T &value) +{ + if(funcs) + funcs->destroy(storage); + + funcs = get_functions::type>(); + create(storage, value); +} + +inline void Variant::copy_from(const Variant &v) +{ + if(funcs) + funcs->destroy(storage); + + funcs = v.funcs; + if(funcs) + funcs->clone(storage, v.storage); +} + +template +inline T &Variant::get() +{ + if(!has_type()) + throw type_mismatch(typeid(T), (funcs ? funcs->get_type() : typeid(void))); + + if(sizeof(T)<=INTERNAL_SIZE) + return *reinterpret_cast(storage); + else + return **reinterpret_cast(storage); +} + +template +inline const Variant::Functions *Variant::get_functions() +{ + static Functions funcs = + { + &get_type, + &compare, + &clone, + &destroy + }; + return &funcs; +} + } // namespace Msp #endif