C is one of the most popular languages, especially for security-related applications including server programs, cryptography implementations and much more. Paradoxically it is also one of the most dangerous languages, because there are many spots where mistakes can be made, some of the most common being to forget to free allocated memory or to free it twice, to exceed a buffer (colloquially called a buffer overflow), to dereference a NULL
pointer and more.
Some of you may have read my article about functional concepts in C, which I have written for fun. That article actually uses a non-standard extension to implement them. I have had a long thought about it and found that some of the concepts can be implemented without extensions easily and that they can be very useful in practice for real C code.
My focus was on continuations. They would immediately solve the problem of freeing memory and closing file handles properly in many situations. The idea is so simple and obvious that I just show you some example code of how it works:
int withFile(const char *fileName,
const char *mode,
int (*user)(FILE *))
{
FILE *fh;
int err;
fh = fopen(fileName, mode);
if (fh == NULL) return 1;
err = user(fh);
fclose(fh);
return err;
}
The big advantage of this approach is that the responsibility for closing the file handle is moved from the programmer to the withFile
function. In that sense withFile
acts like a little run-time system (RTS) for the file handle. This also makes code much simpler. If you remember Linus Torvalds’ note that goto is not necessarily evil and can make writing cleanup code much easier and more beautiful, you will find the above approach even nicer, because it gets along without goto, but does essentially the same.
Of course the above version of withFile
is made for a single file handle. You can write versions of it for more than one or even arbitrarily many file handles. You can also make it allocate a working buffer for you and much more.
This style of implementing managed handles through a mini-RTS can also be applied to temporarily allocated strings and buffers, which particularly suffer from buffer overflow errors, double frees, dereferencing of NULL
pointers, etc. The RTS guarantees that no code ever receives a NULL
pointer or an invalid handle, it helps against double frees and forgetting to free memory, because this responsibility is moved out of mainstream code. Of course this works best, if you don’t use global variables or use them sparingly.
Unfortunately C does not support closures. If it did, then you could easily pass handler functions to the user function, which obey certain rules for you like buffer boundaries. For certain kinds of managed handles you could even pass handler functions only, such that no handle is used at all. This is impossible in C, but can be very useful in languages with closures like Python or functional languages.
Comments
Anonymous:
ertes: