Posts Tagged ‘c’
C programming hints and code-style (part 1)
Over the years I’ve had to deal with many different coding styles (can something without style be called style?). Here is a list of no-brainer tips that every C programmer should be aware of.
Hints
Update:
Have a copy of the C99 spec handy
Don’t scratch your head when you are not sure if a function is part of the standard, and what is the expected behavior, open the spec an check by yourself: here.
free() can receive NULL
There’s no need to do:
if (pointer)
free(pointer);
This works fine:
free(pointer);
Therefore if you write a struc_free function it makes sense to allow NULL as an argument.
There’s no need to cast a void *
tmp = (EXTEREMELY_UGLY_TYPE *) malloc(10);
malloc() returns a void * there’s no need to cast those:
tmp = malloc(10);
sizeof can receive a variable
size = sizeof(EXTEREMELY_UGLY_TYPE);
No need to burden yourself with that:
size = sizeof(tmp);
If tmp is a pointer you can do sizeof(*tmp) and so on.
Avoid big macros
Have you seen horrendous code like this?
#define ERROR(e, code, text) \
G_STMT_START { \
if ((text)) \
GST_WARNING_OBJECT ((e), "error: %s", (text)); \
gst_element_message (GST_ELEMENT((e)), GST_MESSAGE_ERROR, \
GST_STREAM_ERROR, (code)); \
} G_STMT_END
It’s much cleaner to use an inline function:
static inline void send_error(GstElement *e, int code, const char *text)
{
if (text)
GST_WARNING_OBJECT(e, "error: %s", text);
gst_element_message(e, GST_MESSAGE_ERROR, GST_STREAM_ERROR, code);
}
Yes, you can put inline functions in header files.
C99 is your friend
C99 has nice types such as bool (true and false), uint32_t and similar int types. Also these beauties:
static struct device = {
.base = 0x480bd400,
.irq = 24,
.pdata = {
.name = "isp",
.nr_tlb_entries = 8,
.clk_name = "cam_ick",
},
};
I don’t even want to imagine how horrible the code would look for C89.
Those are the ones I can think about, any suggestions? On the next part I’ll about code-style
Continuations in C; easy asynchronous stuff
If you have worked with asynchronous stuff, you surely must have reached a point where you need to save your current context, in order to continue the computation after you’ve received some data asynchronously.
For example, let’s say you want to upload a file to a web service, then you need to wait for some kind of id, and then you can add tags to identify it.
The simplest way to do it would be to just block until you receive the id, and then tag it. That is easy, but in the meantime nothing else can be done, and if you have a UI you’ll just see an unresponsive application.
Another approach is to create a new thread, that way other stuff can keep running while your thread waits for the id. The problem here is that threads are not exactly easy to handle, and some UI’s aren’t particularity good at handling threads. Another problem is that threads are expensive, if you have many threads being created and destroyed rapidly you are probably wasting resources.
Then if you are not lazy, you might want to save the context in a separate structure, write a separate a callback to handle the response, and the continue the processing. That is not too difficult, there are no performance penalties and should play nicely with every other code you might be running. Unfortunately it’s tedious, and if you have many contexts to store, it becomes painful.
So, here comes continuations the rescue. The concept is really simple, you reach a point in your code where don’t have anything else to compute, so you yield to allow other computations to continue, and when the relevant data is ready, the context is resumed and your code continues the execution as if nothing happened.
file.send
yield id = file.get_id # wait, but let other stuff to happen
tags.send(id)
It sound too good to be truth, and it is. Most languages don’t have support for this continuations, so you end up with ugly workarounds in order to make them work.
Fortunately there are some C primitives you can use for continuations. Wikipedia has a nice list of alternatives.
So I implemented something very basic based on Marc Lehmann’s libcoro, which makes this code possible:
struct task *test_task (const char *str) {
struct task *t;
void helper (struct task *t) {
const char *tmp = str;
printf ("%s: test: tmp=%s\n", __func__, tmp);
task_yield (t);
printf ("%s: after yield: tmp=%s\n", __func__, tmp);
task_complete (t);
}
t = task_new (helper);
return t;
}
The output goes like this:
main: begin
helper: test: tmp=hello world!
main: foo
helper: after yield: tmp=hello world!
main: end
I still haven’t used this for anything on real-world scenarios, but looks like an interesting idea to explore.