Skip to main content
gamedev.net
🔒 Locked

#define magic

Started by Diodor 16 replies 500+ views
Original Post
Diodor
Diodor
What are the weirdest things that can be achieved with preprocessor directives? Here are some of my most advanced macros: ASSERT macro with variable number of parameters that automatically logs the file and line number of the error.
      
extern int MAIN_assert_line;
extern char MAIN_assert_file[128];
extern void MAIN_assert (int x, char* txt, ...);

#define ASSERT (MAIN_assert_line = __LINE__,  

	strcpy (MAIN_assert_file, __FILE__)), MAIN_assert

.......

int MAIN_assert_line = 0;
char MAIN_assert_file[128] = "";
void MAIN_assert (int x, char* txt, ...)
{
	char buf[256];
    va_list	va;

	if (0 == x)
	{
		sprintf (buf, "file %s line %d \"",
			MAIN_assert_file, MAIN_assert_line);
		va_start(va, txt);
		vsprintf (buf + strlen (buf), txt, va);
		strcat (buf, "\"\n");
		fprintf (stderr, buf);
		MAIN_kill (); // frees everything

		exit (1);
	}
}

........

ASSERT (n < 5, "buffer overflow %d", n);
  
Code that calculates the height of points in a Bezier patch. Notice the ## token-pasting operator.
        
#define B0(u) ((1 - (u)) * (1 - (u)) * (1 - (u)))

#define B1(u) (3 * (u) * (1 - (u)) * (1 - (u)))

#define B2(u) (3 * (u) * (u) * (1 - (u)))

#define B3(u) ((u) * (u) * (u))

#define ADD(i, j) (rez += ctrl[i][j] * B##i (u) * B##j (v))

float h (float u, float v)
{
	float rez = 0.0f;
	ADD(0, 0);
	ADD(0, 1);
	ADD(0, 2);
	ADD(0, 3);

	ADD(1, 0);
	ADD(1, 1);
	ADD(1, 2);
	ADD(1, 3);

	ADD(2, 0);
	ADD(2, 1);
	ADD(2, 2);
	ADD(2, 3);

	ADD(3, 0);
	ADD(3, 1);
	ADD(3, 2);
	ADD(3, 3);
	return rez;
}
  
This almost looks like Lisp
  
#include <stdio.h>

#define WRITE_OP(operator, a, b) printf ("%d "#operator" %d = %d\n", 

	((int)(a)), ((int)(b)), ((int)(a)) operator ((int)(b)))

void main (void)
{
	WRITE_OP (+, 5, 3);
	WRITE_OP (*, 7, -5);
}

//result:

//5 + 3 = 8

//7 * -5 = -35

      
[edited by - Diodor on April 14, 2002 5:21:04 AM]
SabreMan
SabreMan
Just because you''ve found a fun new toy doesn''t mean you should use it all the time. Macros are bad form - try and avoid them.
Diodor
Diodor
quote:

Just because you''ve found a fun new toy doesn''t mean you should use it all the time. Macros are bad form - try and avoid them.



It doesn''t mean I shouldn''t play with it at all either.
Null and Void
Null and Void
Sure, macros that affect code having nothing to do with them can cause problems, but I''ll often times write a macro for a single function then undefine it at the bottom of that function for sheer simplicity. They''re a tool like any other, as long as you know their faults there''s no reason to use them in places where the faults don''t come up.

the Speed Bump
the Speed Bump
Coroutines! :D

http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

"Eagles may soar, but weasels don''t get sucked into jet engines."
"There is only one everything"
Diodor
Diodor
Thanks the Speed Bump, that was a great read! I also liked the Macro are evil articles.

I want more
TerranFury
TerranFury
I once saw a header file that used macros to impliment cin and cout in standard C. That''s pretty impressive!
LessBread
LessBread
quote:
Original post by TerranFury
I once saw a header file that used macros to impliment cin and cout in standard C. That''s pretty impressive!


Got a link?

There was a "code of the day" at flipcode last year that used macros to fake a vector template - that''s vector as in stl vector.

Here''s an interesting application:
Using a C preprocessor as an HTML authoring tool
http://www.cs.tut.fi/~jkorpela/html/cpre.html
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Null and Void
Null and Void
quote:
Original post by LessBread
There was a "code of the day" at flipcode last year that used macros to fake a vector template - that''s vector as in stl vector.

There''s an entire mergesort macro (inlined and emulating templates by the nature of macros) that is or was in the Linux kernel source (I saw it in a mailing list archive ). It even named each variable using with its line number in the name, so that you could call it multiple times in a single function. I might have the code sitting around still, I''ll see if I can find it .

Dean Harding
Dean Harding
My favorite macro is one that I saw in a Chris Hargrove tutorial a few years back: http://www.loonygames.com/content/1.17/cotc/. The site's been closed for a while now, so there's no new tutes, but this was really good when it came out

Basically it's for a quake-like console system, after you have it all set up, you can define a new console variable like this:


VARIABLE( my_var, typeInteger, "0", true )
{
    // code that get's called when the value changed
    if( atoi( newValue ) == 100 )
    {
        g_Console->Print( "Hello!\n" );
    }
}


or a console function like this:


FUNCTION( my_func )
{
    for( int i = 0; i < argc; i++ )
    {
        g_Console->Print( "argv[%d] = %s\n", i, argv[ i] );
    }
}


Then, you can do things like:


g_Console->Execute( "my_func 1 cmd line blah" );
g_Console->SetValue( "my_var", "100" );


The output from this is (as you'd expect):


1
cmd
line
blah
Hello!


It's very neat...

(edit: Formatting)


codeka.com - Just click it.

[edited by - Dean Harding on April 15, 2002 6:58:37 PM]

[edited by - Dean Harding on April 15, 2002 6:59:04 PM]
LessBread
LessBread
quote:
Original post by Null and Void
There''s an entire mergesort macro (inlined and emulating templates by the nature of macros) that is or was in the Linux kernel source (I saw it in a mailing list archive ). It even named each variable using with its line number in the name, so that you could call it multiple times in a single function. I might have the code sitting around still, I''ll see if I can find it .


That would be cool, if you could find it, I''d like to see it. My linux box is dead at the moment and I''ve been too lazy to fix it.

"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Null and Void
Null and Void
quote:
Original post by LessBread
That would be cool, if you could find it, I''d like to see it. My linux box is dead at the moment and I''ve been too lazy to fix it.

I found it, but it''s too large to post here, so I''ll just email it to you.

LessBread
LessBread
quote:
Original post by Null and Void
I found it, but it''s too large to post here, so I''ll just email it to you.


Thanks! Man, there were so many backslashes I thought it was raining

Here''s the preprocessor output for a simple call to the macro:

FAST_MERGE_SORT (LIST, TYPE, NEXT, LESS_THAN_OR_EQUAL_P);


  

do {TYPE * _stack [8 * sizeof (unsigned long)];TYPE ** _stack_ptr = _stack; unsigned long _run_number = 0UL - 1; register TYPE * _list = (LIST); register TYPE * _current_run = (0); if (1 && (_list == 0 || _list-> NEXT == 0)) break; if (! 1 && _list == 0) goto __FAST_MERGE_SORT_empty_list_413; while (_list != 0) { *_stack_ptr++ = _current_run; _current_run = _list; if (_list-> NEXT != 0) { if (LESS_THAN_OR_EQUAL_P (_list, (_list-> NEXT))) _list = _list-> NEXT; else { _current_run = _list-> NEXT; _list-> NEXT = _current_run-> NEXT; _current_run-> NEXT = _list; } if (_list-> NEXT != 0) { if (LESS_THAN_OR_EQUAL_P (_list, _list-> NEXT)) _list = _list-> NEXT; else {TYPE * _tmp = _list-> NEXT; _list-> NEXT = _tmp-> NEXT; if (LESS_THAN_OR_EQUAL_P (_current_run, _tmp)) { _tmp-> NEXT = _list; _current_run-> NEXT = _tmp; } else { _tmp-> NEXT = _current_run; _current_run-> NEXT = _list; _current_run = _tmp; } } while (_list-> NEXT != 0 && LESS_THAN_OR_EQUAL_P (_list, (_list-> NEXT))) _list = _list-> NEXT; } } {TYPE * _tmp = _list-> NEXT; _list-> NEXT = 0; _list = _tmp; } if (!(++_run_number & 1)) continue;__FAST_MERGE_SORT_final_merge_413 : { unsigned long _tmp_run_number = _run_number; do { register TYPE * _other_run = *--_stack_ptr;TYPE * _output_run = _other_run; register TYPE ** _output_ptr = &_output_run; for (;;) { if (LESS_THAN_OR_EQUAL_P (_other_run, _current_run)) {__FAST_MERGE_SORT_merge_1_413 : _output_ptr = &_other_run-> NEXT; _other_run = *_output_ptr; if (_other_run != 0) continue; *_output_ptr = _current_run; goto __FAST_MERGE_SORT_merge_done_413; } *_output_ptr = _current_run; goto __FAST_MERGE_SORT_merge_2_413; } for (;;) { if (! LESS_THAN_OR_EQUAL_P (_other_run, _current_run)) {__FAST_MERGE_SORT_merge_2_413 : _output_ptr = &_current_run-> NEXT; _current_run = *_output_ptr; if (_current_run != 0) continue; *_output_ptr = _other_run; goto __FAST_MERGE_SORT_merge_done_413; } *_output_ptr = _other_run; goto __FAST_MERGE_SORT_merge_1_413; }__FAST_MERGE_SORT_merge_done_413 : _current_run = _output_run; } while ((_tmp_run_number >>= 1) & 1); } } _run_number = (1UL << (_stack_ptr - (_stack + (1 || _stack [0] == 0)))) - 1; if (_run_number) goto __FAST_MERGE_SORT_final_merge_413;__FAST_MERGE_SORT_empty_list_413 : (LIST) = _current_run; } while (0);


Thank goodness for these new source boxes!
"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
Puzzler183
Puzzler183
This is some very cool stuff... That coroutines article was great! Have to see what my company would say about it .
SabreMan
SabreMan
I'm afraid I made the assumption in my earlier post that you were talking C++, when you're clearly talking C. My mistake! Just to continue in that vein, I thought I'd mention I'm not convinced that there is anything you'd want to do with coroutines that you can't do with function objects. So, in C++ terms, to even justify macros for coroutines, you'd have to justify coroutines first!

As soon as you start needing to simulate such techniques through degenerate language features, you should be questioning whether you are using the right language for the job.

[edited by - SabreMan on April 16, 2002 4:57:47 AM]

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.