Click here to Skip to main content
15,886,840 members
Articles / DevOps / Testing

Performance comparison test suit

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
19 Aug 2013CPOL4 min read 21.8K   15  
Code to easily compare performance of code variants.
//	 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;
	};
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions