Newer
Older
invertedlogic / invertedlogic / iLFramework / toolkit / include / Variant.h
@John Ryland John Ryland on 10 Nov 2019 5 KB add framework
#ifndef VARIANT_H
#define VARIANT_H


#include <string.h>
#include <new>
#include <string>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <sstream>
#include "Utils.h"


#include "Namespace.h"
BEGIN_NAMESPACE


typedef bool          boolean_t;
typedef float         float32_t;
typedef double        float64_t;
typedef void*         pointer_t;
typedef const char*   cstring_t;
typedef std::string   string_t;
//typedef enum          enum_t;

class Variant;
//typedef Vector<Variant*>  ArgList_t;

// TODO: User types
// TODO: other types, datetime, color, 3d points, matrixes, ???

enum VariantType
{
#define VARIANT_TYPES \
	/* VARIANT_MACRO(ArgList_t) */ VARIANT_MACRO(boolean_t) VARIANT_MACRO(char)      VARIANT_MACRO(wchar_t) \
	VARIANT_MACRO(int8_t)    VARIANT_MACRO(int16_t)   VARIANT_MACRO(int32_t)   VARIANT_MACRO(int64_t)  \
	VARIANT_MACRO(uint8_t)   VARIANT_MACRO(uint16_t)  VARIANT_MACRO(uint32_t)  VARIANT_MACRO(uint64_t) \
	VARIANT_MACRO(float32_t) VARIANT_MACRO(float64_t) VARIANT_MACRO(pointer_t) VARIANT_MACRO(cstring_t) \
	VARIANT_MACRO(String)    VARIANT_MACRO(WidgetFlags)
	//VARIANT_MACRO(enum_t) VARIANT_MACRO(string_t) 
#define VARIANT_MACRO(type) \
			VT_##type,
VARIANT_TYPES
#undef VARIANT_MACRO
	VT_Unknown
};

template<typename T>
VariantType variantTypeFromType();

/*
const size_t maxTypeSizes[] = {
#define VARIANT_MACRO(type) \
			sizeof(type),
VARIANT_TYPES
#undef VARIANT_MACRO
};
const int maxTypeSizesLength = ARRAY_SIZE(maxTypeSizes);
*/
const size_t maxTypeSize = 80;//*std::max_element(maxTypeSizes, maxTypeSizes + maxTypeSizesLength);


class Variant
{
public:
    template<typename T>
    void setValue(const T &a_val)
	{
		m_type = variantTypeFromType<T>();
		m_null = false;
		STATIC_ASSERT(sizeof(T) <= maxTypeSize, typeTooBig);
		memset(m_data, 0, maxTypeSize);
		new (m_data)T(a_val);
	}

	template<typename T>
	const T variant_cast() const {
		STATIC_ASSERT(sizeof(T) <= maxTypeSize, typeTooBig);
		if (VariantType(m_type) == variantTypeFromType<T>())
			return reinterpret_cast<const T&>(m_data);
		// Next try canConvert and then convert?
		return T();
	}

	Variant()                                         { m_type = VT_Unknown; m_null = true; }
	
	~Variant()
	{
		if (m_type == VT_String) {
			String* v = reinterpret_cast<String*>(m_data);
			v->~String(); // Properly tidy up,  TODO: need to do this for all non-basic types
		}
	}

	template<typename T>
	Variant(const T& a_val)                           { setValue(a_val); }

	template<typename T>
	operator const T() const                          { return variant_cast<T>(); }

    template<typename T>
    const T value() const                             { return variant_cast<T>(); }

	operator const char*() const					  { m_str = toString(); return m_str.c_str(); } // TODO
	//operator void()                                   { return; }

	bool isNull() const                               { return m_null; }
	bool isValid() const                              { return !m_null && (m_type != VT_Unknown); }

	VariantType type() const                          { return (VariantType)m_type; }
	const char* typeName() const;

    bool operator==(const Variant &v) const           { return compareEquality(v); }
    bool operator!=(const Variant &v) const           { return !compareEquality(v); }
    
	// TODO: probably just comparing memory is not correct, what if it is a pointer to a string for example
	bool compareEquality(const Variant &v) const      { return (m_type == v.m_type) && (memcmp(m_data, v.m_data, maxTypeSize) == 0); }

	static VariantType nameToType(const char* a_name);
	static const char* typeToName(VariantType a_type);

    template<typename T>
    static Variant fromValue(const T &value)          { return Variant(value); }

	String toString() const;
	
	/*
	static Variant fromString(std::string str)
	{
		for each type
			tryParse<T>(str)
	}
	*/
	
private:
	mutable std::string m_str;  // TODO: should see if there is a way to avoid needing this member variable needed for const char* cast
	char          m_data[maxTypeSize];
	uint32_t      m_type : 31;
	uint32_t      m_null : 1;

/*
    int userType() const;

    bool canConvert(VariantType t) const;
    bool convert(VariantType t);	

	void load(QDataStream &ds);
    void save(QDataStream &ds) const;

    void *data();
    const void *data() const { return constData(); }

    template<typename T>
    bool canConvert() const
    { return canConvert(VariantType(qMetaTypeId<T>())); }

*/
};


// Experimentation with idea of smart enums
class EnumBaseBase
{
public:
	struct EnumMaps;
	EnumBaseBase() : value(0) {}
	static const String& toStringHelper(const EnumMaps& a_maps, int a_val);
	static int fromStringHelper(const EnumMaps& a_maps, const String& a_str);
	static EnumMaps& initMapsHelper(const char* enumName, const char* enumValues);
    int value;
};


template <class Derived>
class EnumBase : public EnumBaseBase
{
public:
    //operator int () const { return value; }
	operator const char* () const					  { return toStringHelper(initMaps(), value).data(); }
	String toString() const				         	  { return toStringHelper(initMaps(), value); }
	static const String& toString(Derived a_val)      { return toStringHelper(initMaps(), a_val.value); }
	static int fromString(const String& a_str)        { return fromStringHelper(initMaps(), a_str); }
	static Vector<int> allValues()					  { return initMaps().valueList; }
	static EnumMaps& initMaps()	{ return initMapsHelper(Derived::enumName(), Derived::enumValues()); }
};


#define DECLARE_ENUM(EnumName, ...) \
	class EnumName : public EnumBase<EnumName> \
	{ \
	public: \
		enum						    { __VA_ARGS__ }; \
		EnumName(int a_val)			    { value = a_val; } \
		static const char* enumName()   { return #EnumName; } \
		static const char* enumValues() { return #__VA_ARGS__; } \
	};


END_NAMESPACE


#endif // VARIANT_H