Table Of Contents:
- #defined function-names after function-definitions (confusing)
- DO NOT TRUST: that there will be warnings/errors for uninitialized variables!!!
- (gnu make) Makefile Recursive inclusion follies (6-17-15)
- (gnu make) makefile auto-creation follies (6-17-15)
- (xc32-gcc, plausibly others?) escaping command-line quotes, etc. (7-14-15)
- function-like macros with/without return-values (7-16-15)
- TODOs and Qs (Latest: 1-29-16)
- Code-Size reduction ideas... check out #limited-code hacks/tips (added 11-26-16)
- I HAVE NOT been keeping this ToC up to date... check out the logs!
OTHER REFERENCES:
- computed-gotos, more efficient state-machines than using switch() ( @Analog Two's #reprint: modern printf )
1. #defined function-names after function-definitions
Thanks to @LazyHD for the reminder: This is generally considered BAD PRACTICE. But there may be times when you'll come across it's having-been-done (and most-likely by mistake!) and then it will be quite confusing.
(See his comment, below. But, briefly, #define's should be CAPITALIZED to avoid such confusion.)
#include <stdio.h>
//Uncomment one of the '#define function' lines to see what I'm sayin'
//Uncommenting this one results in an error...
//#define function() printf("running '#define function()'\n")
//And of course, disabling both #defines, results in this being called.,,
void function(void)
{
printf("running actual 'void function(void)'\n");
}
//Uncommenting this one results in no errors/warnings
// and *this* is called, not the actual function.
#define function() printf("running '#define function()'\n")
int main(void)
{
function();
}
This can be royally confusing... and SHOULD NOT BE DONE INTENTIONALLY.
Say you've a function between 'void function(void)' and the second '#define function()' which happens to call function()... it'll call the actual function, but main will call the #define... right? I dunno, it's risky-business. DON'T DO THIS. More-complicated programs, however, might just cause something like this, most-likely by mistake, e.g. through #if statements...
2. DO NOT TRUST: that there will be warnings/errors for uninitialized variables!!!
GCC: This one has bit me in the butt so many times...
int function(int b)
{
int a;
if(b==3)
a=3;
return a;
}
CLEARLY: a will only be assigned a value IF b==3!CLEARLY: "-Wuninitialized" and "-Werror=uninitialized" is designed *exactly* for the purpose of noting when a variable like int a; could be used without being assigned a value! GCC WILL NOT catch this case... at least with the versions I've used. You WILL NOT receive a warning, nor error, in this case! And, allegedly, there's no intention to fix this glitch.
There's a good discussion on it HERE.
3. (gnu make) Makefile Recursive Inclusion Follies
(initially poorly-titled: 'include $(INCLUDABLES)' wherein those 'INCLUDABLES' modify the variable 'INCLUDABLES')
Say you have a bunch of makefile 'snippets' you'd like included in a makefile.
makefile:
INCLUDABLES += inc1.mk
INCLUDABLES += inc2.mk
include $(INCLUDABLES)
INCLUDABLES += inc1a.mk
The end-result appears clear, at least on my system...
My search-fu is failing me. is this result expected/able to go cross-platform? cross-makes?
The end-result, on my system, is that apparently the call to 'include $(INCLUDABLES)' works with the variable 'INCLUDABLES' AS IT EXISTED when the 'call was placed'
So, inc1a.mk is never included.
(This goes against my [utter-lack-of-]understanding of make's "multipass technique", but goes completely in favor of my having an utter-lack-of-understanding of it, in the first place ;)
Thoughts?
4. (gnu make) makefile auto-creation follies
It's possible to have 'make' auto-create files it wishes to include...
e.g.
makefile:
include includeMe.mk
...if the file 'ncludeMe.mk' doesn't exist, 'make' can try to auto-create it based on a rule...
makefile continued:
# Rules:
#Default ('make')
all:
@echo "'make [all]'"
#Missing makefile snippet:
%.mk:
@echo "creating missing makefile snippet"
@touch $@"
Cool.
Now... what if you have a list of "makefile snippets" that don't yet exist and need to be auto-created?
INCLUDABLES += inc1.mk
INCLUDABLES += inc2.mk
include $(INCLUDABLES)
all:
@echo "'make [all]'"
%.mk:
@echo "$@ not found, auto-creating it..."
@touch $@
Now it gets interesting... apparently, on my system, they're created in reverse-order. Does it matter? Not here, but somewhere more complex it might.
If nothing else... combined with the previous "make follies", this led to some confusion... Does 'include' actually *include the files in reverse order*?
Took a bit of experimenting, but no, it seems to include them in the right order, it just *creates* them in reverse-order. Weird.
5. (xc32-gcc, plausibly others?) escaping command-line quotes, etc.Some gcc compilations (precompiled, cross-compiled?) may require additional 'escaping' of things like quotes, when used on the command-line. E.G.gcc -D'thing="thingString"' might have to be called as
gcc -Dthing=\\\"thingString\\\"
In the case I encountered, this is *not* the result of the way bash passes the argument to gcc, but actually something this particular compilation/configuration of gcc is doing...
Keep your eyes peeled!
6. Function-like macros with/without return-values (7-16-15)
Say you have the functions:
int withRetVal(int arg)
{
return arg+1;
}
void withoutRetVal(int arg)
{
<do something>;
}
Maybe there's some reason you'd prefer to put these in a macro via #define... e.g.:#define withRetVal(arg) ((arg)+1)
#define withoutRetVal(arg) <doSomething>
FIRST, NOTE: USUALLY a #defined macro should be CAPITALIZED (see #1, above!)Those are generally fine, as-is...
Now, what if you've got two-liners...?
We'll do these one-at-a-time, here...
#define withoutRetVal(pin) \
setOutput(pin); \
turnOn(pin)
Sure, that sorta works...When 'withoutRetVal(pin);' is replaced, it will be replaced with 'setOutput(pin); turnOn(pin);'
Note, the semicolon after withoutRetVal(pin); doesn't get replaced, it gets tagged-on after turnOn(pin)... Seems to work, right?
Well, there's several problems with this...
It looks like it could be used in an if-statement:
if (whatever)
withoutRetVal(pin);
else
<somethingElse>;
But, really, you've got:
if (whatever)
setOut(pin);
setOn(pin);
else
<somethingElse>;
And, then, you're screwed, 'cause that else doesn't go with anything... (It can certainly get uglier than merely that, imagine nested-ifs!).
So, then, add "squiggles," right?
#define withoutRetVal(pin) \
{ \
setOutput(pin); \
turnOn(pin); \
}
Done and done... RIGHT?
Sure. Done. (For now...)
Now let's consider the withRetVal case (again, multi-line):
int getPinVal(int pin)
{
setInput(pin);
return getInput(pin);
}
Could be written as a macro...
#define getPinVal(pin) \
{ \
setInput(pin); \
getInput(pin); \
}
Handy-thing... the value "returned" by these curly-brackets is the return-value from getInput(); Handy...
Done and done... right?
Except, what if you want to use this in an if-statement...
if(getPinVal(pin))
<do something>
Then you haveif( { setInput(pin); getInput(pin); } )
Hmm... Is that legal?Actually, I think I bit-off more than I could chew in starting to write this, I should probably test these things, again, before acting like some sort of authority. Fact-is, I'm no authority, I just figured this out the hard-way. And I've developed habits and seem to have forgotten some of the reasoning.
UPDATE: Found this:
"The `({ ... })' notation produces a compound statement that acts
as an expression. Its value is the value of its last statement."
--info cpp 3.10.4
Fact-is, there're a few more "handy" things, here and there...
For instance, say you *don't* want a return-value... but the last function-call in the macro *has* a return-value... Then you can do something like:
void doSomething(void)
{
something1WithRetVal();
something2WithRetVal();
}
--->
#define doSomething() \
{ \
something1WithRetVal(); \
something2WithRetVal(); \
{}; \
}
The key being: Note the curly-brackets containing NOTHING at the end of the #define. This makes the doSomething() macro have no return-value, since {}; has no return-value.Then, there's something with Parentheses... I can't recall the details off-hand (did I mention I bit off more than I could chew in writing this?)
Basically the above looks alright, again, right? But for some reason I've forgotten, I needed parentheses around those outter curly-braces...
The end-result is basically: If you want to simulate a void-function via a macro, it WORKS (in all cases I've encountered) when written like this:
#define doSomething() \
({ \
something1WithRetVal(); \
something2WithRetVal(); \
{}; \
})
(Note, again, the surrounding parentheses... why was that again?)
And, if you want a non-void function-like macro, the same-goes, as I recall... just remove that {}; and it will have the same return-type as the last function called (or variable listed?)...
So, basically, habitually: I just throw in ({ <normal c-syntax function-code, with line-continuators '\'> {}; }) and it looks pretty much like a normal C function...
There are other ways to accomplish the same... 'info gcc' has a whole section on it, but the end-result is kinda ugly, as I recall... something about surrounding it with a do{ <something> }while(0) (and parens?) as I recall...
(I really should rewrite this, and rerun those experiments, and maybe even look at the notes/links I wrote in those experiments)...
It looks like I started using the ({ ... {}; }) syntax somewhere 'round 2008, so there're probably well-over a couple dozen projects that've used it this way, but ALL BASED ON various versions of GCC, so YMMV. And... I don't have a "withRetVal" case in front of me, so I'm not 100% on the part about "just remove the {};"
7. TODOs and Qs (Latest 1-29-16)
- SERIOUSLY: is there no such thing as something like int_native_t and uint_native_t?
- e.g. all I need is a few values, maybe -1, 0, and 1... Would be smartest to have it in the processor's NATIVE register-size, rather'n e.g. 'int' which is guaranteed to be at least 16-bits (right?) despite maybe being on an 8-bit processor...
- Optimization *might* recognize this in *many* cases, but I bet not when various functions are called from various files with this 'type' as an argument...
- Besides, it would make things more clear...
- And using int8_t isn't probably good 'nough since in e.g. a 32 bit processor, that'd require *more* instructions to extract, right...? (optimizer *might* be smart enough here...?)