#ifndef INCLUDED_UTIL_GENERICVALUE_H #define INCLUDED_UTIL_GENERICVALUE_H #include "Util/Format.h" #include #include #include #include #include #include namespace Util { namespace detail { // Support for hexadecimal conversion for 16-bit or greater integers. // Cannot distinguish chars from 8-bit integers, so unsupported there. template struct IntegerEncodableAsHex { static constexpr const bool value = std::is_integral::value && sizeof(T) >= 2 && sizeof(T) <= 8; }; // This case should never actually be called template static typename std::enable_if::value, T>::type ParseInteger(const std::string &str) { return T(); } // This case will be generated for hex encodable integers and executed template static typename std::enable_if::value, T>::type ParseInteger(const std::string &str) { T tmp = 0; if (str.find_first_of("0x") == 0 || str.find_first_of("-0x") == 0 || str.find_first_of("+0x") == 0) { bool negative = str[0] == '-'; size_t start_at = 2 + ((negative || str[0] == '+') ? 1 : 0); for (size_t i = start_at; i < str.size(); i++) { tmp *= 16; char c = str[i]; if (isdigit(c)) tmp |= (c - '0'); else if (isupper(c)) tmp |= (c - 'A' + 10); else if (islower(c)) tmp |= (c - 'a' + 10); } if (negative) tmp *= -1; return tmp; } std::stringstream ss; ss << str; ss >> tmp; return tmp; } // This case should never actually be called template inline T ParseBool(const std::string &str) { return T(); } // This case will be generated for bools template <> inline bool ParseBool(const std::string &str) { if (!Util::Stricmp(str.c_str(), "true") || !Util::Stricmp(str.c_str(), "on") || !Util::Stricmp(str.c_str(), "yes")) return true; if (!Util::Stricmp(str.c_str(), "false") || !Util::Stricmp(str.c_str(), "off") || !Util::Stricmp(str.c_str(), "no")) return false; bool tmp; std::stringstream ss; ss << str; ss >> tmp; return tmp; } } class GenericValue { private: std::type_index m_type; virtual void *GetData() = 0; virtual const void *GetData() const = 0; public: template inline bool Is() const { return m_type == std::type_index(typeid(T)); } template const T &Value() const { if (!Is()) throw std::logic_error(Util::Format() << "GenericValue::Value(): cannot get value as " << std::type_index(typeid(T)).name() <<" because it is stored as " << m_type.name()); return *reinterpret_cast(GetData()); } template T ValueAs() const { if (m_type == std::type_index(typeid(T))) return *reinterpret_cast(GetData()); if (m_type == std::type_index(typeid(std::string)) && detail::IntegerEncodableAsHex::value) return detail::ParseInteger(Value()); // special case string -> integer conversion if (m_type == std::type_index(typeid(std::string)) && std::type_index(typeid(T)) == std::type_index(typeid(bool))) return detail::ParseBool(Value()); // special case string -> bool conversion std::stringstream ss; Serialize(&ss); T tmp; ss >> tmp; return tmp; } template void Set(const T &value) { if (!Is()) throw std::logic_error(Util::Format() << "GenericValue::Set(): cannot set value as " << std::type_index(typeid(T)).name() <<" because it is stored as " << m_type.name()); *reinterpret_cast(GetData()) = value; } void Set(const char *value) { Set(value); } virtual void Serialize(std::ostream *os) const = 0; virtual std::shared_ptr MakeCopy() const = 0; GenericValue(std::type_index type) : m_type(type) {} virtual ~GenericValue() { } }; template struct ValueInstance: public GenericValue { private: T m_data; void *GetData() { return reinterpret_cast(&m_data); } const void *GetData() const { return reinterpret_cast(&m_data); } public: void Serialize(std::ostream *os) const { *os << m_data; } std::shared_ptr MakeCopy() const { return std::make_shared>(*this); } ValueInstance(const ValueInstance &that) : GenericValue(std::type_index(typeid(T))), m_data(that.m_data) { } ValueInstance(const T &data) : GenericValue(std::type_index(typeid(T))), m_data(data) { } ValueInstance() : GenericValue(std::type_index(typeid(T))) { } ~ValueInstance() { //std::cout << "ValueInstance destroyed" << std::endl; } }; } // Util #endif // INCLUDED_UTIL_GENERICVALUE_H