The C Preprocessor
For such a small project it might be okay that we have to program differently depending on what operating system we are using, but if I want to send my source code to a friend on different operating system to give me a hand with the programming, it is going to cause problems. In an ideal world I’d wish for my source code to be able to compile no matter where, or on what computer, it is being compiled. This is a general problem in C, and it is called portability. There is not always an easy or correct solution.
Octopus • Sort of like Octothorpe
But C does provide a mechanism to help, called the preprocessor.
The preprocessor is a program that runs before the compiler. It has a number of purposes, and we’ve been actually using it already without knowing. Any line that starts with a octothorpe #
character (hash to you and me) is a preprocessor command. We’ve been using it to include header files, giving us access to functions from the standard library and others.
Another use of the preprocessor is to detect which operating system the code is being compiled on, and to use this to emit different code.
This is exactly how we are going to use it. If we are running Windows we’re going to let the preprocessor emit code with some fake readline
and add_history
functions I’ve prepared, otherwise we are going to include the headers from editline
and use these.
To declare what code the compiler should emit we can wrap it in #ifdef
, #else
, and #endif
preprocessor statements. These are like an if
function that happens before the code is compiled. All the contents of the file from the first #ifdef
to the next #else
are used if the condition is true, otherwise all the contents from the #else
to the final #endif
are used instead. By putting these around our fake functions, and our editline headers, the code that is emitted should compile on Windows, Linux or Mac.
#include <stdio.h>
#include <stdlib.h>
/* If we are compiling on Windows compile these functions */
#ifdef _WIN32
#include <string.h>
static char buffer[2048];
/* Fake readline function */
char* readline(char* prompt) {
fputs(prompt, stdout);
fgets(buffer, 2048, stdin);
char* cpy = malloc(strlen(buffer)+1);
strcpy(cpy, buffer);
cpy[strlen(cpy)-1] = '\0';
return cpy;
}
/* Fake add_history function */
void add_history(char* unused) {}
/* Otherwise include the editline headers */
#else
#include <editline/readline.h>
#include <editline/history.h>
#endif
int main(int argc, char** argv) {
puts("Lispy Version 0.0.0.0.1");
puts("Press Ctrl+c to Exit\n");
while (1) {
/* Now in either case readline will be correctly defined */
char* input = readline("lispy> ");
add_history(input);
printf("No you're a %s\n", input);
free(input);
}
return 0;
}