Messing around with templates

The Partridge Family were neither partridges nor a family. Discuss.
Post Reply
albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Messing around with templates

Post by albinopapa » February 26th, 2018, 6:06 am

Code: Select all

namespace
{
	template<template<class> class vectype, class T>
	std::enable_if_t<std::is_arithmetic_v<T>, vectype<T>>
		operator+( const vectype<T>& lhs, const vectype<T>& rhs )
	{
		vectype<T> res;
		res.x = lhs.x + rhs.x;
		res.y = lhs.y + rhs.y;

		if constexpr( num_elements<vectype, T>::value == 3 )
		{
			res.z = lhs.z + rhs.z;
		}

		return res;
	}

	template<template<class> class vectype, class T>
	std::enable_if_t<std::is_arithmetic_v<T>, vectype<T>>
		operator+=( vectype<T>& lhs, const vectype<T>& rhs )
	{
		lhs = lhs + rhs;

		return lhs;
	}

	template<template<class> class vectype, class T>
	std::enable_if_t<std::is_arithmetic_v<T>, vectype<T>>
		operator-( const vectype<T>& lhs, const vectype<T>& rhs )
	{
		vectype<T> res;
		res.x = lhs.x - rhs.x;
		res.y = lhs.y - rhs.y;

		if constexpr( num_elements<vectype, T>::value == 3 )
		{
			res.z = lhs.z - rhs.z;
		}

		return res;
	}

	template<template<class> class vectype, class T>
	std::enable_if_t<std::is_arithmetic_v<T>, vectype<T>>&
		operator-=( vectype<T>& lhs, const vectype<T>& rhs )
	{
		lhs = lhs - rhs;
		return lhs;
	}

	template<template<class> class vectype, class T>
	std::enable_if_t<std::is_arithmetic_v<T>, vectype<T>>
		operator*( vectype<T>& lhs, const T rhs )
	{
		vectype<T> res;
		res.x = lhs.x * rhs;
		res.y = lhs.y * rhs;
		if constexpr( num_elements<vectype, T>::value == 3 )
		{
			res.z = lhs.z * rhs;
		}

		return res;
	}

	template<template<class> class vectype, class T>
	std::enable_if_t<std::is_arithmetic_v<T>, vectype<T>>&
		operator*=( vectype<T>& lhs, const T rhs )
	{
		lhs = lhs * rhs;
		return lhs;
	}

	template<template<class> class vectype, class T>
	std::enable_if_t<std::is_arithmetic_v<T>, vectype<T>>
		operator/( vectype<T>& lhs, const T rhs )
	{
		if constexpr( std::is_floating_point_v<T> )
		{
			return lhs * ( static_cast< T >( 1 ) / rhs );
		}

		vectype<T> res;
		res.x = lhs.x / rhs;
		res.y = lhs.y / rhs;

		if constexpr( num_elements<vectype, T>::value == 3 )
		{
			res.z = lhs.z / rhs;
		}

		return res;
	}

	template<template<class> class vectype, class T>
	std::enable_if_t<std::is_arithmetic_v<T>, vectype<T>>&
		operator/=( vectype<T>& lhs, const T rhs )
	{
		lhs = lhs / rhs;

		return lhs;
	}
}
Here's the REPO.

For awhile now I've been trying to figure out std::enable_if to limit the types of variables that can be passed to template functions. I finally got it working for functions that return a value, as you can see above ( if you can read that gibberish ). I can't seems to get it to work when used in the parameter list though. I keep getting the error: "no overloaded function matches the given arguments". I think it's because I get confused about type deduction and replacement of template parameters during type deduction.

For instance:

Code: Select all

namespace String
{
#if defined(UNICODE) || defined(_UNICODE)
	using string_type = std::wstring;
	using stream_type = std::wstringstream;
	using cstring_type = wchar_t*;
#else
	using string_type = std::string;
	using stream_type = std::stringstream;
	using cstring_type = char*;
#endif
	template<class T = void> struct is_char_type :std::false_type
	{};
	template<> struct is_char_type<char> :std::true_type{};
	template<> struct is_char_type<wchar_t> : std::true_type{};

	template<class T> size_t length( const T* str )
	{
		static_assert( is_char_type<T>::value, "Function only works with const char * or const wchar_t * types." );
		if constexpr( std::is_same_v<T,char> )
		{
			return strlen( str );
		}
		else
		{
			return wcslen( str );
		}
	}

	using cstring = cstring_type;
	using string = typename string_type;
	using stringstream = typename stream_type;
	
	string encode( const char*str)
	{
		const auto len = length( str ) + 1;
		string res = string( str, str + len );

		return res;
	}
	string encode( const std::string& str )
	{
		return string( str.begin(), str.end() );
	}
	string encode( const wchar_t* str )
	{
		const auto len = length( str ) + 1;
		string res = string( str, str + len );

		return res;
	}
	string encode( const std::wstring& str )
	{
		return string( str.begin(), str.end() );
	}
	
}
For the String::length function, I wanted to limit the types passed in to const char* or const wchar_t *.
I would have thought this would work:

Code: Select all

	template<class T> size_t length( 
		const std::enable_if_t<typename is_char_type<T>::type, T>* str )
	{
		if constexpr( std::is_same_v<T,char> )
		{
			return strlen( str );
		}
		else
		{
			return wcslen( str );
		}
	}
I suppose I could just do what I did for the ones that work:

Code: Select all

	template<class T> 
	std::enable_if_t<typename is_char_type<T>::type, size_t> length( const T* str )
	{
		if constexpr( std::is_same_v<T,char> )
		{
			return strlen( str );
		}
		else
		{
			return wcslen( str );
		}
	}
Cyboryxmen is on to something, templates are both fun and frustrating to mess around with. I'm sure once I catch up to his level ( I guess I can't really catch up lol ), I'll have learned most of the current C++ features with the exception of multi-threading, and iterators...f'ing iterators.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Messing around with templates

Post by albinopapa » February 27th, 2018, 9:14 am

If anyone has looked through the repo, you'll notice the title: Cpp_No_Classes. Now, the idea was to mimic C while still using C++ features. This means making use of the template system for one. This allows me to keep the C++ type system safety. I use value, move and reference semantics when I can. The only real cheat I use, because C doesn't have classes, is using the constructor and destructor of my structs. Other than that, I don't define member functions. All functions are nested in namespaces ( something C also doesn't have ), but essentially global.

One thing that you can do in C to mimic a class though, is create a struct of function pointer members. It is like C++ creating a vtable for polymorphic types. Really, the only difference here is that in C++, the first parameter is a pointer to itself...the this pointer.
Even a function like:
float Enemy::GetX()const
is actually:
float Enemy::GetX(const Enemy* this)

Since all my functions are considered free functions they already have to have an object passed in to have something done...I can't use 'this' as it's a C++ keyword, but I could do
float Enemy::GetX(const _Enemy& _this)
Where Enemy:: is the namespace and _Enemy is the struct holding the enemy data.

Getting back on track...function pointer members.

If you follow the link below, you'll notice I create a struct called vTable and in it, there is currently a single function pointer to an Update function inside the Units namespace.
Units.h

Here in Units.cpp I switch on the job that the unit is assigned. From there, I assign a function from one of the corresponding namespace in the _Unit constructor like:

Code: Select all

case Job::Farmer:
	vtable.Update = &Farmer::Update;
	break;
The core Units namespace has an Update function that will be used as the interface for all units, that function calls the individual unit's vtable.Update function pointer.

Code: Select all

	void Update( _Unit& unit, float dt )
	{
		// TODO: this should be an assert, vtable function pointers should never be unassigned
		if( unit.vtable.Update )
			unit.vtable.Update( unit, dt );

		ClampToScreen( unit );
	}

Here's what this allows me to accomplish. In another thread on this forum, Cyboryxmen came up with a very clever templated way of handling multiple types with a common interface. I had challenged his implementation before fully understanding what he had come up with. I wanted to see if there was a way to have runtime polymorphism and not sacrifice performance because of the way inheritance is handled implemented. I haven't tested this method and in order to compare my approach to his, I'd also have to implement his and I'm not sure I am that commited. So, this may be a complete flop, but this is what I'm going with.

I can create a vector of Units::_Unit objects so the data is contiguous.
I can have polymorphic behavior using the vTable function pointer members.
Using some template magic and a "traits" struct, I can have each unit have different values for the same data categories: they all have attack, armor, hp, etc... but each type will have different values.

It's polymophism at it's core really, with a few perks.

The drawback is that I can't have types with different data sets without implementing something like Cyboryxmen did.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

nG Inverse
Posts: 115
Joined: April 27th, 2012, 11:49 pm

Re: Messing around with templates

Post by nG Inverse » February 27th, 2018, 8:55 pm

Hmm..Interesting stuff you got going on here. It's been forever since I messed around with templates. I'll have to refresh myself then come revisit this.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Messing around with templates

Post by albinopapa » March 1st, 2018, 10:10 am

Two things so far that I've run realized while working with this sandbox of mine.

I'm a C++ programmer, I love classes. Restricting myself from using classes ( aside from RAII usage ) has been very distracting as I learned C++ through the Chili tutorials and haven't really ventured from it since. I keep finding myself going back and forth trying to remember if a function exists, or if I should even use a function since all my data is public. Of course, I do make and use functions just to keep from writing stuff like:
game.window.mouse.x.

Instead opting for
Framework::Input::Mouse::GetPosition( Framework::Window::GetMouse(game.window) ) lol.

Sort of just kidding, I aliased the Framework::Window and Framework::Input::Mouse namespaces to just Window and Mouse, so I actually write:
Mouse::GetPosition( Window::GetMouse(game.window) ).


Speaking of namespaces, that's number two. In C you don't have namespace which leads to C programmers usually prefixing their functions with the name of the structure they deal with or an abbreviation of.

For instance, I could have done:
Mouse_GetPosition( Win_GetMouse( game.window ) );

I chose to go with namespaces instead to see if it was any cleaner or had more advantages. As far as advantages go, when calling functions within the same or child namespace, you only have to write the name of the function. If I were to have gone with the C naming convention, I would always have to write the prefix. One big drawback I have come across is being careful or in this case, not being careful to write the name of the namespace the same for each location used.

For instance, in the header file I might write 'namespace Graphics' and in the cpp file I might end up writing 'namespace Grphics'. Since the functions are free functions ( essentially global ), the compiler might think I'm just declaring an overload to a function in the previously define Graphics namespace, but now in the Grphics namespace.

C programmers would have a similar problem with the prefix naming convention, just having functions with different names and same parameter list, one in the .h file and one in the .c file.

BONUS REALIZATION: LOL, with the separation of data and functions, I feel as if I'm able to forward declare a lot more. Since function signatures don't require the definition of a data type ( struct/class ) just a declaration, I can forward declare anything in the header files and just include them in the .cpp files. Not a huge deal, but it's something I've been striving for. Again, same as with the namespace dilemma, you have to make sure to write the forward declaration the same as the actual definition or you get errors about incomplete types.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

nG Inverse
Posts: 115
Joined: April 27th, 2012, 11:49 pm

Re: Messing around with templates

Post by nG Inverse » March 1st, 2018, 3:48 pm

And be happy you are a C++/OOP programmer!

I started my "programming career" off in a multiplayer community where you custom suited game servers with the Pawn scripting language. Bear in mind there is no OOP support, but on top of that, no dynamic memory allocation, no real "types" (just 32-bit "cells"), and a load of other restrictions. When creating a massive modification that would span hundreds of thousands of lines of code, it gets messy and complicated. Building systems without a proper class-style layout for user data, house/business systems, vehicle ownership systems, database connection and querying, etc. it got quite messy and jumbled.

The transition from a complete procedural language over to a full object-oriented language was a task and very exhaustive. I wish I could have started here instead. Going in reverse would be a breeze.

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Messing around with templates

Post by albinopapa » March 1st, 2018, 7:12 pm

I will admit, it's not difficult. I'm the road block here lol. As I said, I find it too distracting writing in a procedural style, because I'm always wanting to do: obj.Func(); Then I realize, "Oh, wait, I have to call a free function then pass obj to that function...doh!".

The closest I have come to procedural programming in the past was playing around in QBasic back in the mid '90s. I spent a lot of time making text stuff, but only real accomplishment back then was adding multiplayer to the provided "Nibbles" game. Earlier than that I think I was too young to really grasp programming and merely just copied what was in the book my parents bought me. None of them worked though, and without knowing what I was doing and no one to turn to for help, I gave up on it for a while.

Yes, I am glad to have object oriented languages, specifically C++. I don't really remember why I chose C++ over C# or Java in the beginning though. I think it was merely a quick google search for which programming language is used for making games. I tried back in 2001 to learn C++, but I can't say that the starting criteria was the same. I think I just looked up programming tutorials and C++ was the most popular.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

nG Inverse
Posts: 115
Joined: April 27th, 2012, 11:49 pm

Re: Messing around with templates

Post by nG Inverse » March 2nd, 2018, 12:03 am

Once you get the hang of the concept, I agree, it isn't difficult - but easier. Especially in the design stages.

I've been getting into C# as of lately. Mainly as I think it is the best suited for creating forms for applications. I tried the C/C++ variants with Win32 and MFC, but they are horrid and take a lot of time and patience just to get basic controls and their behaviors implemented. I'm currently working on a POS system with a user interface, accounts, admin control for settings, SQLite connection for data storage, items, inventory tracking, payroll management, etc. It's a huge project to bite off, but it's coming along quite nicely. I like the fact that C# has all the benefits of C++ (minus the speed I suppose) but a very low learning curve from C++. Garbage collection and the form editor are a huge plus as well :)

albinopapa
Posts: 4373
Joined: February 28th, 2013, 3:23 am
Location: Oklahoma, United States

Re: Messing around with templates

Post by albinopapa » March 2nd, 2018, 2:00 am

I tried a while back to get into C#, but being restricted to the message loop confused me too much. I'll get around to it again someday, but honestly, it would take another couple of years to understand it.
If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves. - gameprogrammingpatterns.com

Post Reply