In writing a compiler, I’ve come across a sleek part of the standard c library that I just can’t help and a take a few minutes to write a blog post about it!
You can send any number of arguments to a method / macro that you want. It’s called Variadic methods and they have a crazy syntax. Here is a piece of code that I am using in the compiler. It’s super similar to the printf method (the printf method itself is a variadic method, if you’ve ever wondered…)
char *stringerize(const char *fmt, ...) {
char *buf = safe_malloc(sizeof(char)*BUFSIZE);
va_list args;
va_start(args, fmt);
if (args && strlen(args) > 0)
(void) vsnprintf(buf, BUFSIZE, fmt, args);
va_end(args);
return (buf);
}
Kind uninteresting, huh? I’ll show you a much more interesting one at the end of the post thatallows you to safely concatenate any number of strings.
The basic idea is that you define a method with the … syntax, such as
char *stringerize(const char *fmt, ...)
...
There are two two ways to access those variables.
One by one
First, you declare all your variables that you are going to use in the method. That has to be before accessing the list. Then, you start the list with
va_list args;
Make sure you don’t forget to declare the args as a list otherwise C will throw a fit.
Then, when you are ready to get the variables, call
va_start(args, fmt);
The second parameter is the last required argument in the command. You do need at least one required argument in a function. The same requirement does not hold true for macros, though.
Then, to get to the variable you call
va_arg(args, const char *);
The second parameter is the type you are expecting to come back. This gives C a heads up as to what to expect.
Then, when you are done and want to move on to the next one in the list, simply call:
va_end(args);
That’s it!
Accessing the variables all at once
You can either do this in a macro that would look something like (untested):
#define VARGS(...) fprintf(stderr, __VA_ARGS__)
// or
#define VARGS(fmt, args...) fprintf(stderr, fmt, args)
There are other ways as well, but those are the ones that stood out to me. In a method, you can do this as I had done in the example above. Just call list and all the arguments are accessible to you, right there in your shirt pocket!
And now, finally for the more important snippet that:
char *concat_commands(const char* str,...) {
va_list args; char *s; size_t held_memory = 100;
char *res = safe_malloc(sizeof(char)*held_memory);
if (res != NULL) {
char *new_pointer, *word_pointer;
va_start(args, str);
word_pointer = res;
for (s=str;s!=NULL;s=va_arg(args, const char *)) {
size_t len = strlen(s);
if (word_pointer + len + 1 > res + held_memory) {
held_memory = (held_memory + len) * 2;
new_pointer = (char *) realloc (res, held_memory);
if (new_pointer == NULL) { free(res); return NULL; }
word_pointer = new_pointer + (word_pointer - res);
res = new_pointer;
}
word_pointer = memcpy(word_pointer,s,len);
word_pointer = word_pointer + len;
}
*word_pointer++ = ' ';
new_pointer = (char *) realloc(res, word_pointer-res);
if (new_pointer != NULL) {res = new_pointer;}
va_end(args);
}
return res;
}
I hope this helps someone out there save a few hours and remember how cool C even in the midst of the ruby we all love so much. On that note, I urge you to look at the ruby interpreter source code. It’s a tad jumbled up, my guess is because there are a few people working on it, but it is sure damn sexy code. Thanks once again matz!
I’ll be back more in a week or so for more fervent posts after my compiler is done.
0 comments ↓
There are no comments yet...Kick things off by filling out the form below.
Leave a Comment