(macros + functions) && (templates) [C++]

Started by
17 comments, last by CTar 18 years, 5 months ago
Ok, I have two questions. The first: I know about templated classes:

template<class T> class foo { T member; };
But is it possible to just have a templated function? Like this:

template<class T> T add(T x, T y) {return x + y;}
The second question: Whats the difference between a normal function and a macro?

//The macro:
#define ADD(x, y) ((x) + (y))

//The function:
int Add(int x, int y) {return x + y;}

My guess would be that the macro can work with any type, but wouldnt a template fix that? That is, if the answer to my first question is 'yes'.
-----------------------------....::::DRAGON BALL Z::::....C<<"+"<<"+"; // Go C++ !!!-----------------------------
Advertisement
Yes a function can be templated... I'm just learning about them myself and found a ton of good tutorials on google. Here is one on functions specifically.

http://cplus.about.com/od/beginnerctutorial/l/aa070202a.htm

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

Yes, you can have a templated member function, and the syntax looks pretty much like you have. A macro is different from a function in that it does preprocessor text replacement rather compiling a function. If you have your add macro and the following code:
c = Add(a, b);

It changes the code to:
c = a + b;

Before the compiler ever sees the code. To the compiler Add() doesn't exist at all.
Quote:Original post by dudedbz1
is it possible to just have a templated function? Like this:
template<class T> T add(T x, T y) {return x + y;}

Certainly.

Quote:The second question:

Whats the difference between a normal function and a macro?
*** Source Snippet Removed ***
My guess would be that the macro can work with any type, but wouldnt a template fix that? That is, if the answer to my first question is 'yes'.

A template could be written to emulate that functionality. MACROs can be tricky to use and setup in certain situations, as can templates, but templates have a number of advantages: type safety, scope obedience, flexibility, etc. ... I try and stay away from MACROs myself.

A great book for learning about templates is C++ Templates: A Complete Guide. Highly recommend it.
:stylin: "Make games, not war.""...if you're doing this to learn then just study a modern C++ compiler's implementation." -snk_kid
Quote:Original post by dudedbz1
Ok, I have two questions. The first:

I know about templated classes:
template<class T> class foo { T member; };

But is it possible to just have a templated function? Like this:
template<class T> T add(T x, T y) {return x + y;}


The second question:

Whats the difference between a normal function and a macro?
*** Source Snippet Removed ***
My guess would be that the macro can work with any type, but wouldnt a template fix that? That is, if the answer to my first question is 'yes'.


macros can be a pain because their arguments dont always woek as you intend.

the classic example is

#define SQUARE(X) ( (X) * (X) )

what if you do this:

int other = SQUARE(i++);

it gets inlined into code as

int other = ( (i++) * (i++) );

which increments i twice, but is not immediatly obvious.

templated functions dont have such hidden problems.

the other main difference is that such a templated function is translated at compile time. so it obeys the other rules of the language( it can be part of a namespace etc... ). in c++ the preferred way to have "macro-like" functionality is to used templated functions.

There is some differences between them, lets consider these add functions:

#define Add1(x,y) (x+y)int Add2(int x,int y){    return x+y;}inline int Add3(int x,int y){    return x+y;}template<typename T>T Add4(T x,T y){    return x+y;}template<typename T>inline T Add5(T x,T y){    return x+y;}template<typename T,typename T2>T Add6(T x,T2 y){    return x+y;}template<typename T,typename T2>inline T Add7(T x,T2 y){    return x+y;}template<typename T,typename T2>T Add8(T x,T2 y){    return x+static_cast<T>(y);}template<typename T,typename T2>inline T Add9(T x,T2 y){    return x+static_cast<T>(y);}


Ok we have 9 Add functions, they all add 2 types. The first is the most simple, just a preprocessor function, the preprocessor should be seperate from the C++ compiler so first the preprocessor runs the code and generates code. So imagine this code:
void Foo(){    Add1(5,3);}

The preprocessor will start by generating this code:
void Foo(){    5 + 3 ;}


The problem with this is that it is not typesafe.

The second and third add function is a standard function taking an int. So this restricts the program to only pass integers or at least requires them to convert to integer, so if you have a short (and sizeof(short)!=sizeof(int) which it normally is) then the short will just gets converted without telling you and this could give performance problems if done a lot, from short to integer ain't very bad though. The difference between the second and third is that the third is "inline" which means the compiler tries to put the code "in the line it was called from", so it will try to generate the same code as the preprocessor function. The second is not inlined which means that the program needs to jump to the function and get back again which probably takes much longer than the actual addition, so this is not how you should do it. Note that inline is just a suggestion so the compiler can choose for itself, but any decent compiler should inline both the second and third add function since it is so small.

The fourth and fifth is the same as the second and third, just templated so it can take more types and if you pass another type like a short you don't need to convert it to an integer since it will just perform a short addition.

The sixth and seventh is a little tricky, they take too different (they can also be equal) types which means you can pass a float and a double for example and you don't need to convert one to the other (unless the addition requires it). It only works if we have an addition operator taking T and T2 or we have an implicit conversion from T2 to T.

The eighth and nineth is just like the sixth and seventh they just tries to convert from T2 to T explicitely, so it will probably work with more types, but can be a bit slower since if the types aren't equal we will do the conversion even though an addition operator can take T and T2 we will also convert even though there might be a loss of data, or precision.

We could also have created add functions returning T2 instead of T, but that would just be too complicated. We could also make a T3 which were the return type.
Macros is known to have nasty side effects (for beginners anyway),
aswell as making debugging harder.

If you are using anything "bigger" that a primitive datatype
as the T argument, you should also pass the arguments by referance for better efficiency/speed
template<class T> T add(const T& x, const T& y) {return x + y;}
Quote:Original post by pulpfist
Macros is known to have nasty side effects (for beginners anyway),
aswell as making debugging harder.

If you are using anything "bigger" that a primitive datatype
as the T argument, you should also pass the arguments by referance for better efficiency/speed
template<class T> T add(const T& x, const T& y) {return x + y;}


I have created a little class to solve this problem, I'm still a beginner with template metaprogramming and this is the first thing I try to do so it might not be very good, but I have created a structure with can give you type "const T&" if sizeof(T)>sizeof(T&) and type "const T" if sizeof(T)<=sizeof(T&).

The code for the class is:
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// DOESN'T WORK, SEE POLYMORPHIC OOP'S POST// I will leave it here though so people can see// what he is talking about, but don't use it.//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////template<typename T>struct ReadOnlyValue{	// Get the size of a reference to T and the type itself so we can later compare and	// choose what to use.	enum	{		ReferenceSize = sizeof(const T&),		ValueSize = sizeof(const T)	};	template<bool UseValueType>	struct _Type;	template<>		struct _Type<true>	{		typedef const T Type;	};	template<>		struct _Type<false>	{		typedef const T& Type;	};	typedef typename _Type<ReferenceSize>=ValueSize>::Type Type;};*/


EDIT: But this stuff is in most cases not worth doing.

[Edited by - CTar on November 5, 2005 3:58:23 PM]
I would like to point out that macros don't inherently have the problems mentioned. C and C++ macros do, but (for example) Scheme's hygienic macros don't.
Quote:Original post by Roboguy
I would like to point out that macros don't inherently have the problems mentioned. C and C++ macros do, but (for example) Scheme's hygienic macros don't.

Of course, the OP isn't refering to Scheme. So your comment is completely pointless.

CM

This topic is closed to new replies.

Advertisement