News:

Simutrans Wiki Manual
The official on-line manual for Simutrans. Read and contribute.

Iterating function macros for vectors, etc.

Started by jamespetts, February 26, 2009, 12:58:39 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

jamespetts

Following the recent synchronisation of all of the collection classes' function syntax, I have written some very simple function macros to make code for iterating through vectors a little easier. They are written as function macros not methods of the collection classes because of the need to specify a specific index for each iteration. They enable the following code:


for(unsigned short i=0;  i<last_built.get_count();  i++  ) {
grund_t* gr = welt->lookup(last_built[i]);
...


to be replaced with code like this:


ITERATE(last_built,i)
{
grund_t* gr = welt->lookup(last_built[i]);
...


The function macros work with any collection class that has two properties: (1) a "get_count()" method, that returns a 1 based index representing the number of elements currently contained in the collection; and (2) an overloaded [] operator allowing access to individual elements using a 0 based index. Here is the code for the function macro, which should be inserted at the top of the file of every collection class with those properties:


#ifndef ITERATE
#define ITERATE(collection,i) for(uint16 i = 0; i < collection.get_count(); i++)
#endif

#ifndef ITERATE_PTR
#define ITERATE_PTR(collection,i) for(uint16 i = 0; i < collection->get_count(); i++)
#endif


If it would be helpful, I could try to make up a patch file against the standard version of Simutrans (they are currently integrated into Simutrans-Experimental) for easy integration into the trunk. These macros are helpful in that: (1) they reduce the time that it takes to write iteration loops for vector type collections; and (2) they reduce the possibility of errors such as the following:


for(unsigned short i = last_built.get_count();  i <= 0;  i--  ) {
grund_t* gr = welt->lookup(last_built[i]);
...


Any comments would be appreciated :-)
Download Simutrans-Extended.

Want to help with development? See here for things to do for coding, and here for information on how to make graphics/objects.

Follow Simutrans-Extended on Facebook.

Dwachs

just curious: would that be possible with templates instead of macros?
Parsley, sage, rosemary, and maggikraut.

gerw

I think you have to put the first arguments into brackets for more safety:
#ifndef ITERATE
#define ITERATE(collection,i) for(uint16 i = 0; i < (collection).get_count(); i++)
#endif

#ifndef ITERATE_PTR
#define ITERATE_PTR(collection,i) for(uint16 i = 0; i < (collection)->get_count(); i++)
#endif


Maybe you could also backup the count of the vector like this (but I think there's no easy way to avoid conflicts of the max-variable):

#define ITERATE(collection,i) uint16 max = (collection).get_count(); for(uint16 i = 0; i < max; i++)


I think, I wouldn't like it. A for-loop is more readable than such a macro.

prissi

Then you could use the std-like iterator templates in there anyway. That way you could easily switch between vectors and lists. Moreover they are save for deletion etc. which macros are not.

You could then make an iterator macro that automatically iterates either list or vectors.

jamespetts

Gerw,

would there ever be a situation in which something not in brackets could be passed to the macro that (1) works if it is in brackets; and (2) does not work if it is not? I cannot immediately imagine such a case, as the macro is defined only for template classes, where there would just be the name of the template object passed, but perhaps you have thought of something that I have not...?

As to the saving of the count variable, that is not a bad idea, although it does cause safety problems. Not so much that "max" might conflict with something, as one could choose a keyword that is very unlikely to do so (such as "MACRO_ITERATOR_COLLECTION_TOTAL_COUNT_SIZE"), but that, if the operation involves a deletion of one of the elements, the change in the count of the collection would not be registered, and an out of range error would be produced as for loop would try to access an element beyond its range.

Dwachs and Prissi,

the reason that templates are not used is because it would not produce the same result; one would not be able easily to access

collection[i]->some_function();


(for example) with so few lines of code. One would have to use, as Prissi suggests, an iterator; but I find iterators cumbersome to code, and, as I understand things, are slower to execute, since they require additional function calls and objects, whereas a straight for loop is very simple and accesses the collection directly. Having first to declare an iterator and then advance it is more work both to code and execute than a simple loop, especially, in the case of coding time, where it is given a function macro.

I had the idea for this macro from the foreach keyword in C#, the functionality of which this macro is designed to emulate as much as a C++ function macro sensibly can. In C#, one can write something like this:


foreach(object i in collection)
{
         i.some_function();
}


The idea is that the ITERATE macro allows a very similar result: compare the above to this example:


ITERATE(i,collection)
{
           collection[i]->some_function();
}


The readability comes in part from the similarity to C#'s foreach keyword.

But I'm interested in Prissi's suggestion of an iterator macro; might I ask: how would that work exactly?
Download Simutrans-Extended.

Want to help with development? See here for things to do for coding, and here for information on how to make graphics/objects.

Follow Simutrans-Extended on Facebook.

prissi

you can have a foreach macro with an iterator.

#define FOREACH(typ,basis,iter) for (typ::iterator iter = basis.begin(); iter != basis.end();  ++iter)

and you access i-> or (*i). all desired operators.

This works FOREACH(slist_tpl<fabrik_t *>, fablist, fab ) als well as for FOREACH(vector_tpl<fabrik_t *>, fablist, fab )

One could also think to have and end_iter=basis.end() define once and then iterate without rechecking each time; but this way it is saver.

However, I strongly suggest not using macros for lasy typing un such cases. This easily causes errors that are hard to catch later.

VS


My projects... Tools for messing with Simutrans graphics. Graphic archive - templates and some other stuff for painters. Development logs for most recent information on what is going on. And of course pak128!

jamespetts

Prissi,

that could work for iterators - but would lose the speed advantage of a for loop. Interesting, though.
Download Simutrans-Extended.

Want to help with development? See here for things to do for coding, and here for information on how to make graphics/objects.

Follow Simutrans-Extended on Facebook.

prissi

Those iterators are as fast as the normal for loop, since the just return pointers and compare pointers. Since the std uses also such iterators, compiler are nowadays very much trimmed to optimize those.

And the boost macro works only on std and not on simutrans marcos I think. (Apart from the fact that boost is a pain to set up on some achritectures and makes compilation more difficult for many people.)

jamespetts

Ahh, interesting - I always thought that iterators were slower. Don't they require more memory because they create a special iterator object? If they really are just as fast, your FOREACH macro is very interesting indeed.
Download Simutrans-Extended.

Want to help with development? See here for things to do for coding, and here for information on how to make graphics/objects.

Follow Simutrans-Extended on Facebook.

gerw

Quote from: jamespetts on February 26, 2009, 08:54:34 PM
would there ever be a situation in which something not in brackets could be passed to the macro that (1) works if it is in brackets; and (2) does not work if it is not?
Maybe some fancy constructs like
ITERATE_PTR(collection+4,i)
where collection is a pointer to an array of vectors.

jamespetts

Couldn't one just write


ITERATE_PTR((collection+4),i)


?
Download Simutrans-Extended.

Want to help with development? See here for things to do for coding, and here for information on how to make graphics/objects.

Follow Simutrans-Extended on Facebook.

gerw

Of course. But it's safer (and more programmer friendly) when you add the brackets to the macro definition.

jamespetts

Ahh, yes, I see. The change suggested will be incorporated into the next version of Simutrans-Experimental.
Download Simutrans-Extended.

Want to help with development? See here for things to do for coding, and here for information on how to make graphics/objects.

Follow Simutrans-Extended on Facebook.