The aim of this article is to find a way to implement elements C++ has always missed. We can rediscover it in the C#, even if many languages have this natively for years.
The typeof() function is a way to give the type of a variable at compile time. So nothing to do with RTTI, (Run time Type Information, so runtime and not compile).
foreach() is a way to iterate on the elements of a collection, knowing a minimum about the way the items are stored in the collection.
typeof() and C++
Of course, like we know, typeof() doesn’t exists in C++. We know well sizeof(), very useful when you make some template programming, but no typeof.
Hey, Boost made it ! Sure but Boost it’s thousands of template class, and a lot of programmers are afraid of it, at least afraid of using it in real professional projects. Too much files, too much includes, too much potential compile time…
We’ll try to write a simple version, without dependencies on any other file, unlike Boost’s typeof.
Boost typeof()… without Boost
template<int N> struct typeof_class;
template<class T> struct wrap_type { typedef T class_type; };
#define REGISTER_TYPEOF(N,T) template<> struct typeof_class<N> \
{ typedef wrap_type<T>::class_type type_value; }; \
char (*typeof_fct(wrap_type<T>::class_type &))[N];
#define TYPE_OF(x) typeof_class<sizeof(*typeof_fct(x))>::type_value
How does it works ?
We know it’s impossible to resolve template class arguments implicitly. If we could, we would easily write something like
template<class T> class typeof { typedef T class_type; }
typeof<montype>::class_type maVariable = montype();
It doesn’t works. The only way of resolving automatically a parameter is to pass a parameter to a template function, for example:
template<class T> class typeoffunction(T& myTypedVar) { .... }
TheType myVar;
typeoffunction(myVar);
In this case we know the type of myTypedVar… but only within the function ! Anyway, using functions seems to be the key.
That’s why we define not only a function, but an array of function, which site is dependant on the type. So we can resolve automatically the function type passing the parameter to it, because we pass the type as parameter. If we dereference this function, we get the array, and the sizeof() this array returns a number dependant on the type.
void typetoNumber(int myParam)[123];
void typetoNumber(float myParam)[456];
int iVar;
sizeof(*typetoNumber(intVar)) // = 123
float fltVar;
sizeof(*typetoNumber(fltVar)) // = 456
At this step, we can have a number associated to a type, at compile time.
The only thing we only need to do is to implement specializations of a template class, template of an integer, and to create inside a typedef giving back the type associated to the number.
class numberToType<123> { typedef int MyType; };
class numberToType<456> { typedef float MyType; };
We can hide all these class in macros. So we can write
REGISTER_TYPEOF(1,int)
REGISTER_TYPEOF(2,float)
REGISTER_TYPEOF(3,CMyClass)
...
int x = 5;
TYPE_OF(x) y = x;
The only -important- thing is that we need to declare every type we’ll need to recognize in a typeof(). This constraint is not removable, but we’ll try to make this declaration as easy as possible.
Simplify the type declaration
We need to simplify this declaration. Using 1, 2, 3… incremental numbers seem impossible in a real world case, with multiple .h inclusions
We can use the __COUNTER__ compiler macro, that gives a different number every time it is used (fist 0, then 1…)
REGISTER_TYPEOF(__COUNTER__,int)
We can now create a type declaration macro that doesn’t takes any number. I also added all the related types registration like *, const, const*, vector, list… A single macro will also register all the related types.
REGISTER_IT_TYPEOF(bool)
Direct use : foreach()
Here we are. Our typeof() is easy to use and types are easy to declare. Let’s use it.
Classic use is a foreach() macro, that can iterate trough all the elements of an stl vector. I won’t be too long on the subject, there are many articles talking about this. The aim is to write a macro to enumerate all objects in a collection, and the code we write is independent of from collection type. (It’s the same way to iterate trough a vector, a list, ….)
#define foreach(_it_,_collection_) for(type_of(_collection_)::iterator _it_ = \
_collection_.begin();_it_ != _collection_.end(); ++_it_)
list<int> myList;
vector<int> myVector;
...
foreach(it, myList)
{
cout << *it << endl;
}
foreach(it, myVector)
{
cout << *it << endl;
}
// instead of
// for(list<int>::iterator it=myList.begin(); it!=myList.end(); ++it)
// for(vector<float>::iterator it=myVector.begin(); it!=myVector.end(); ++it)
and there, we gain some characters to write, but also some flexibility because we write code that is resistant to a container type change.
The user (not the creator) of a collection of items who only wants to iterate trough elements is not really interested in the containter (vector, list…), this is only a concern for the creator. So the user writes his code once, and if the collection container type changes, for a reason the creator only knows, all the code reading the collection doesn’t change.
We are not using fully abstract collections, like IList<T> in C#, but we still have, for the iteration part, similar functionalities, without any performance drawback !
Conclusion
for someone writing a lot of template code, typeof() can be very useful. Even if its use need a declaration of the types, it opens a lot of possibilities.
For the foreach() even if its use must be limited to cases where we don’t delete elements within the loop, the advantage is to write always the same code, whatever the container (or the element) is. We gain efficacity writing the code, but also readability (we can be sure the user doesn’t hides uncommon instructions inside its for). Wherever you see a foreach() macro, you know it’s a simple, harmless iteration.
Source code
You can download source code of implementation of typeof() and foreach(). You should also read the article…
References