A while back I wanted to write some C code that looped over every element in a collection. But I wanted the code to be parametrisable in the face of different implementations of that collection, because it might have to link against several other pieces of code which might all store the things in question in a different format.
(
iteration over a collection of elephants by C switch abuse )
Comments 22
And then realised that you were working in C, not C++, and thus objects and interfaces were not an option for you!
And that made me remember why I like OOP so much.
Reply
But I think that even if I'd been able to guarantee the availability of C++, it's not clear to me in this context that the various implementations would have been set up that way - they're all in code bases maintained by different groups of people who have no incentive to cooperate on issues like this. So it's quite likely that I'd still have needed some sort of macro-based parametrisation at the call site, even if it was less overwhelming than what I've got here.
Reply
(And yes, I know that C# isn't suitable for all sorts of things.)
Reply
Reply
int specialfirsttimeflag;
for (elephant = head, specialfirsttimeflag= 1;
elephant != head && specialfirsttimeflag--;
elephant = elephant->next) {
Or you can't because it might conflict with a name outside the macro?
Reply
if (condition)
LOOP_OVER_ALL_ELEPHANTS(elephant) {
/* ... */
}Admittedly you might reasonably feel that I should just avoid calling the macro in any circumstances like that, which would be a fair enough position, but I note that all the other expansions discussed here (including the evil switch one) do work in that context.
But the other, more important, reason is that it has a declaration in it, and in C90 declarations can't be interleaved with code. (For annoying reasons, the code in which I was using this macro is required to compile cleanly in C90 as well as C99.) So if I put any other statement before calling LOOP_OVER_ALL_ELEPHANTS, the declaration of specialfirsttimeflag would become illegal.
Reply
You could possibly wrap up the "compare elephant and head, except the first time, then just return true" logic into a function with a static variable in, but it would mean the code would be definitely non-reentrant. (Unless your preprocessor did something clever with the LINE directive in the macro, but I can't remember, that wasn't in C90 either, right?)
Reply
I don't see how it helps you here, though. It surely can't save you from thread-safety issues, because it won't have a distinct value in each thread?
Reply
The STL iterator concepts are a well thought out framework for generic programming.
You can write code like this:
for (Elephant::iterator i=elephant.begin(); i!=elephant.end(); ++i) {
// loop body executed once for each elephant
}
…and it works regardless of the type of elephant. In C++0x, it gets even simpler:
for (auto &e: elephant) {
// loop body executed once for each elephant
}
The iterator for each container type deals with the specifics of stepping through the elements of an array, a linked list, a circularly linked list, a balanced tree, a hash table, a readdir(), a circular buffer, the characters of a UTF-8 string… whatever. Because the code is generic rather than run-time polymorphic, the optimiser is told precisely what's going on for a particular use of the ( ... )
Reply
Reply
Reply
(Although I'm unsurprised Simon's effectively unable to change the codebase, to me something like this is a BIG FLAG that switching to a more expressive language is long overdue.)
Reply
Although honestly, while I love the tweaks, I think for all practical purposes, making it clear that LOOP_OVER_ALL_ELEPHANTS can't substitute in as an arbitrary statement is fine, it's clearly magic, you shouldn't TRY to use it in a more sophisticated context. (Also, pretty much always put braces after ifs and loops, unless the loop is a single short statement on the same line as the if, and even then I very rarely find I want to.)
Reply
Leave a comment