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

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.

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. /* If we are compiling on Windows compile these functions */
  4. #ifdef _WIN32
  5. #include <string.h>
  6. static char buffer[2048];
  7. /* Fake readline function */
  8. char* readline(char* prompt) {
  9. fputs(prompt, stdout);
  10. fgets(buffer, 2048, stdin);
  11. char* cpy = malloc(strlen(buffer)+1);
  12. strcpy(cpy, buffer);
  13. cpy[strlen(cpy)-1] = '\0';
  14. return cpy;
  15. }
  16. /* Fake add_history function */
  17. void add_history(char* unused) {}
  18. /* Otherwise include the editline headers */
  19. #else
  20. #include <editline/readline.h>
  21. #include <editline/history.h>
  22. #endif
  23. int main(int argc, char** argv) {
  24. puts("Lispy Version 0.0.0.0.1");
  25. puts("Press Ctrl+c to Exit\n");
  26. while (1) {
  27. /* Now in either case readline will be correctly defined */
  28. char* input = readline("lispy> ");
  29. add_history(input);
  30. printf("No you're a %s\n", input);
  31. free(input);
  32. }
  33. return 0;
  34. }