C/C++ loops and macros
In C/C++ you often break from the middle of a loop instead of from the top/bottom. There are arguments against this — it doesn’t respect block structure and it isn’t a functional style. A break is just a goto. But it’s common, easy to understand, and a very useful pattern.
...
// General loop micro-pattern:
for ( ; ; ) {
.. do some stuff ..
if ( .. test .. ) break;
.. do some stuff ..
if ( .. test .. ) break;
.. do some stuff ..
}
...
// I've seen programmers define LOOP and LOOP_forever macros.
# define LOOP for(;;)
# define LOOP_forever for(;;)
...
LOOP {
if ( .. ) break;
.. etc ..
}
...
You should be careful when defining keyword and structure macros like LOOP — they interfere with editors and other tools and they can hurt readability. But if you like LOOP, you can go further:
// Getting carried away with macros.
# define LOOP for(;;)
# define LOOP_exit break
# define LOOP_next continue
# define LOOP_while( x ) if ( ! x ) break
# define LOOP_until( x ) if ( x ) break
...
LOOP {
LOOP_until( test );
.. do something ..
LOOP_while( test );
.. do something ..
if ( .. ) {
LOOP_next;
}
if ( .. ) {
.. do something ..
LOOP_exit;
}
.. do something ..
}
...
Finally, let’s imagine taking the loop macro to the limit. The following isn’t real code so don’t try to compile it.
// Fantasy loop macro.
...
loop named top_level {
int how_many_times = 0;
int my_sum = 0;
int my_sum2 = 0;
std::vector<int> my_vector;
std::stack<int> my_stack;
...
loop {
for x in { 1, 2, 4, 8 };
for e in 1..7;
for y = e * e; /* eval'd each time */
init g = x * x; /* eval'd once */
for z = 1 to 10 by 2;
for a = 10 downto 1;
for b = 2 inc by 2;
for c = 2 dec by 2;
for d = 2 then 6;
while my_test( );
until my_test( ) do (is_special = true) exit top_level;
until my_test( ) exit 2 levels;
exit;
next;
next 2 levels;
next top_level;
collect x in my_vector;
collect x in my_stack push;
sum x in my_sum;
reduce x in my_sum2 using plus;
count my_test( ) in how_many_times;
if ( ! LOOP_first_time ) {
std::cout << LOOP_count << std::endl;
}
finally {
std::cout << "leaving loop" << std::endl;
}
}
...
}
...
Of course this is no longer C/C++ — you can’t do this with #define, you’d have to use a more powerful pre-processor. One that can manipulate code blocks and define nested macros in an enclosing parent. The standard #define macros are not even meant to take blocks as params.
// I've seen this but it may not be portable and I don't recommend it.
# define STANDARD_CATCH( block_ ) \
try block_ \
catch( my::error::warning* w ) { \
my::error::log( w); \
} \
catch ( ... ) { \
my::error::log_unknown( ); \
throw; \
}
void some_fn( )
{
.. stuff ..
STANDARD_CATCH( {
// Does this macro expand correctly if a comma appears in the block?
.. stuff ..
} )
}
Macros like this are usually a bad idea in C/C++, but in Lisp they are common and a very powerful feature of the language. C/C++ has a lot of syntax that communicates structure and keeps the source compact. Lisp has a very simple syntax, which makes it wordy and full of deeply nested parentheses but also makes it easy to extend. Lisp has a programming model that makes it harder to write tight, efficient, close-to-the-metal code, so C and C++ are probably better choices for realtime and systems engineering. But you don’t have to have Lisp to get something like the Lisp macro facility — you could define a pre-processing facility for C/C++ that worked on the block level and added a lot of power. A simpler C++ sytax would help, but it’s not vital.
But this post is getting too long. I’ll talk about what these macros might look like later.
Comments
2 Responses to “C/C++ loops and macros”
Leave a Reply
This blog Is very informative , I am really pleased to post my comment on this blog . It helped me with ocean of knowledge so I really believe you will do much better in the future.
thanks for your sharing ! it is useful!