// SimpleAnyValue.h
// This header is part of SimpleWin library, SimpleCore namespace
// Author - Kosta Cherry
//
// License: use any way you want, except the following:
// 1. Don't claim that you are the author of this code.
// 2. Don't try to prevent others from freely using this code. It's in public domain now. It is free and always will be free.
// 3. Don't hold me responsible for any bugs or malfunction, or alike. Use at you own risk. Absolutely no guarantee here whatsoever.
//
// Description: contains code for implementation of "any" variable (similar to boost::any, but with some extention) and CSimpleSingleClassAnyMap - map of ("type", "pointer to instance of that type")
//
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// CSimpleAnyValue:
// Difference from (boost::any) : when "any" is assigned (created), user has ability to also assign processor (a.k.a visitor) class for later processing.
// Basically, with other (boost-like) standard implementation there are 2 possible choices:
// a) "any"-like containers. They allow for keeping really any type, but in exchange any operation with data require casting, i.e. knowledge of the type for contained variable;
// b) "variant"-like containers. They are much more flexible, allowing, for example, usage of visitors (which are not restricted to anything), but in exchange one should
// provide a list of possible types up-front, i.e. limit number of available types.
//
// CSimpleAnyValue code attempts to breed "any" and "variant" together. If no processor/visitor class provided during CSimpleAnyValue initialization, it behaves exactly as boost::any, although
// you have an ability to get numerical representation of the type (via getTypeId() function; keep in mind though, value returned by getTypeId() might differ from run to run, and is only
// consistent for a given instance of program run (process), so there is no sense to store it for later use). On the other hand, user can provide processor/visitor class during CSimpleAnyValue
// initialization, and later call CSimpleAnyValue::process(void*) function to apply visitor logic on contained variable. The only restriction imposed on processor/visitor - it should provide
// function with signature void process(T* value), where T - possible type of contained variable. See default implemenation (CSimpleAnyValueGenericProcessor) - it provides such a function (which
// does nothing by default).
//
// The benefit of having such processor/visitor is that you don't need to know all your types in advance (just like "any"-like container);
// in exchange you need to know the processor/visitor class that you are going to apply. Yes, it is not as flexible as visitor for
// typical variant-like container, but for vast majority of cases this is sufficient.
// Examples:
// -- any-like behavior:
// CSimpleAnyValue vint = 15;
// CSimpleAnyValue vfloat = 2.3f;
// auto rint = vint.getValue<int>(); // now rint == 15;
// auto rfloat = vfloat.getValue<float>(); // now rfloat == 2.3f;
// vfloat = vint; // now vfloat contained variable is erased and int with value 15 is copied into it;
// rfloat = vfloat.getValue<float>(); // exception raised - vfloat at this moment contains int, not float!
//
// -- variant-like behavior:
// class MyVisitor
// {
// public:
// template<typename T> void process(T* value) {}; // to catch all other unhandled types
// virtual void process(int* value) { cout << _T("Processing happens in MyVisitor on int with value ") << *value << _T("\n");}
// };
// class MyVisitorDerived : public MyVisitor
// {
// public:
// virtual void process(int* value) { cout << _T("Processing happens in MyVisitorDerived on int with value ") << *value << _T("\n");}
// };
//
// CSimpleAnyValue vint(15, (MyVisitor*)nullptr); // don't have to provide actual instance of visitor during initalization; class type is enough
// MyVisitor visitor;
// MyVisitorDerived visitorDerived;
// CSimpleAnyValue vint2(20, &visitor); // or one can be initialized this way, if instance of visitor already exists; instance is not used in any way - only to determine type of visitor
//
// vint.process(&visitor); // this outputs "Processing happens in MyVisitor on int with value 15"
// vint.process(&visitorDerived); // this outputs "Processing happens in MyVisitorDerived on int with value 15"
// vint2.process(&visitor); // this outputs "Processing happens in MyVisitor on int with value 20"
// vint2.process(&visitorDerived); // this outputs "Processing happens in MyVisitorDerived on int with value 20"
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// CSimpleSingleClassAnyMap:
// This map keeps pointers (and only pointers!!!) to any class you chose to store
// For example, you chose to store class of type A.
// Then usage is as following:
// CSimpleSingleClassAnyMap m_map;
// A& ap = m_map.getValue<A*>(); // will return value of type A&, if found; if not found, will create a new one via default constructor, and then return reference to it;
// A& a = m_map.getValue<A>(); // will return value of type A&, if found; if not found, will create a new one via default constructor, and then return reference to it;
// // whatever objects were created, will be desructed during map destruction
// m_map.setValue(ap, true); // will delete whatever was stored of type A (or A* - same location), and then will take ownership of the pointer (i.e. just copy pointer address);
// // when map will be deleted, that pointer also will be deleted
// m_map.setValue(ap, false); // same as above, but will do nothing with the pointer during destruction
// m_map.setValue(a); // will create copy of a, and then delete that copy during destruction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//
// History:
// Version 1.0 - initial implementation; only single-threading is supported;
#pragma once
#include <stdexcept>
#include <map>
namespace SimpleCore
{
class CSimpleTypeId
{
public:
template<class T>
static INT_PTR getTypeId()
{
static auto keeper = CSimpleTypeStub<T>();
return (INT_PTR)&keeper;
}
private:
template<typename T> struct CSimpleTypeStub {}; // type stub
};
//-----------------------------------------
class CSimpleAnyValueGenericProcessor
{
public:
CSimpleAnyValueGenericProcessor() {};
template<typename T> void process(T*) {}; // catch all
};
class CSimpleAnyValueBase
{
public:
virtual const INT_PTR getTypeId() const = 0;
virtual const INT_PTR getProcessorTypeId() const = 0;
virtual ~CSimpleAnyValueBase() {};
virtual void deleteValue() = 0; // use this if Any holds a pointer, and we have a collection of Any, so we don't need to know type of each in a loop
virtual CSimpleAnyValueBase* clone() = 0;
virtual bool isSame(const CSimpleAnyValueBase* value) const = 0;
virtual bool operator == (const CSimpleAnyValueBase& value) const { return isSame(&value); }
virtual void process(void* processor) = 0;
};
template<class T, class P = CSimpleAnyValueGenericProcessor>
class CSimpleAnyValueHolder : public CSimpleAnyValueBase
{
public:
typedef P ProcessorType;
CSimpleAnyValueHolder(const T &t) : m_value(t) {}
virtual const INT_PTR getTypeId() const
{
return CSimpleTypeId::getTypeId<T>();
}
virtual const INT_PTR getProcessorTypeId() const
{
return CSimpleTypeId::getTypeId<P>();
}
virtual void deleteValue()
{
_deleteValue(m_value);
}
virtual CSimpleAnyValueBase* clone()
{
return new CSimpleAnyValueHolder<T, P>(m_value);
}
template <typename Q>
typename std::enable_if<std::is_pointer<Q>::value && !std::is_function<typename std::remove_pointer<Q>::type>::value >::type _deleteValue(Q& value)
{
if (value)
delete value;
value = nullptr;
}
template <typename Q>
typename std::enable_if<!std::is_pointer<Q>::value || std::is_function<typename std::remove_pointer<Q>::type>::value >::type _deleteValue(Q value)
{
UNREFERENCED_PARAMETER(value);
}
bool isSame(const CSimpleAnyValueBase* value) const
{
if (!value)
return false;
if (getTypeId() != value->getTypeId())
return false;
if (this == value)
return true;
const CSimpleAnyValueHolder<T, P>* ref = static_cast<const CSimpleAnyValueHolder<T, P>*>(value);
return ref->m_value == m_value;
}
virtual void process(void* processor)
{
((ProcessorType*)processor)->process(&m_value);
}
private:
friend class CSimpleAnyValue;
T m_value;
};
// overload to add void
template<class P>
class CSimpleAnyValueHolder<void, P> : public CSimpleAnyValueBase
{
public:
friend class CSimpleAnyValue;
virtual const INT_PTR getTypeId() const
{
return CSimpleTypeId::getTypeId<void>();
}
virtual const INT_PTR getProcessorTypeId() const
{
return CSimpleTypeId::getTypeId<P>();
}
// This is an optimization to avoid heap pressure for the
// allocation of stateless CSimpleAnyValueHolder<void, P> instances.
void* operator new(size_t)
{
static CSimpleAnyValueHolder<void, P> inst;
return &inst;
}
void operator delete(void* d)
{
UNREFERENCED_PARAMETER(d);
}
virtual void deleteValue() {};
virtual CSimpleAnyValueBase* clone()
{
return new CSimpleAnyValueHolder<void, P>();
}
bool isSame(const CSimpleAnyValueBase* value) const
{
if (!value)
return false;
if (value->getTypeId() == getTypeId())
return true;
return false;
}
virtual void process(void* processor) {UNREFERENCED_PARAMETER(processor);};
};
class CSimpleBadValueCast : public std::runtime_error
{
public:
CSimpleBadValueCast(const char *w="") : std::runtime_error(w) {}
};
class CSimpleAnyValue
{
public:
CSimpleAnyValueBase* m_anyValueBase;
typedef CSimpleAnyValueGenericProcessor DefaultProcessor;
public:
// avoid null-values
CSimpleAnyValue()
{
m_anyValueBase = new CSimpleAnyValueHolder<void, DefaultProcessor>();
}
template<class T, class P>
CSimpleAnyValue(const T &t, const P* p = nullptr, typename std::enable_if<!std::is_same<T, CSimpleAnyValue>::value>::type* = 0)
{
UNREFERENCED_PARAMETER(p);
m_anyValueBase = new CSimpleAnyValueHolder<T, P>(t);
}
// no-copy constructor with default processor type
template<class T>
CSimpleAnyValue(const T &t, typename std::enable_if<!std::is_same<T, CSimpleAnyValue>::value>::type* = 0)
{
m_anyValueBase = new CSimpleAnyValueHolder<T, DefaultProcessor>(t);
}
// copy constructor
CSimpleAnyValue(const CSimpleAnyValue &t)
{
m_anyValueBase = t.m_anyValueBase->clone();
}
CSimpleAnyValue& operator=(const CSimpleAnyValue& t)
{
if (this == &t) // sanity check
return *this;
if (m_anyValueBase->getTypeId() != CSimpleTypeId::getTypeId<void>()) // to combat strange debug error on deleting "same" address twice, even though no actual delete happens
delete m_anyValueBase;
m_anyValueBase = t.m_anyValueBase->clone();
return *this;
}
~CSimpleAnyValue()
{
if (m_anyValueBase->getTypeId() != CSimpleTypeId::getTypeId<void>()) // to combat strange debug error on deleting "same" address twice, even though no actual delete happens
delete m_anyValueBase;
};
const INT_PTR getTypeId() const { return m_anyValueBase->getTypeId(); }
const INT_PTR getProcessorTypeId() const { return m_anyValueBase->getProcessorTypeId(); }
bool isSame(const CSimpleAnyValue* value) const
{
if (!value)
return false;
if (getTypeId() != value->getTypeId())
return false;
if (!m_anyValueBase && !value->m_anyValueBase)
return true;
return m_anyValueBase->isSame(value->m_anyValueBase);
}
bool operator == (const CSimpleAnyValue& value) const
{
return isSame(&value);
}
template<class T, class P>
T& getValue() const
{
if (getTypeId() != CSimpleTypeId::getTypeId<T>())
throw CSimpleBadValueCast();
// this is safe now
CSimpleAnyValueHolder<T, P> *value=static_cast<CSimpleAnyValueHolder<T, P>*>(m_anyValueBase);
return value->m_value;
}
template<class T>
T& getValue() const
{
return getValue<T, DefaultProcessor>();
}
template<class T, class P, class O>
void resetProcessor()
{
CSimpleAnyValueHolder<T, O> *value=static_cast<CSimpleAnyValueHolder<T, O>*>(m_anyValueBase);
if (CSimpleAnyValueBase* oldp = new CSimpleAnyValueHolder<T, P>(value->m_value)) // in case it throws...
{
if (m_anyValueBase->getTypeId() != CSimpleTypeId::getTypeId<void>()) // to combat strange debug error on deleting "same" address twice, even though no actual delete happens
delete m_anyValueBase;
m_anyValueBase = oldp;
}
}
void deleteValue()
{
m_anyValueBase->deleteValue();
}
void process(void* processor)
{
m_anyValueBase->process(processor);
}
};
// This map keeps pointers (and only pointers!!!) to any class you chose to store
// For example, you chose to store class of type A.
// Then usage is as following:
// CSimpleSingleClassAnyMap m_map;
// A& ap = m_map.getValue<A*>();// will return value of type A&, if found; if not found, will create a new one via default constructor, and then return reference to it;
// A& a = m_map.getValue<A>(); // will return value of type A&, if found; if not found, will create a new one via default constructor, and then return reference to it;
// // whatever objects were created, will be desructed during map destruction
// m_map.setValue(ap, true); // will delete whatever was stored of type A (or A* - same location), and then will take ownership of the pointer (i.e. just copy pointer address);
// // when map will be deleted, that pointer also will be deleted
// m_map.setValue(ap, false); // same as above, but will do nothing with the pointer during destruction
// m_map.setValue(a); // will create copy of a, and then delete that copy during destruction
class CSimpleSingleClassAnyMap
{
public:
CSimpleSingleClassAnyMap() {};
~CSimpleSingleClassAnyMap()
{
for (auto it = m_anyMap.begin(); it!= m_anyMap.end(); it++)
if ((*it).second.second)
(*it).second.first.deleteValue();
m_anyMap.clear();
}
template <class T, class P>
typename std::enable_if<std::is_pointer<T>::value, decltype(*T())&>::type getValue()
{
auto iter = m_anyMap.find(CSimpleTypeId::getTypeId<T>());
if (iter != m_anyMap.end())
return *iter->second.first.getValue<T, P>();
auto it = m_anyMap.insert(std::make_pair(CSimpleTypeId::getTypeId<T>(), std::make_pair(CSimpleAnyValue(new std::remove_reference<decltype(*T())>::type(), (P*)nullptr), true)));
return *it.first->second.first.getValue<T, P>();
};
template <class T>
typename std::enable_if<std::is_pointer<T>::value, decltype(*T())&>::type getValue()
{
return getValue<T, DefaultProcessor>();
};
template <class T, class P>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type getValue()
{
auto iter = m_anyMap.find(CSimpleTypeId::getTypeId<T*>());
if (iter != m_anyMap.end())
return *iter->second.first.getValue<T*, P>();
auto it = m_anyMap.insert(std::make_pair(CSimpleTypeId::getTypeId<T*>(), std::make_pair(CSimpleAnyValue(new T(), (P*)nullptr), true)));
return *it.first->second.first.getValue<T*, P>();
}
template <class T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type getValue()
{
return getValue<T, DefaultProcessor>();
}
template <class T, class P>
typename std::enable_if<std::is_pointer<T>::value, typename std::remove_pointer<T>::type& >::type setValue(const T& value, bool autoDelete = true) // if pointer
{
auto iter = m_anyMap.find(CSimpleTypeId::getTypeId<T>());
if (iter != m_anyMap.end())
{
// check if value is the same:
T content = iter->second.first.getValue<T, P>();
if (content == value)
return *iter->second.first.getValue<T, P>();
if ((*iter).second.second)
iter->second.first.deleteValue();
iter->second.first = value;
(*iter).second.second = autoDelete;
return *iter->second.first.getValue<T, P>();
}
auto it = m_anyMap.insert(std::make_pair(CSimpleTypeId::getTypeId<T>(), std::make_pair(CSimpleAnyValue(value, (P*)nullptr), autoDelete)));
return *it.first->second.first.getValue<T, P>();
}
template <class T>
typename std::enable_if<std::is_pointer<T>::value, typename std::remove_pointer<T>::type& >::type setValue(const T& value, bool autoDelete = true) // if pointer
{
return setValue<T, DefaultProcessor>(value, autoDelete);
}
template <class T, class P>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type setValue(const T& value)
{
auto iter = m_anyMap.find(CSimpleTypeId::getTypeId<T*>());
if (iter != m_anyMap.end())
{
// check if value is the same:
T& content = *iter->second.first.getValue<T*, P>();
if (content == value)
return *iter->second.first.getValue<T*, P>();
if ((*iter).second.second)
iter->second.first.deleteValue();
T* newVal = new T(value);
iter->second.first = CSimpleAnyValue(newVal, (P*)nullptr);
iter->second.second = true;
return *iter->second.first.getValue<T*, P>();
}
T* newVal = new T(value);
auto it = m_anyMap.insert(std::make_pair(CSimpleTypeId::getTypeId<T*>(), std::make_pair(CSimpleAnyValue(newVal, (P*)nullptr), true)));
return *it.first->second.first.getValue<T*, P>();
}
template <class T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type setValue(const T& value)
{
return setValue<T, DefaultProcessor>(value);
}
template <class T, class P>
typename std::enable_if<std::is_pointer<T>::value, T>::type hasValue()
{
auto iter = m_anyMap.find(CSimpleTypeId::getTypeId<T>());
if (iter != m_anyMap.end())
return iter->second.first.getValue<T, P>();
return nullptr;
}
template <class T>
typename std::enable_if<std::is_pointer<T>::value, T>::type hasValue()
{
return hasValue<T, DefaultProcessor>();
}
template <class T, class P>
typename std::enable_if<!std::is_pointer<T>::value, T*>::type hasValue()
{
auto iter = m_anyMap.find(CSimpleTypeId::getTypeId<T*>());
if (iter != m_anyMap.end())
return iter->second.first.getValue<T*, P>();
return nullptr;
}
template <class T>
typename std::enable_if<!std::is_pointer<T>::value, T*>::type hasValue()
{
return hasValue<T, DefaultProcessor>();
}
private:
typedef CSimpleAnyValueGenericProcessor DefaultProcessor;
std::map <INT_PTR, std::pair<CSimpleAnyValue, bool>> m_anyMap;
};
}