Welcome Guest [Log In] [Register]
We hope you enjoy your visit.


You're currently viewing the Ultimate 3D Community as a guest. This means that you can only read posts, but can not create posts or topics by yourself. To be able to post you need to register. Then you can participate in the community active and use many member-only features such as customizing your profile, sending personal messages, and voting in polls. Registration is simple, fast, and completely free.

Join our community!

If you are already a member please log in to your account to access all of our features:

Username:   Password:
Add Reply
Templates
Topic Started: Sep 26 2009, 12:28 AM (392 Views)
NGen
Member Avatar
Advanced Member
[ *  *  * ]
Templates are a way of using a single function or class to handle multiple situations. The vector, list, map, and other STL classes of the sort are a good example. You can use the following piece of code:

Code:
 
std::vector <MyClass> MyClassVector;


This creates a vector (basically a managed array) of MyClass's, even though the vector class doesn't have any code made to deal with your class. Well, technically it does, which is why templates are so useful. You don't have to create a new function or class to deal with multiple types, you can just make one template function/class.

NOTE: Templates can be a confusing concept if you don't pay attention, so read carefully. It took me at least a week before I finally figured out templates, because I skipped around random parts of the chapter in my book.

--- The Basics ---

Let's start with creating a template function:

Code:
 
template < typename T > T AddValues ( T Type1, T Type2 ) {
return Type1 + Type2;
}


Yes, there are a lot of T's in that piece of code, but let's just pick it apart so that we don't have to see it like this. We'll analyze the beginning first:

Code:
 
template < typename T >


This obviously declares the function ( can also be used for classes ) as a template. typename is a keyword that is used to hold a variable type. This can be a class, a pre-defined declarator (int, float, double, etc.), an enum... basically anything that can be used to create a variable (where a variable is any space in memory that can be edited). However, typename can only be used in a template declaration (I'm lieing, there's one other place you can use it, but I'll show that later).

So, in a nutshell, this basically says that the class/function after the template<> part is, well... a template. A template that takes one template parameter. (A multi-template parameter template would be something like template <typename T1, typename T2>)

Note A: You have probably seen typename replaced with class in some other tutorials. This is also acceptable, but using the typename keyword makes a bit more sense, since it's self-explanatory as to how it's to be used. Class is accepted because int, float, etc. are all built-in classes that are managed by the compiler. However, you cannot inherit built-in classes.

Note B: You might have also seen a template declared as this:

Code:
 
template < typename T >
T AddValues ( T Type1, T Type2 ) { }

as well. This is equivalent to what we just entered, except that there's just a newline to make things a bit easier to read. I don't support this method, as it can make a user think that a template is set up using some strange compiler magic. For example, me.


Now, let's go on to the declaration of our function:

Code:
 
... T AddValues ( T Type1, T Type2 ) ...


Now we begin to see our T variable being used as what the typename keyword specifies it as: a type name. This is basically taking whatever the template parameter T was set to, and is using it to define the return type of this function, as well as the 2 arguments.


The definition of the code should be self-explanatory. Simply add the 2 values together, and return the result. This should also work for a class, so long as there is an overloaded + operator defined for the class.


--- Using More Than One Type ---

Compiling this (using the same AddValues template made previously):

Code:
 
AddValues<int> ( 5, 6.0f );


Will generate errors. Want to guess why?

We specify the template parameter to be an int, and yet in our second function parameter we give the function a float. That's not good.

The following code should fix it:

Code:
 
template < typename T1, typename T2 > T1 AddValues ( T1 Type1, T2 Type2 ) {
return Type1 + Type2;
}


Here, we allow the user to specify 2 template parameters to be used as the arguments, and we have the function return the value as the type defined by T1. Notice the order of the template parameters and the function parameters. The T1 and T2 variables are used in the same order. Please note that this has no significance other than simply defining the variable types. We could define both function parameters as T2, or we could've used T2, T1 instead. However, it's a good idea to keep these template parameters and function parameters in the same order, so as to not confuse the user.



--- Template Classes ---

Up until now, we've only been using functions for templates. Let's get onto classes.

Template classes are about as simple as template functions were:

Code:
 
template <typename T> MyClass {

};


The T variable can be used within the methods of the class, and the members themselves can also have templates:

Code:
 
template < typename T > class MyClass {
public:
template < typename N > T Cast ( N Arg ) { return (T)Arg; }
};


The T variable can also be used to define variables within the class:

Code:
 
template < typename T > class MyClass {
public:
T MyMember;
};


Note: In some compilers, this won't be accepted. Put the typename keyword before it to get it to compile:

Code:
 
template < typename T > class MyClass {
public:
typename T MyMember;
};

This is that other place where typename can be used.



--- Declaring a Template ---

is not possible. It's just the way the compilers work. You have to define your class/function right then and there. If you're putting your template in a header file, you can inline it if it's a function:

Code:
 
template < typename T > inline T AddValues ( T Val1, T Val2 ) { ... }


Note: An inline function is a function where the compiler basically takes the code in the function and fills it in where the function is used (Think #define). A function is normally automatically determined to be inline by the compiler if it uses inlining as optimization, but using the inline keyword tells the compiler that you prefer it to be inline. If you use VC++, you can use __forceinline to make VC++ create the function as an inline function.

Unfortunately, I do not know how a class would be handled, or if you would even have to handle it. I've never had to put a class template in a header file.

--- Template Specialization (Functions Only) ---

While you can use any type for a template, you might want to have the function do something different for a certain type. In this case, we use a method called Template Specialization, and is done as shown:

Code:
 
template <> AddValues < MySpecificClass > ( MySpecificClass Val1, MySpecificClass Val2 ) { ... }


So, let's break this down:

- The function is redefined, but with a "template <> " in front of it
- The AddValues function is used as though it were being called with the template parameter MySpecificClass (This determines the type to specialize by)
- All argument types are replaced from T to MySpecificClass

That's the main difference between a regular template definition and a specialized definition. A multi-parameter template would be done as so:

Code:
 
template <typename T1, typename T2> AddValues ( T1 Val1, T2 Val2 ) { ... }

template <> AddValues <Class1, Class2> ( Class1 Val1, Class2 Val2 ) { ... }


It's basically the same thing, but see how T1 was replaced with Class1, and T2 was replaced with Class2. The typenames and the class to be specialized by must match, otherwise the compiler will generate an error.

Note: It might be easier to think of a template as a sort of pre-processor directive, where the types are replaced with the template parameters.


--- Variadic Templates ---
Variadic templates are not yet a part of the current C++ standard, but they should be available when the C++0x standard is released. The draft can be found here:

http://en.wikipedia.org/wiki/C%2B%2B0x

Note: A variadic function is a function that can take an infinite amount of parameters. printf (part of stdio.h) is an example of a variadic function. Obviously, a variadic template will be a template that accepts an infinite amount of template parameters.




Did I not explain it well enough? If so, try here instead:
http://www.cplusplus.com/doc/tutorial/templates/
Edited by NGen, Sep 26 2009, 07:12 AM.
Offline Profile Quote Post Goto Top
 
skarik
Member Avatar
kitten eating scum
[ *  *  *  *  *  * ]
I like you.



Anyhow, you totally made me realize that "T" gets replaced. I mean, I knew how to use templates, but not to make them. I'm not sure if I'll ever need to make one, but it's very good knowledge, I think.
Blog|EHS
Offline Profile Quote Post Goto Top
 
NGen
Member Avatar
Advanced Member
[ *  *  * ]
Quote:
 
I like you.

Thanks, I tend to get that a lot. :P

I've noticed that a few of the tutorials here are quite short, and, like the VC++ thread, decided that I might try to heighten the bar for tutorials by creating my own. I always try to explain my tutorials as best as possible, and I've gotten quite good at it. B)


And templates are VERY useful. I'm making a template function right now to write a variable to a file (while also handling endianness), allowing for any kind of class to be used if it has the 'ToCharArray' function, and string objects are written as a string of chars (obviously) and are ended with 0xFF. Then, when you read a string it would read the whole text, like in GM. I'm assuming it did something similar to this. Which reminds me, I forgot to include template specialization. I'll add that now.
Offline Profile Quote Post Goto Top
 
Dr. Best
Member Avatar
Administrator
[ *  *  *  *  *  * ]
Good tutorial. Personally I love using templates. They are a great feature to avoid redundancy and they do not lead to an additional computing time cost at runtime, because they are resolved at compilation time. It is amazing what you can do with templates. Ever heard about template meta programming (TMP)? This term refers to a set of techniques, which can be used to compute values during compilation using templates. There has been a proof that TMP is Turing complete, so basically you can write code that computes any computable value, by making the compiler resolve templates. The program would have finished its task when compilation is done and the result would be somewhere in the compiler output (e.g. in the executable or in the build log).

Of course TMP is not really useful, but it gives you an idea of how powerful templates are. I have had a lot of fun with them. You can do the oddest things, if you derive template classes from non-template classes. Have a look at this code for an example:
Code:
 
// This class represents a bone chunk, which holds data describing a bone among with
// its place in the skeleton, its animation and its appended meshes.
class CU3DBone:public CChunk{
public:
// This constructor prepares this chunk object for loading or writing
CU3DBone():
piMesh(nMesh.GetData()),
pMeshToBoneSpace(nMesh.GetData()),
pScalingKey(nScalingKey.GetData()),
pTranslationKey(nTranslationKey.GetData()),
pRotationKey(nRotationKey.GetData()){
}

// This function returns the identifier of this chunk
CString GetIdentifier() const{
return "$U3D_BONE";
}

CSimpleDataSet<DWORD> iBone;
CStringDataSet Name;
CSimpleDataSet<DWORD> iParent;
CSimpleDataSet<float> BoneFrame;
CSimpleDataSet<bool> PassOnBoneFrame;
CSimpleDataSet<DWORD> nMesh;
CArrayDataSet<DWORD> piMesh;
CArrayDataSet<CMatrix4x4> pMeshToBoneSpace;
CSimpleDataSet<DWORD> nScalingKey;
CArrayDataSet<SScalingKey> pScalingKey;
CSimpleDataSet<DWORD> nTranslationKey;
CArrayDataSet<STranslationKey> pTranslationKey;
CSimpleDataSet<DWORD> nRotationKey;
CArrayDataSet<SRotationKey> pRotationKey;
};
This is the class, which is used to load the $U3D_BONE chunk from version 2.0 of Ultimate 3D model file format. As you can see it is derived from CChunk and all of its data is packed into some template data types. The awesome thing is that CChunk implements Read(...) and Write(...) functions. All these data set types you see in the code are derived from a class called CDataSet. CChunk automatically generates a list of them. Then when CChunk::Read(...) or CChunk::Write(...) is called, it calls CDataSet::Read(...) or CDataSet::Write(...) for all derived (template) classes in the list of CDataSet objects. It is a bit complicated but the bottom line is: Using templates I do not need to write any code for reading or writing chunks from binary file formats. All I need to do is specifying the architecture of the chunk as seen above.

One thing I would like to add to your tutorial is that template parameters do not have to be data types. They can also be values or functions. This is a relatively new addition to the C++ standard, but by now most compilers should support it. An example of how this could be used is a template for matrices with arbitrary size. The definition of the class could look like this:
Code:
 
// This class handles a matrix (or a vector) with an arbitrary number of lines and columns
template<unsigned long nLine,unsigned long nColumn=1>
class CMatrix{
public:
// This default constructor initializes this matrix as null-matrix
CMatrix(){
for(unsigned long i=0;i<nLine;++i){
for(unsigned long j=0;j<nColumn;++j){
SetEntry(i,j,0.0f);
}
}
}

// This operator adds two matrices together (component-wise) and returns the result
CMatrix<nLine,nColumn> operator+(const CMatrix<nLine,nColumn>& rRHS) const{
CMatrix<nLine,nColumn> Result;
for(unsigned long i=0;i<nLine;++i){
for(unsigned long j=0;j<nColumn;++j){
Result.SetEntry(i,j,GetEntry(i,j)+rRHS.GetEntry(i,j));
}
}
return Result;
}
// This operator performs a matrix multiplication between this matrix and the given other matrix
// and returns the result
template<unsigned long nRHSColumn>
CMatrix<nLine,nRHSColumn> operator*(const CMatrix<nColumn,nRHSColumn>& rRHS) const{
CMatrix<nLine,nRHSColumn> Result;
for(unsigned long i=0;i<nLine;++i){
for(unsigned long j=0;j<nRHSColumn;++j){
for(unsigned long k=0;k<nColumn;++k){
Result.SetEntry(i,j,Result.GetEntry(i,j)+GetEntry(i,k)*rRHS.GetEntry(k,j));
}
}
}
return Result;
}
// This function returns the value of the entry, at the given indices
inline float GetEntry(unsigned long iLine,unsigned long iColumn) const{
return ppEntry[iLine][iColumn];
}
// This function sets the value of the entry, at the given indices to the given value
inline void SetEntry(unsigned long iLine,unsigned long iColumn,float NewValue){
ppEntry[iLine][iColumn]=NewValue;
}

private:
// This array holds the nLine*nColumn entries of this matrix
float ppEntry[nLine][nColumn];
};

This template is interesting, because it depicts the rules for matrix multiplication and matrix addition perfectly. You can multiply an n x m matrix by an m x l matrix and you get a n x l matrix as result. You can not multiply an n x m matrix, by an k x l matrix, if k and m are different. If you try to do so with this template class, you will get a compiler error. If this class would not use templates like this the matrix class would need to save the line and column count in variables, which cost memory. Besides it would have to use new[] and delete[] to allocate memory, which can take more computing time. Besides you would not get information about invalid operations before the program runs. Here are some examples of its use.
Code:
 
#include "CMatrix.h"

// This function does nothing that makes any sense at all
void PlayAroundWithCMatrixABit(){
CMatrix<3,2> A;
A.SetEntry(0,0,1.0f);
A.SetEntry(1,0,2.0f);
A.SetEntry(2,0,3.0f);
A.SetEntry(0,1,4.0f);
A.SetEntry(1,1,5.0f);
A.SetEntry(2,1,6.0f);
CMatrix<2,3> B;
B.SetEntry(0,0,1.0f);
B.SetEntry(0,1,2.0f);
B.SetEntry(0,2,3.0f);
B.SetEntry(1,0,5.0f);
B.SetEntry(1,1,8.0f);
B.SetEntry(1,2,13.0f);
CMatrix<3,3> C=A*B;
CMatrix<2,2> D=B*A;
CMatrix<2,2> E;
E.SetEntry(0,0,-2.0f);
E.SetEntry(1,0,-4.0f);
E.SetEntry(0,1,-8.0f);
E.SetEntry(1,1,-16.0f);
E=E+D;
// The following lines give compilation errors, if the comments are removed. The
// numbers of colums and lines do not match.
//E=E+C;
//C=A*C;
}
Offline Profile Quote Post Goto Top
 
NGen
Member Avatar
Advanced Member
[ *  *  * ]
I've never heard of TMP, but it definitely sounds like an interesting concept. Unfortunately, I'll have to re-read that post a few times to figure out what you mean (I didn't read too much on inheritance... well, that's not true, I know how inheritance works, it's just that sometimes I get the vocabulary mixed up).

As for your 3D maths, I don't understand any of it. :P I just started geometry this year, so I'm hoping that at some point we'll get into linear algebra, which is what I'm guessing you're using. I saw a huge section on linear algebra in the GMC, where they explained its uses. I didn't understand it, so I'm just going to have to wait until geometry is done.

Quote:
 
One thing I would like to add to your tutorial is that template parameters do not have to be data types. They can also be values or functions.


I realized that I had left that out after I added the template specialization section, but it was 1AM at that point and I was just too tired to continue.
Offline Profile Quote Post Goto Top
 
Dr. Best
Member Avatar
Administrator
[ *  *  *  *  *  * ]
NGen
 
I've never heard of TMP, but it definitely sounds like an interesting concept. Unfortunately, I'll have to re-read that post a few times to figure out what you mean (I didn't read too much on inheritance... well, that's not true, I know how inheritance works, it's just that sometimes I get the vocabulary mixed up).
Yeah, it certainly is quite a complex use of templates and inheritance and I did not go into much detail, when explaining it. Still it is interesting that something like this is possible with templates. It is pretty much the most convenient imaginable way to specify file formats and it is just using standardized C++ features.

NGen
 
As for your 3D maths, I don't understand any of it. :P I just started geometry this year, so I'm hoping that at some point we'll get into linear algebra, which is what I'm guessing you're using. I saw a huge section on linear algebra in the GMC, where they explained its uses. I didn't understand it, so I'm just going to have to wait until geometry is done.
Actually it is not 3D it is nD ^_^ . That is the whole trick with the templates. Having these two template parameters you can generate vectors and matrices with arbitrary numbers of columns or lines and this allows you to represent positions, directions and linear transformations in any nD space. Since it was an example on templates and not on matrices you do not really need to know the rules of matrix maths, just have a look at the use of unsigned long template parameters.
Offline Profile Quote Post Goto Top
 
NGen
Member Avatar
Advanced Member
[ *  *  * ]
Alright, it's just that the use of those maths made it hard for me to comprehend what was going on.

P.S.- Is it safe to assume that I'll learn linear algebra in geometry?
Offline Profile Quote Post Goto Top
 
Dr. Best
Member Avatar
Administrator
[ *  *  *  *  *  * ]
NGen
Sep 27 2009, 09:33 PM
P.S.- Is it safe to assume that I'll learn linear algebra in geometry?
I have no idea what you do in geometry classes in the US. In Germany you scrap on the surface of linear algebra, but do not get into the depths of it at all. The knowledge you get is very useful for 2D and 3D graphics and physics though. Most of the time the focus is on vectorial analytic geometry.

If you attend linear algebra at the university this is quite different. It is a lot more abstract. You define vector spaces in a way that allows them to have arbitrary dimensions (including infinite-dimensional spaces) and the definition is not bound to real numbers at all.
Offline Profile Quote Post Goto Top
 
NGen
Member Avatar
Advanced Member
[ *  *  * ]
Well, since the US is apparently behind in its education, it'll probably be even worse for me. :/
Offline Profile Quote Post Goto Top
 
skarik
Member Avatar
kitten eating scum
[ *  *  *  *  *  * ]
NGen
Sep 28 2009, 03:31 AM
Well, since the US is apparently behind in its education, it'll probably be even worse for me. :/
Most of the books you'll get in school will have it, but your curriculum doesn't touch it. I'd recommend doing what I've been doing for a while: reading the whole book anyways.
Blog|EHS
Offline Profile Quote Post Goto Top
 
« Previous Topic · Tutorials · Next Topic »
Add Reply