Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional compilation in C and Delphi

The next pattern is common in C code:

#ifndef SOMETHING
#define SOMETHING
#endif

The pattern is possible in Delphi code too:

{$IFNDEF SOMETHING}
{$DEFINE SOMETHING}
{$ENDIF}

but it is not common - I have never seen it at all. If a Delphi code requires a conditional define, it just defines it without IFNDEF check.

Why so? What is the difference in conditional compilation between C and Delphi so that ifndef check is needed for former and not needed for latter?

like image 534
kludg Avatar asked Dec 06 '22 17:12

kludg


2 Answers

That's because this is not only common but mandatory in C:

#include <something.h>

While this is rarely used in Delphi. And when used, it's actually used to set up those {$DEFINE} 's:

{$INCLUDE 'something.inc'}

This matters because DEFINES are only valid while compiling one object (may it be a .PAS file or a .C file). Delphi uses the uses clause to include other units, while C uses the include to include it's headers. In C headers might themselves include other headers. The pattern you're asking about is used to prevent recursively re-including the same header.

To make maters crystal-clear, here's a sample of what one might use in C, and the equivalent in Delphi. Let's say we've got a 3 files setup, where A needs to include both B and C, and B only needs to include C. The "C" files would look like this:

// ----------------------- A.h
#ifndef A
#define A

#include "B.h"
#include "C.h"

// Stuff that goes in A

#endif

// ------------------------ B.h
#ifndef B
#define B

#include "C.h"

// Stuff that goes in B

#endif

// ----------------------- C.h
#ifndef C
#define C

// Stuff that goes in C

#endif

Without the conditional defines in C.h, the C.h file would end up being included twice in A.h. This is how the code would look like in Delphi:

// --------------------- A.pas
unit A;

interface

uses B, C;

implementation

end.

// --------------------- B.pas
unit B

interface

uses C;

implementation

end.

// --------------------- C.pas

unit C

interface

implementation

end.

The Delphi/Pascal version doesn't need to protect "C" from being included twice in "A" because it doesn't use the {$INCLUDE} to achieve this goal, it use the uses statement. The compiler would get the exported symbols from the B.dcu file and the C.dcu files with no risk of including symbols from C.dcu twice.


Other reasons to see a lot more precompiler directives in C code:

  • The precompiler is a lot more powerful then Delphi's. A {$DEFINE} in Delphi code only deals with conditional compilation, while the C variant can be used for both conditional compilation and as a form of word substitution.
  • The mandatory use of #include for headers means you can have a header that defines macros. Or you can have a header that's configured by specifying some #define statements before the actual #include <header.h>
like image 154
Cosmin Prund Avatar answered Dec 19 '22 10:12

Cosmin Prund


This pattern is NOT "common in C" code (.c or .cpp source files). It's common in C/C++ headers (.h) files:

#ifndef SOMETHING
#define SOMETHING
#endif

The reason is to prevent the SAME header from being inadvertantly including MULTIPLE TIMES in the same translation unit.

For example, let's say module "a.c" uses header "b.h". And "b.h" #include's "c.h". That means "a.c" explicitly uses "b", and implicitly also uses "c". So far, so good.

Now let's say that "c.h" uses "b.h". The "#ifndef/#define/#endif" stuff PREVENTS "b" from being #include'd a SECOND TIME (once in "a" by "a", the second time in "a" from "c").

This is all unnecessary in Delphi. Delphi "$ifdef" is used only for condition compilation; Delphi "units" take care of potentially recursive and/or circular dependencies.

like image 38
paulsm4 Avatar answered Dec 19 '22 11:12

paulsm4