/
traits

traits

Developing traits
// templates-typedefs.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include "pch.h"
#include <iostream>
#include <string>
#include <limits>

using	namespace std;

//++ Templated code ...
/*
  The significance of using typedefs in templates can be seen by the code below.
  It may not seem important at first, but it allows one template class to be defined
  in terms of the data types that are being used in another class i.e. the one that
  was passed in as a template parameter.   So what?
*/

template	<typename T>
class SubElement
{
public:
	typedef	T	_type;

	SubElement(_type value) :data(value) { return; }
private:
	_type	data;
};

template	<typename T>
class	Container
{
public:
	typedef	T	_type;

	//Container(_type value) :data(value) { return; }
private:
	//_type	data;
};
// Partial instantiation
template<>
class Container<SubElement<int>>
{};

template <typename T>
class Component
{
public:
	typedef T itsT;
	// By using typedefs and templates together, one can create a class and
	// use its data members types in other classes

public:
	Component(itsT value) :data(value)//, elementData(value)
	{ 
		return; 
	}

	itsT	getData() const { return data; }

private:
	itsT data;
	//Container< SubElement<itsT> > elementData;
	// This declaration creates an instance of a Container class that contains a type
	// Component< T > object, but the data members of Component are actually typed
	// on whatever the types are within Component and not Component< T > itself.
};

template	<class K, class V>
class	KeyValueMap
{
public:
	KeyValueMap(K key, V value) :itsKey(key), itsValue(value) { return; }

private:
	K	itsKey;
	V	itsValue;
};

template	<class K, class V>
KeyValueMap< K, V >&	Map(K key, V value)
{
	return *new KeyValueMap< K, V >(key, value);
}


/*
	The following classes are a demonstration of what we talking about in terms of
  modelling the command object and its related objects
*/

class	TCommand;


class	TCommandType
{
protected:
	TCommandType(string& cmd);
public:
	const string&	getCommand() const;
	virtual	void	executeCommand(TCommand& theCmd) = 0;

private:
	string itsCmd;
};

class	TStoredProcCmd : public TCommandType
{
public:
	TStoredProcCmd(string cmd);
	void	executeCommand(TCommand& theCmd);
};

class	TSQLCmd : public TCommandType
{
public:
	TSQLCmd(string cmd);
	void	executeCommand(TCommand& theCmd);
};

class	TDataBaseType
{
public:
	TDataBaseType(string dbType, TCommandType& cmdMode) :
		itsDBType(dbType), itsCmdMode(cmdMode)
	{
		return;
	}

	const string&	getDBType() const
	{
		return itsDBType;
	}

	TCommandType&	getCmdType() const
	{
		return itsCmdMode;
	}
private:
	string	itsDBType;
	TCommandType&	itsCmdMode;
};

class	TCommand
{
public:
	TCommand(TDataBaseType& dbType, string cmd) :itsDBType(dbType), itsCmd(cmd)
	{
		return;
	}

	void	execute();
	void	executeCommand(TStoredProcCmd& cmdType);
	void	executeCommand(TSQLCmd& cmdType);
private:
	TDataBaseType&	itsDBType;
	string			itsCmd;
};


//============================================================================
/* TCommandType */
TCommandType::TCommandType(string& cmd) :itsCmd(cmd)
{
	return;
}

const string&	TCommandType::getCommand() const
{
	return itsCmd;
}

/* TStoredProcCmd */
TStoredProcCmd::TStoredProcCmd(string cmd) :TCommandType(cmd)
{
	return;
}

void	TStoredProcCmd::executeCommand(TCommand& theCmd)
{
	theCmd.executeCommand(*this);
}

/* TSQLCmd */
TSQLCmd::TSQLCmd(string cmd) :TCommandType(cmd)
{
	return;
}

void	TSQLCmd::executeCommand(TCommand& theCmd)
{
	theCmd.executeCommand(*this);
}

/* TCommand */
void	TCommand::execute()
{
	itsDBType.getCmdType().executeCommand(*this);
}

void	TCommand::executeCommand(TStoredProcCmd& cmdType)
{
	// do the actual execute in here ...
}

void	TCommand::executeCommand(TSQLCmd& cmdType)
{
	// do the actual execute in here ...
}

// Before we get into developing an example of a trait let's refesh on template classes

// typically template classes are defined as shown here
template	<typename T>
class Holder
{
public:
	Holder(){}
	void	execute()
	{
		cout << "Holder T" << endl;
	}
};

// And this would be a template specialization, it's designed specifically to handle
// integers
template	<>
class Holder<int>
{
private:
	int value;
public:
	Holder(){}
	void	execute()
	{
		cout << "Holder int" << endl;
	}
};

// But templates can go further than this, see below.  In this example we narrow
// the template type to a bool, it could be any type 
template	<bool b>
class Element
{
public:
	Element() {}
	void	execute()
	{
		cout << "Element<bool b>" << endl;
	}
};

// We can specialize the above template on a bool value
template	<>
class Element<true>
{
public:
	Element() {}
	void	execute()
	{
		cout << "Element<true>" << endl;
	}
};

// Going to develop the example from ACCU
// First create a really simple template for testing whether a type can be void
template< typename T >
struct _is_void{
	static const bool value = false;
};

// Now create a template specialization that must yield a true to the question
// That is void itself must yield true, all other parameters must yield false
template<>
struct _is_void< void > {
	static const bool value = true;
};

template <typename T>
bool	isVoid()
{
	return _is_void<T>::value;
}
// We now have a complete traits type that can be used to detect if any given type, 
// passed in as a template parameter, is void. Not the most useful piece of code on 
// its own, but definitely a useful demonstration of the technique.

// Here is another example testing if a type is a pointer
template< typename T >
struct _is_pointer {
	static const bool value = false;
};

// Here is our template specialization
// Notice here the template parameter cannot be empty because the type is needed
// On the struct we simply add the * to give this it's specialization
template< typename T >
struct _is_pointer< T* > {
	static const bool value = true;
};

// Here is our test function
template <typename T>
bool	isPointer(T data)
{
	return _is_pointer<T>::value;
}

//-------------------------------------------------------------------------------//
// Example of a trait being used, found on accu.org
//-------------------------------------------------------------------------------//
template< class T >
T findMax(const T * const data,
	const size_t numItems) {
	// Obtain the minimum value for type T 
	// This is an example of a trait, T which is findMax template parameter
	// is being used as the type info for numeric_limits<T>
	// begin trait
	T largest = std::numeric_limits< T >::min();
	// end trait
	for (unsigned int i = 0; i < numItems; ++i)
		if (data[i] > largest)
			largest = data[i];
	return largest;
}


//-------------------------------------------------------------------------------//
// Example of designing a trait, found on accu.org
// An algorithm selector
//-------------------------------------------------------------------------------//
/*
  In this example we going to build a demo classs called algorithm_selector
  that is called from a generic method called algorithm.  An algorithm fx is passed
  an object that is used as part of the algorithm fx, it simply wants the passed object 
  to perform an operation to fulfill the requirement of the algorithm.  We will call
  this operation algorithm_part()

  The object passed to the algorithm function must indicate whether or not it has an
  optimised implementation.  The algorithm_selector makes use of this information to
  determine whether or not it should provide a basic section of the desired requirement
  or if it should delegate this to the object passed into the algorithm function.
*/

template< typename T >
struct supports_optimised_implementation 
{
	// The static is important, it allows us to use compile time switching
	static const bool value = false;
};

template< bool b >
struct static_algorithm_selector 
{
	template< typename T >
	static	void algorithm_part(T& object)
	{
		//implement the alorithm operating on "object" here 
		cout << "performing non optimised part of algorithm [static]" << endl;
	}
};

template<>
struct static_algorithm_selector< true > 
{
	template< typename T >
	static	void algorithm_part(T& object) {
		object.optimised_implementation_algorithm_part();
	}
};

template< bool b >
struct dynamic_algorithm_selector
{
	template< typename T >
	static	void algorithm_part(T& object)
	{
		//implement the alorithm operating on "object" here 
		cout << "performing non optimised part of algorithm [dynamic]" << endl;
	}
};

template<>
struct dynamic_algorithm_selector< true >
{
	template< typename T >
	static	void algorithm_part(T& object) {
		object.optimised_implementation_algorithm_part();
	}
};

template< typename T >
void algorithm_v1(T& object) 
{
	// The supports_optimised_implementation< T >::value will instantiate the object
	// and set ::value to either true or false, we saw that above with 
	// _is_void<> and _is_pointer<> examples
	//
	// The algorithm_selector has been templatised to accept a boolean value only
	static_algorithm_selector< supports_optimised_implementation< T >::value >::algorithm_part(object);
}

template< typename T >
void algorithm_v2(T& object)
{
	// The supports_optimised_implementation< T >::value will instantiate the object
	// and set ::value to either true or false, we saw that above with 
	// _is_void<> and _is_pointer<> examples
	//
	// The algorithm_selector has been templatised to accept a boolean value only
	dynamic_algorithm_selector< supports_optimised_implementation< T >::value > das;
	das.algorithm_part(object);
}

// Our test classes
class ObjectA 
{
public:
	void no_optimised_implementation() 
	{
		//this never gets called so can delete it...
	}
};

class ObjectB 
{
public:
	void optimised_implementation_algorithm_part() 
	{
		cout << "performing optimised part of algorithm" << endl;
	}
};

// Create a template specialization of our supports_optimised_implementation<T>
// By doing this we guarantee that when supports_optimised_implementation<> is used
// in the algorithm function the correct version X_algorithm_selector<> is compiled
// into the code
template<>
struct supports_optimised_implementation< ObjectB > 
{
	// The static is important, it allows us to use compile time switching
	static const bool value = true;
};

//---------------------------------------------------------------------------//
int	fooBar()
{
	cout << "fooBar()" << endl;
	return 0;
}

template<class _Ty>
class crazy_trait
{	// numeric limits for arbitrary type _Ty (say little or nothing)
public:
	static  constexpr	_Ty(fooBar)() noexcept
	{	// return minimum value
		return (_Ty());
	}
};



//---------------------------------------------------------------------------
int main()
{
	auto val = crazy_trait<int>::fooBar();

	Holder<int> t1;
	t1.execute();
	Holder<char> t2;
	t2.execute();

	Element<false> e1;
	e1.execute();
	Element<true> e2;
	e2.execute();

	// Work through above example above before looking at the next set of examples
	// below

	// This frst example makes use the numeric_limits<> as trait
	std::numeric_limits<int> il;
	cout << std::numeric_limits<int>::min() << "/" << il.max() << endl;

	int data[] = { 3, 7, 99, 34, 2, 78, 101, 250, 178 };
	int count = sizeof(data) / sizeof(int);
	int max = findMax(data, count);

	//-----------------------------------------------------------------------------//
	// Now we're going to develop our own traits
	//-----------------------------------------------------------------------------//
	// Using the is_void template
	_is_void<int> test;
	test.value;
	// The function adds very little value...
	bool result = isVoid<void>();

	// Here the template function adds a lot of value, you don't need to know 
	// the data type at design time, the compiler can perform the diagnostics for you
	// by looking at the varibale itself
	result = isPointer(data);
	result = isPointer(count);
	result = isPointer("Hello world");
	
	// Now we move onto building a more substantial trait, look at the following
	// classes above
	// supports_optimised_implementation
	// static_algorithm_selector
	// static_algorithm_selector< true >
	//
	ObjectA a;
	algorithm_v1(a);
	// calls default implementation 
	
	ObjectB b;
	algorithm_v1(b);
	// calls ObjectB::optimised_implementation(); 

	algorithm_v2(a);
	// calls default implementation 

	algorithm_v2(b);
	// calls ObjectB::optimised_implementation(); 

	SubElement<int>::_type x = 5;
	//Container< SubElement<int> > elementData(5);

	//Component<int> fb1(5);
	/*
	int i = fb1.getData();

	Container<Component<int>> fbu(5);

	Component<int>::itsT x = i;

	Component<KeyValueMap<int, int>> fb2(KeyValueMap<int, int>);
	*/

	std::cout << "Hello World!\n";
}
//---------------------------------------------------------------------------