3

In C, to make sure we don't re-include headers that are included we use the following structure:

#ifndef UTILS
#define UTILS

#include "my_utils.h"

#endif

I've broken my Lisp program into separate files; multiple files sometimes use the same file (e.g., my_utilities is used by multiple files). When I run the program, I get warnings that I am redefining things (calling load of the same file multiple times).

This would be fixed by doing something similar to #ifndef in C. What is the Common Lisp equivalent or standard method of doing this?

I am fairly new to Lisp. Let me know if there are best practices (perhaps, a different method of structuring my programs?) that I am missing.

audrow
  • 143
  • 1
  • 9
  • 2
    You should use [asdf](https://stackoverflow.com/documentation/common-lisp/670/asdf-another-system-definition-facility#t=20160727160132848467) to define a system. – jkiiski Jul 27 '16 at 16:02
  • That's what I was looking for. Thank you. – audrow Jul 27 '16 at 16:13

2 Answers2

7

The question you asked

The direct analogue of preprocessor conditions like #if in C is the #+ read-time conditionalization facility.

The question you wanted to ask

To avoid multiple loading of a file, you can either use the standard (but deprecated) provide/require facility, or an add-on system like ASDF.

sds
  • 58,617
  • 29
  • 161
  • 278
6
  1. For Common Lisp applications and libraries it is preferred to use a system management tool. Like ASDF or whatever your implementation may provide. A system is a collection of files with dependencies and various actions (load, compile, ...).

  2. You can always check the state of the runtime and do something.

Example:

(unless (fboundp 'foobar)
  (require "foo")
  (load "bar"))

(unless (find-package 'foobar)
  (require "foo")
  (load "bar"))
  1. PROVIDE and REQUIRE are built-in functions for exactly that. If you require a module it will be loaded, unless already provided. Unfortunately this functionality is underspecified in the standard, but implementations may provide useful functionality.

  2. Common Lisp runtimes have a list of features on the list *features*. You can use that to advertise and check functionality.

Example:

In your library:

(push :cool-new-graphics-library cl:*features*)

In your application code:

(when (member :cool-new-graphics-library cl:*features*)
  (funcall (find-symbol "DRAW-SPACE-SHIP" "CNGL")
           :death-star))

Common Lisp provides a way to conditionalize that a read time. The following code will only be read when the :cool-new-graphics-library feature is present, and thus it only then will be executed later:

#+cool-new-graphics-library(cngl:draw-space-ship :death-star)

Common Lisp also allows you to express some logic:

#+(and lispworks cool-new-graphics-library)
  (cngl:draw-space-ship :enterprise)

#-cool-new-graphics-library(warn "no cool graphics library available")

Note that you can force Lisp to execute code at compile-time:

(eval-when (:load-toplevel :compile-toplevel :execute)
  #+(and lispworks cool-new-graphics-library)
    (cngl:draw-space-ship :enterprise)

  #-cool-new-graphics-library(warn "no cool graphics library available")
 )

For this to work the EVAL-WHEN has to be at the toplevel in a file. That means it will not work deep in nested forms. It does work inside a toplevel PROGN,LOCALLY, MACROLET and SYMBOL-MACROLET, though.

Thus EVAL-WHEN allows you to run code which is part of the file which is currently compiled. This code than can look for loaded systems, provided modules, available functions, and more.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346