#include <sstream>
#include "Widget.h"
#include "Variant.h"
BEGIN_NAMESPACE
class VariantTypeMapper
{
public:
VariantTypeMapper();
VariantType lookup(const char* a_name) { VariantType def = VT_Unknown; return m_variantTypeMap.forwardFind(a_name, def); }
const char* lookup(VariantType a_type) { const char* def = "Unknown"; return m_variantTypeMap.reverseFind(a_type, def); }
private:
BidirectionalMap<const char*,VariantType> m_variantTypeMap;
};
VariantTypeMapper::VariantTypeMapper()
{
#define VARIANT_MACRO(type) \
m_variantTypeMap.insert(#type, VT_##type);
VARIANT_TYPES
#undef VARIANT_MACRO
m_variantTypeMap.insert("Unknown", VT_Unknown);
}
VariantTypeMapper g_variantTypeMapper;
template<typename T>
VariantType variantTypeFromType() { return VT_Unknown; }
#define VARIANT_MACRO(type) \
template<> \
VariantType variantTypeFromType<type>() { return VT_##type; }
VARIANT_TYPES
#undef VARIANT_MACRO
/*
template<>
VariantType variantTypeFromType<WidgetFlags>() { return VT_enum_t; }
template<>
VariantType variantTypeFromType<enum_t>() { return VT_enum_t; }
template<>
const enum_t Variant::variant_cast<enum_t>() const
{
STATIC_ASSERT(sizeof(enum_t) <= maxTypeSize, typeTooBig);
if (VariantType(m_type) == variantTypeFromType<enum_t>())
return reinterpret_cast<const enum_t&>(m_data);
// Next try canConvert and then convert?
return enum_t();
}
*/
/*
void operator<<(std::ostringstream& s, const ArgList_t& args)
{
s << "ArgList_t streaming not implemented";
}
*/
void operator<<(std::ostringstream& s, String& str)
{
s << str.data();
}
String Variant::toString() const
{
if (VariantType(m_type) == VT_boolean_t)
{
return (variant_cast<boolean_t>()) ? "true" : "false";
}
/*
else if (VariantType(m_type) == VT_wchar_t)
{
wchar_t str[2] = { variant_cast<wchar_t>(), 0 };
return String(str);
}
*/
std::ostringstream s;
switch (VariantType(m_type))
{
#define VARIANT_MACRO(type) \
case VT_##type: s << variant_cast<type>(); break;
VARIANT_TYPES
#undef VARIANT_MACRO
case VT_Unknown: break;
}
return String(s.str().c_str());
}
const char* Variant::typeName() const
{
return g_variantTypeMapper.lookup((VariantType)m_type);
}
VariantType Variant::nameToType(const char* a_name)
{
return g_variantTypeMapper.lookup(a_name);
}
const char* Variant::typeToName(VariantType a_type)
{
return g_variantTypeMapper.lookup((VariantType)a_type);
}
END_NAMESPACE
#include "Test.h"
#include <climits>
BEGIN_NAMESPACE
UNIT_TEST(VariantTests, 0)
{
Variant v0(true);
Variant v1('a');
//Variant v2(L'a');
Variant v3(-12345);
Variant v4(UINT_MAX);
Variant v5(9223372036854775808ULL);
Variant v6(0.0001f);
Variant v7(0.0001);
CHECK(String(v0.typeName()) == "boolean_t");
CHECK(String(v1.typeName()) == "char");
//CHECK(String(v2.typeName()) == "wchar_t");
CHECK(String(v3.typeName()) == "int32_t");
CHECK(String(v4.typeName()) == "uint32_t");
CHECK(String(v5.typeName()) == "uint64_t");
CHECK(String(v6.typeName()) == "float32_t");
CHECK(String(v7.typeName()) == "float64_t");
CHECK(v0.toString() == "true");
CHECK(v1.toString() == "a");
//CHECK(v2.toString() == "a");
CHECK(v3.toString() == "-12345");
CHECK(v4.toString() == "4294967295");
CHECK(v5.toString() == "9223372036854775808");
CHECK(v6.toString() == "0.0001");
CHECK(v7.toString() == "0.0001");
}
END_NAMESPACE
#if 0
#include <iostream>
#include <utility>
BEGIN_NAMESPACE
// Experimentation with idea of a smart enum
struct EnumBaseBase::EnumMaps
{
BidirectionalMap<int,String> valueNameMap;
Vector<int> valueList;
};
static void baseInitMaps(const char* a_str, EnumBaseBase::EnumMaps& maps)
{
if (maps.valueNameMap.size() == 0)
{
// TODO: there might be some much more efficient string parsing and marking the sub-strings
// of a_str of where the names begin/end that doesn't involve a lot of string copying
int val = 0;
Vector<String> strings = String(a_str).replace(" ", "").replace("\t", "").replace("\n", "").split(",");
for (unsigned i = 0; i < strings.size(); i++)
{
Vector<String> nameValue = strings[i].split("=");
if (nameValue.size() == 2)
val = atoi(nameValue[1].toUtf8().c_str());
maps.valueNameMap.insert(val, nameValue[0]);
maps.valueList.push_back(val);
val++; // This is copying how enums auto increment if not assigned a value
}
}
}
const String& EnumBaseBase::toStringHelper(const EnumBaseBase::EnumMaps& a_maps, int a_val)
{
static const String errorMsg = "Invalid enum value"; // TODO: crash? exception? exit?
return a_maps.valueNameMap.forwardFind(a_val, errorMsg);
}
int EnumBaseBase::fromStringHelper(const EnumBaseBase::EnumMaps& a_maps, const String& a_str)
{
int def = -1;
return a_maps.valueNameMap.reverseFind(a_str, def);
}
EnumBaseBase::EnumMaps& EnumBaseBase::initMapsHelper(const char* enumName, const char* enumValues)
{
static std::map<const char*,EnumMaps> allMaps;
std::map<const char*,EnumMaps>::iterator it = allMaps.find(enumName);
if (it == allMaps.end()) {
EnumBaseBase::EnumMaps maps;
baseInitMaps(enumValues, maps);
allMaps.insert(std::pair<const char*,EnumMaps>(enumName,maps));
}
return allMaps[enumName];
}
DECLARE_ENUM(
MyEnum,
ONE=1,
TWO,
THREE,
TEN=10,
ELEVEN
);
UNIT_TEST(SmartEnumTests, 0)
{
MyEnum foo = MyEnum::TWO;
std::cout << foo << "\n";
CHECK(MyEnum::toString(foo) == "TWO"); // static method
CHECK(foo.toString() == "TWO"); // member method
CHECK(MyEnum::toString(MyEnum::TWO) == "TWO");
CHECK(MyEnum::toString(10) == "TEN");
CHECK(MyEnum::toString(100) == "Invalid enum value");
CHECK(MyEnum::fromString("COW") == -1);
CHECK(MyEnum::fromString("TWO") == 2);
/*
// C++11 iteration over all values
for( auto x : MyEnum::allValues() )
{
std::cout << MyEnum(x).toString() << std::endl;
}
*/
}
END_NAMESPACE
#endif