Mundane C tricks ...

Discuss programming topics for the various GPL'd game engine sources.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Mundane C tricks ...

Post by Baker »

taniwha wrote:As for the QF header dependencies: it's not as bad as it looks: mathlib.h is for min/max/bounds, compat mabe strequal, sys.h for Sys_Printf and Sys_Error: all easy to replace.
I plan to mine through it, I trust tested code that has been in operation for long periods of time (beats experimenting) :D alloca scares me a bit, maybe not for gcc but I've actually read some stuff about alloca even on gcc that can cause trouble. Somewhat wondering what the point of alloca is in C99 after thinking about it in depth, you can make the allocation in the function as a regular variable like char * myvar[some number] ...

I came up with another C trick that half-way sucks, but in conjunction with the above COUNT_ARRAY #define has proven to be a time saver ...

Code: Select all

#define ForEach(_x) for (cur_index = 0, cur = &(_x)[cur_index]; cur_index < _num ## _x ; cur_index++, cur = &(_x)[cur_index])

Code: Select all

fbool Highscores_Table (void)
{
	int cur_index;
	highscore_t* cur;
	ForEach (highscores)
	{
		fprintf (stdout, "%i: %-03s %i", cur_index, cur->initials, cur->score);
	}
	return True;
}
I've read that in future C standards, they plan on finally getting threading standardized (I've mined through FTEQW before for threading stuff Spike has recommended, I find it annoying it varies from platform to platform). I wish they could figure out a few little conveniences to make working with structs just a tad more convenient.

Miscellaneous notes: SDL mixer, A+++++++. DevIL A++++++++ (time saver, I love how it conforms to the OpenGL way of doing things. Libpng is "difficult". Haven't tried compiling under CodeBlocks/MinGW/GCC yet, as I am a MSVC6 addict but I'm hoping for no surprises with DevIL.) Yet other miscellaneous notes: Constructed private "commands buffers" to process reading in entire files and feed said function another function to execute it. C is such a wonderful and awesome language once one ends up with a large enough knowledgebase to correctly and cleanly structure sets of functions. Did I mention I love C? :P
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Mundane C tricks ...

Post by taniwha »

The point of alloca is for variable sized temporary arrays. For example, you might know the absolute maximum number of elements needed for an array, but the usual number will be much less. Rather than messing with realloc and unnecessarily fragmenting your heap, you use alloca to allocated that maximum number off the stack, build your array, malloc the exact amount of memory needed and copy the allocaed array to the malloced array. This is exactly what alloca is being used for in qfplist.c.

Another example would be if you want to use strtok with a const char * string parameter: alloca a buffer, copy the string, strtok to your heart's content :).

The only real traps with alloca are you must not return a pointer to the allocated memory, and it's quite possible to run out of stack and trash other memory.
man alloca wrote: NOTES
The alloca() function is machine- and compiler-dependent. For certain
applications, its use can improve efficiency compared to the use of
malloc(3) plus free(3). In certain cases, it can also simplify memory
deallocation in applications that use longjmp(3) or siglongjmp(3).
Otherwise, its use is discouraged.

Because the space allocated by alloca() is allocated within the stack
frame, that space is automatically freed if the function return is
jumped over by a call to longjmp(3) or siglongjmp(3).

Do not attempt to free(3) space allocated by alloca()!
Your ForEach macro is quite good, actually. The only issue is it doesn't let you specify the "cur" and "cur_index" variables, which prohibits nested use of ForEach. Maybe:

Code: Select all

#define ForEach(_cur, _x) for (_cur = &(_x)[_cur##_index = 0]; _cur##_index < _num##_x; _cur = &(_x)[++_cur##_index])
(I took the liberty of also compressing the code a little).

Another way (eliminates a variable):

Code: Select all

#define ForEach(_cur, _x) for (_cur = (_x); _cur - (_x) < _num##_x; _cur++)
Of course, *_cur and _x[] must both have the same base type.

I haven't got a macro for it, but with this discussion I'm beginning to think I should, but here's my currently often used loop (whole function pasted for some context):

Code: Select all

static void
print_root_nodes (dstring_t *dstr, dag_t *dag)
{
    set_iter_t *node_iter;
    dasprintf (dstr, "    subgraph roots_%p {", dag);
    dasprintf (dstr, "      rank=same;");
    for (node_iter = set_first (dag->roots); node_iter;
         node_iter = set_next (node_iter)) {
        dagnode_t  *node = dag->nodes[node_iter->value];
        print_node_def (dstr, node);
        dasprintf (dstr, "      dag_enter_%p ->dagnode_%p [style=invis];\n",
                   dag, node);
    }
    dasprintf (dstr, "    }\n");
}
Leave others their otherness.
http://quakeforge.net/
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Mundane C tricks ...

Post by Baker »

taniwha wrote:Another way (eliminates a variable):

Code: Select all

#define ForEach(_cur, _x) for (_cur = (_x); _cur - (_x) < _num##_x; _cur++)
Of course, *_cur and _x[] must both have the same base type.
For reasons I can't immediately recall, I felt the index was important. Admittedly, I'm having trouble remembering why. And now, of course, it hits me ... I have a possibly evil insertion function that needs to know the index to work: (It could probably function without the index by deriving it from the memory address ...)

Code: Select all

void Memory_Array_Insert_Bumpdown (int index, byte* array, int numentries, size_t entrysizeof)
{
	int j;
	// Provided an array with 10 elements (0 through 9) and we bump at 4.
	// 4 will be new.   9 = 8, 8 = 7, 7 = 6, 5 = 4
	// j starts at 9.  8 7 6 5 > index
	for (j = numentries - 1; j > index; j --)
	{
		byte* blockhi = &array[(j + 0) * entrysizeof];
		byte* blocklo = &array[(j - 1) * entrysizeof];
		memcpy (blockhi, blocklo, entrysizeof);
	}
}
Have to admit I'm not 100% certain what your function is doing. Obviously something with linked lists, but for example I'm not sure what "da" stands for.

Something I did realize early on in learning C, I don't abbreviate anything. Now this style of doing things can make my code look "newbie" --- which I consider a bonus despite potentially differing opinions --- but when I go back to old code I rarely have to investigate what the code is doing:

Code: Select all

void    String_Edit_SlashesForward_Like_Unix	(char *WindowsStylePath); <---- hehe
const char* String_Get_Extension_With_Dot (const char *filename);	// Skips to extension 
re: Back to alloca ... I can't think of a circumstance where I would actually need to use it.

Case in point illustration:

Code: Select all

Some_Image_Function (byte* data, int width, int height)
{
// 	byte* row_allocation = alloca (width * 4 );   /* 4 =  RGBA */

	byte row_allocation (width * 4); // No alloca required

	...
I'm not seeing why alloca would actually ever need to be used I guess ... in the above both the alloca and the "no alloca way" would be deallocated from stack memory upon exiting the function.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Mundane C tricks ...

Post by taniwha »

Actually, names like that are very good: nothing quite like self-documenting code.

As for "da": dstring append. Thus, dasprintf is "sprintf to a dstring, but append it to what's already there". If you mean dag_t, that's "directed acyclic graph".

And the code itself: heh, close. Not linked lists, but sets. The sets are implemented via a bit array (but that's a private implementation detail :)). The code certainly code be implemented via linked lists (and the initial dag code did use lists), but using sets proved much easier (impossible to double-remove or double-add an item: no more looping through lists for removal, no worries about adding an item twice :)).

C's for-loops would have to be one of my favorite features of C. Any initializer, any test, any iterator. Using functions like I did in that code means I can change how sets are implemented and not worry about any code that uses them.

Alloca: hmm, well, I can think of a time: you're deep in the quake renderer somewhere and need a little bit of (variable sized) data. Since you're in a few loops deep, you don't want to use malloc (and friends). Hunk_TempAlloc is out of the question for some reason (does any engine other than QF have that one? I don't remember if I wrote it). alloca is your friend here: grab a chunk off the stack (very fast operation: two instructions on x86: sub esp,X; mov var,esp), work on the data, return from the function without worrying about freeing the code (exact same cost as having not used alloca: mov esp,ebp). Look in QF's libs/video/renderer/gl/gl_sky_clip.c for an example: very inner-loop. Or libs/video/renderer/r_light.c

For tool code: generally better to not use alloca. Image loaders: if you're in an inner-loop, you're doing it wrong :) (doesn't mean alloca isn't handy, though). Anyway, alloca is a powerful tool that should not be used lightly. Much like that weapon in Krull: if you're not sure, it's not time to use it.
Leave others their otherness.
http://quakeforge.net/
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: Mundane C tricks ...

Post by Spike »

in c99 you can use dynamically sized arrays. internally its an implicit alloca, but cleaner.
this avoids needing to make alloca part of the standard, of course.
alloca is much faster than malloc+free, and you don't need to care about freeing - which means it still gets freed if you longjmp out on say a Host_EndGame error or some such, but yes, you can still run out of memory, stack space is limited. makes it quite unsafe.
its awesome for small amounts though.
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Mundane C tricks ...

Post by mh »

taniwha wrote:Alloca: hmm, well, I can think of a time: you're deep in the quake renderer somewhere and need a little bit of (variable sized) data. Since you're in a few loops deep, you don't want to use malloc (and friends). Hunk_TempAlloc is out of the question for some reason (does any engine other than QF have that one? I don't remember if I wrote it). alloca is your friend here: grab a chunk off the stack (very fast operation: two instructions on x86: sub esp,X; mov var,esp), work on the data, return from the function without worrying about freeing the code (exact same cost as having not used alloca: mov esp,ebp). Look in QF's libs/video/renderer/gl/gl_sky_clip.c for an example: very inner-loop. Or libs/video/renderer/r_light.c
Of course, if your memory subsystem allows you to have more than one hunk, more than one zone and more than one cache, all of this becomes so much easier. You get single entry points for all allocations, you can implement garbage collection, and you get virtual immunity to memory leaks.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Mundane C tricks ...

Post by revelator »

Hmm though static analyzers warn about using alloca in loops because it can easily overflow the stack i seen no problems in Doom3 which does that a few places.

It does however scream use _malloca at me :P but then you also need to use freea so same story as malloc and free with the twitch that _malloca uses stack mem untill it overflows and then changes to heap mem.
Productivity is a state of mind.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Mundane C tricks ...

Post by Baker »

taniwha wrote:Much like that weapon in Krull: if you're not sure, it's not time to use it.
:D
mh wrote:Of course, if your memory subsystem allows you to have more than one hunk, more than one zone and more than one cache, all of this becomes so much easier. You get single entry points for all allocations, you can implement garbage collection, and you get virtual immunity to memory leaks.
I'm trying to think of a way to be able to peel off a frame if I use setjmp/longjump. Memory, like you suggested, 2nd hunk. I'm think of how to be able to "decommit" changes to structs, texture uploads, etc ... hmmm. Or maybe I shouldn't worry about that.

The one thing that sticks out and bothers me .... closing files! :D I guess I'd really prefer to not track that, but I guess if I don't have a loose end. Only takes one mess up in 100 functions to have a situation where somehow a function is closing a file out.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Mundane C tricks ...

Post by Baker »

Hmmm ... SDL apparently needs a different download for MinGW/GCC in Windows, a project can't use the VC sdl.lib / sdlmain.lib files, instead uses sdl.a and sdlmain.a files in a separate "For Mingw/Win32" download. Anyone know why this would be? [oddly enough SDL_mixer just uses the .lib]

I always like making a CodeBlocks project file since CodeBlocks/MinGW win installer can easily be installed on some other machine using a flash drive and only takes a couple of mins to do.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Mundane C tricks ...

Post by revelator »

MinGW uses a different naming convention for libs though it is possible to link with some msvc libs.
For dll import libraries it uses libname.dll.a and static libname.a one of the more obscure things is that the static libraries for mingw dont hold information on dependencies so you need to know what other libraries are needed. Thats because MinGW libraries actually arent libraries at all there an archive of object files (you can even open them with 7zip or winrar).

Some of the precompiled versions on the SDL site might not have import libraries for MinGW but mostly its pretty easy to recompile them for mingw :)
Productivity is a state of mind.
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Mundane C tricks ...

Post by mh »

Baker wrote:The one thing that sticks out and bothers me .... closing files! :D I guess I'd really prefer to not track that, but I guess if I don't have a loose end. Only takes one mess up in 100 functions to have a situation where somehow a function is closing a file out.
That's actually where C++ really shines. You can wrap your FILE * in a class and handle closing in it's destructor (just make sure that you know when your destructor runs!)

IMO a lot of the hatred you see for C++ is really stupid and immature - it genuinely does have some language features that are just so handy and make things infinitely easier. You also do see a lot of extremely tortured syntax and the like though, so treating C++ as "C with some handy extra bits that you can use when you really need them" is the best way to go.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Mundane C tricks ...

Post by taniwha »

Indeed, C++ certainly has its good bits, and even the STL has its uses, but oh, the error messages! The biggest legitimate gripe I know of is the lack of any form of standard for the ABI, and that might be history now.
Leave others their otherness.
http://quakeforge.net/
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Mundane C tricks ...

Post by revelator »

C++ error messages make me cringe :twisted: C is usually pretty clear on where you might have done something it does not like on C++ though you get things like undefined vtables and other not so clear messages but it does indeed make certain operations easier when you get it right :)
Productivity is a state of mind.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Mundane C tricks ...

Post by Baker »

mh wrote:
Baker wrote:The one thing that sticks out and bothers me .... closing files! :D I guess I'd really prefer to not track that, but I guess if I don't have a loose end. Only takes one mess up in 100 functions to have a situation where somehow a function is closing a file out.
That's actually where C++ really shines. You can wrap your FILE * in a class and handle closing in it's destructor (just make sure that you know when your destructor runs!)

IMO a lot of the hatred you see for C++ is really stupid and immature - it genuinely does have some language features that are just so handy and make things infinitely easier. You also do see a lot of extremely tortured syntax and the like though, so treating C++ as "C with some handy extra bits that you can use when you really need them" is the best way to go.
Just FYI: I don't have any sort of hate for C++. Some of the things about C++, I wish C had (all of these are obvious).

What I like about C is that when properly used, it is naturally procedural ... i.e., you can see the flow of events. But a lot of C code is hard to make heads or tails out of (I've tried to mine GTK for how it did something and surprisingly I was able to locate the vicinity but not the exact code, and there is no way that chain-reaction mess of dependencies is something I'll be able to compile on Windows or a Mac.

I feel that good code should be readable because code is supposed to be readable (languages exist to get away from 010000101001001 :D ).

You write about the most likeable C++ code possible.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Mundane C tricks ...

Post by Baker »

reckless wrote:MinGW uses a different naming convention for libs though it is possible to link with some msvc libs.
For dll import libraries it uses libname.dll.a and static libname.a one of the more obscure things is that the static libraries for mingw dont hold information on dependencies so you need to know what other libraries are needed. Thats because MinGW libraries actually arent libraries at all there an archive of object files (you can even open them with 7zip or winrar).

Some of the precompiled versions on the SDL site might not have import libraries for MinGW but mostly its pretty easy to recompile them for mingw :)
Ah, I see ... Makes a great deal of sense now.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Post Reply