Mundane C tricks ...

Discuss programming topics for the various GPL'd game engine sources.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Mundane C tricks ...

Post by revelator »

Mostly i would agree but in the case i had it was something that translated to {const char,const char, const char, huge cast to null terminate this array of strings} in turn it would translate to {const char, const char, const char, 0} so i was wondering why not just use the null string '\0' instead of the huge cast since its an array of strings wouldnt that be more correct ? or am i missing something. Ofc i would newer use the null string for a non char pointer :) that would be silly.
Productivity is a state of mind.
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Mundane C tricks ...

Post by taniwha »

95% of the time, I couldn't care less what a variable's type is, which is a large part of the reason I despise Hungarian notation (*twitch*, *twitch*, *bleh*, I feel dirty just saying it :mrgreen:).

I don't know, maybe it's just something that comes with programming for 30 years (20 of which in C or C++), in a variety of fields (2d and 3d graphics, 2d and 3d physics, compilers, networking). After a while, beyond telling the compiler how the data is to be handled (eg, int vs float), type becomes meaningless. I don't think in terms of pointers and structs, I think in terms of "I've got this thing here, with these named features". Might be part of why I always get my . and -> mixed up :) (another part is python and qfcc (I didn't bother with ->, though I might add it as an option)).

Having to think about type ('\0' for char, 0 for int, 0.0 for float, NULL for pointers) would only get in the way (ouch, my head hurt just filling in the examples), just like having to get . and -> right does (and I read somewhere that either Kernighan or Ritchie (don't remember) regrets ->). I am very grateful that C always treats 0 of any type as false and non-zero of any type as true, and that just a bare 0 works for any non-aggregate type. The compiler gets out of the way and lets the programmer concentrate on the problem the code is to solve rather than futzing with trivial details.

Actually, I have a good argument against using '\0': it can be visually confused with '0', or worse, '10' (sure, gcc will warn about that one, but...). I think I read that somewhere in some style guide. Oh, and a second one: what is '\013'? :)
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:95% of the time, I couldn't care less what a variable's type is, which is a large part of the reason I despise Hungarian notation (*twitch*, *twitch*, *bleh*, I feel dirty just saying it :mrgreen:)
..
Actually, I have a good argument against using '\0': it can be visually confused with '0', or worse, '10' (sure, gcc will warn about that one, but...). I think I read that somewhere in some style guide. Oh, and a second one: what is '\013'? :)
Here is the philosophy I use:

1) Almost everything has been coded before.
2) Most of this code is functionally equivalent. (Compiles ... likely to nearly the same binary output, works ..., probably performs at near same speed ...)
3) Compiled output serves machines, code serves humans.

If everything has been coded before, code that integrates faster and understood more easily > any other functionally equivalent code.

Then again, that philosophy is rather idealistic. :D Doesn't take time directly into consideration, indirectly therefore doesn't take productivity into account. Indirectly. The bet is that in the long run, it exceeds those factors in the same way that a high quality tutorial has an impact massively greater than one no one likes or uses or the impact of a math teacher that kicks ass versus a crappy one. But "long runs" are no sure thing in practice. And certainly don't apply to every situation ...

But most of my learning to code has literally been either reading others' existing code or Googling usually involving searches with "stackoverflow" in quotes. :D And perhaps due to that, I feel a counter-obligation to contribute back to that flow of knowledge.

[ Strange new world we live in. Which is also the reason Linux is destined to win one day. Computers are soon to be keychain size (Raspberry PI is credit card size) and diverse and like Valve + Steam's efforts to make a Linux console, like Android / Kindle before ... or Mac OS X from BSD, eventually closed source gets left behind because open source is faster and as it breeds refinement and in a world of complexity no one wants to be starting from 0. ]
Last edited by Baker on Fri Dec 14, 2012 7:01 am, edited 1 time in total.
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 »

Heh true it can be a bit confusing :) atleast 0 is allways equal to none and doing things with an escaped string type might be rather hard to compute. Im not 100% on it but i think \013 would translate to 0.13 but is it a positive value ??? hmm probably though im not sure how to do a negative value with a string type like that unless char > '\stringvalue' or does it accept a - ? hmm. In that case i would probably prefer just using plain 0.13 ;)

I guess my style comes from not having learned neither C nor C++ professionally its just things i picked up over the years and theres a lot of holes still to be filled but mostly i can get things working though if i had to do it for a living i would probably starve to death :lol: im not exatly fast but i do the best with what i got. Only language i actually invested heavily in was basic back when the spectrum zx80 and amigas where the thing today i can remember two things from all that (jack and shit) :mrgreen: i guess when you dont use use a skill everyday it tends to go blank hehe.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Mundane C tricks ...

Post by revelator »

Totally agree baker :) if someone can use my ideas who am i to argue when i got so much in return i feel obliged to atleast leave a footprint of what i learned from all of it and if it benefits others all the better.
Productivity is a state of mind.
szo
Posts: 132
Joined: Mon Dec 06, 2010 4:42 pm

Re: Mundane C tricks ...

Post by szo »

taniwha wrote:... I despise Hungarian notation
Seconded. And I not just despise it but feel disgusted by it. (Try reading datatypes in the windows SDK and see....)
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Mundane C tricks ...

Post by taniwha »

The answer to '\013': 11.In char and string \ followed by the digits 0-7 indicate an octal number consisting of up to 3 digits.

For this reason, using \0 is actually bad practice, especially in strings, because char foo[] = "\012"; will produce the same string as char foo[] = "\a";, but char foo[] = "\089"; will produce the same string as char foo[] = {0, 0x38, 0x39, 0};.

To me, embedding the null char in a string is bad practice. I think the only time I've ever done it was for Turbo-Vision's palette strings (and then always as "\x00") ('twas an evil hack to allow palette specs to be concatenated at compile time (abused C's string constant concatenation), and it worked because they used (sizeof (palette_string) - 1)).

So, to sum up my rules (for myself (mostly*): "leave others their otherness" :)):
  • 0 is universal with no specific type. The only time I use 0.0 is when dealing with a lot of floats and I want things to line up nicely.
  • Never embed 0 (null char) in a string. If you want to initialize a byte buffer, use an array of numbers, preferably in hex. 0 in C strings is just asking for trouble.
  • Never explicitly test against 0/NULL/'\0' for equality or inequality (for any type). I do occasionally make an exception for the strcmp family, but don't count on it. This means always "if (foo)" or "if (!foo)". I might make an exception for when I want to convert true/false to 1/0, but I'll often do "!!bar" instead of "bar != 0".

    Note that <[=] 0 and >[=] 0 just can't be avoided, so that doesn't count (it's not really testing for (in)equality with 0 anyway).
* If I feel the urge (not often), I might "clean up" patches I accept from others, but I will never barge into someone's project and insist they follow my rules (I'll even bend my rules so code I contribute follows their conventions (twitching all the while ;)).
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 »

hehe :) ok no hungarian notations noted.
is'nt hex octal by nature ? though a hex number as a string terminator would probably make my head explode :P be gentle on the old hobby programmer here hehe.
Productivity is a state of mind.
taniwha
Posts: 401
Joined: Thu Jan 14, 2010 7:11 am
Contact:

Re: Mundane C tricks ...

Post by taniwha »

Uh, no, hex is not octal by nature. Hex is short for hexadecimal (ie, base 16: 0-F). Octal is base 8 (0-7). Both are convenient for programmers because there's an N:1 relationship between bits and symbols (4:1 for hex, 3:1 for octal). Hex is more appropriate for 8-bits per byte machines (2 hex digits per byte) while octal is more appropriate for 9-bits per byte machines (3 octal digits per byte). And yes, such machines existed (still exist?) in the mainframe world (IBM?).

Code: Select all

bin   oct hx d
00000 000 00 0
00001 001 01 1
00010 002 02 2
00011 003 03 3
00100 004 04 4
...
00111 007 07 7
01000 010 08 8
01001 011 09 9
...
01110 016 0e 14
01111 017 0f 15
10000 020 10 16
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:The answer to '\013': 11.In char and string \ followed by the digits 0-7 indicate an octal number consisting of up to 3 digits.

For this reason, using \0 is actually bad practice, especially in strings, because char foo[] = "\012"; will produce the same string as char foo[] = "\a";, but char foo[] = "\089"; will produce the same string as char foo[] = {0, 0x38, 0x39, 0};.
I like your answer. Not in the technical details because I haven't drawn a conclusions about those [I'm more of a listening type, especially when someone has coded in C for around 25 years longer than I have :D ] ... but in the strength of your convictions to explain it in detail.
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 »

Yeah, well, strong claims need strong support, to paraphrase the quote about proof.

However, I'm not so much about convincing someone I'm right as I am about getting the person to understand my claims and why I make them, giving them an opportunity to point out where I'm wrong (if I am, which is not guaranteed). Unfortunately, not many people see it that way :(.
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 »

I've started getting rather heavy into linked-lists. And to my surprise there, I've discovered that with parent/peer/child structure there are multiple ways you can need to traverse them.

I'm gunning for the oddball ability to save every possible user interface item and entities into the same file. With the ability to have multiple 3D viewports and an interface editor that can work with the data and dragable elements.

I'm also doing something possibly unwise with the display, penetrating through levels of parents/child to be able to calculate real hardware X, Y for "virtual points" in a viewport and setting up windows in such a way that the screen may have the window centered.

At the moment: I am frustrated. :D

/But isn't that just part of working on complex stuff in programming. :mrgreen: :mrgreen: If I gave up easily, hahaha ... you can't code stuff at all ... and you don't have this issue with great frequency then one must not be doing anything fun ...
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 »

Yeah, complex relationship networks grow a lot of hair. Going by some of your other comments, I'm sure you already know this, but this is one place where good variable naming is critical. "next? next what?" :)

The hairiness of dags and flow nodes in qfcc*, and the lack of importance in connection order is what made me really appreciate mixing arrays and sets. Multiple parents, multiple children, multiple peers (= dependencies). I found it easier to just have everything in an array, use a set for parents (if empty, a root node (yes, "a")), a set for all out-going edges (children and peers), and an array of pointers for the actual children (0-3) because child order is important. BTW, this is now publicly available through git (see the qf site for details).

I've been using linked lists since I discovered pointers in Turbo Pascal 3, so I know just how frustrating they can be (and how fun :)).

Take the following with a bag of salt. After writing it, I began to wonder how much I can recommend it, but I thought I'd leave it in as food for thought.

One suggestion I have if you use multiple doubly linked lists: put your next and previous pointers in a small link struct:

Code: Select all

struct messy {
    struct {
        struct messy *next;
        struct messy *prev;
    } peer_x;
    struct {
        struct messy *next;
        struct messy *prev;
    } peer_y;
    ....
};
I can't say that I've done this myself, but I did do something similar for the mess of sets connected to my flow nodes.

It would also be possible to have a separate link struct:

Code: Select all

struct link {
    struct link *next;
    struct link *prev;
    void *data;
};

#define link_data(s,l,t) ((t *) (s)->l.data)
#define link_next(s,l,t) ((t*) (s)->l.next->data)
#define link_prev(s,l,t) ((t*) (s)->l.prev->data)
I'm not certain that wouldn't drive me up the wall, though.

I have to admit, I tend to avoid doubly linked lists when I have multiple links like this.
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:Yeah, complex relationship networks grow a lot of hair. Going by some of your other comments, I'm sure you already know this, but this is one place where good variable naming is critical. "next? next what?" :)
Heh, yeah ... It took maybe 2 hours to write this simple function. Which was more time than I had expected, however, previously it had been several different functions and I wanted "single-point" control of linking. Which paid off --- I discovered several minor oversights and omissions when I had several different linking functions. I'll take one function I trust completely over headaches later.

Code: Select all

void Object_Link_After (object_t* afterthis, object_t* me)
{
	if (afterthis)
	{
		me->prevpeer = afterthis;							// (1) Our prev is "afterthis"
		me->nextpeer = me->prevpeer->nextpeer;				// (2) Our next is afterthis's next
		me->prevpeer->nextpeer = me;						// (3) afterthis's next is us
		
		if (me->nextpeer)									// If we have a next peer ...
			me->nextpeer->prevpeer = me;					// (4) that object's previous is US now.
		
		/* we are never assuming head if after something */	// (5) NEVER take head.
		
		if (me->prevpeer == me->parent->children->tail)		// If afterthis was tail ...
			me->parent->children->tail = me;				// (6) Then assume the tail
	}
	
	if (afterthis == NULL)
	{
		me->prevpeer = NULL;								// (1) We are top so have no previous
		me->nextpeer = me->parent->children->head;			// (2) Topmost.  We are taking head.
		/* We don't have a previous peer */					// (3) Don't have a previous peer, so no need to link it to us
		
		if (me->nextpeer)									// If we have a next peer (former head node) ...
			me->nextpeer->prevpeer = me;					// (4) that object's previous is US now.

		me->parent->children->head = me;					// (5) ALWAYS take head.
		
		if (me->parent->children->tail == NULL)				// If tail is empty ...
			me->parent->children->tail = me;				// (6) Then assume the tail.
	}
}
The good news, said function is incredibly re-usable and replaces my previous bad idea of 6-8 different functions doing mostly the same thing. For instance ...

Code: Select all

void Object_Link_Before (object_t* beforethis, object_t* me)
{
	Object_Link_After (beforethis->nextpeer, me);
}

void Object_Link_Last (object_t* me)
{
	Object_Link_After (me->parent->children->tail, me);
}

void Object_Link_First (object_t* me)
{
	Object_Link_After (NULL, me);
}
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 »

taniwha wrote:It would also be possible to have a separate link struct:

Code: Select all

struct link {
    struct link *next;
    struct link *prev;
    void *data;
};
I'm trying to decide whether or not to do that. I'm betting I will since I seem to be on-track to dynamically allocate different object data anyway.

I was at one point hoping to avoid a chain reaction of dynamic allocation, but I guess I'm already there and I have a memory manager that would let me know if anything goes awry.
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