Something like C's preprocessor #defines, under user control

I was reading the Swift Programming Language web pages, and looking at the section on conditional compilation. The tests for the #if clauses use hard-coded custom functions, operators, and operands. I noticed that, unlike (Objective-)C(++), there isn't any way for the user to contribute an operand. The user can't define a new flagging identifier in such a way that the conditional-compilation part of Swift can read and incorporate it. What about:

#if defined(MyUserFlag)

where "MyUserFlag" is an identifier added by the user on the command line or IDE equivalent. We could support the variant where the flag isn't just defined, but has a value. There can be:

  • definedAsVoid, for when the flag has no attached value
  • definedAsBool
  • definedAsInt
  • definedAsFloating (?)
  • definedAsVersion, which is a period-separated chain of integers, maybe interpreted as [Int]
  • definedAsString

Then we need a way to extract the value (when it isn't Void). Hmm, maybe we limit definedAs_ and extract(IDENTIFIER) for Boolean #defines, since Bool is the only type currently supported by conditional compilation.

Maybe I’m missing something, but how is this different than what’s already supported with the -D compiler flag (besides the ability to define non-Boolean values)?

Do you have a use case in mind?

Conditional compilation is a can of worms that swift aims to avoid it wherever possible. For example, if an API is missing on a platform, conditional compilation is pretty much the only saviour. But for the general purposes it serves in C (defining constants, extracting repeated code, mimicking generics, etc.), there are much better tools to use in Swift.

2 Likes

I didn't know about the existing capability, since the programming guide doesn't mention it in the conditional-compilation grammar (probably since the guide doesn't reference the compiler or how to use it at all). If you use the compiler flag to add a define, how does the code see it?

Not really, especially since I didn't know the current Swift compiler has something like it already (albeit unofficial relative to the Programming Guide). A naïve look at the guide suggested a point of control that was missing but the user should have. The user should be able to block code from even compiling (besides the minimal parsing) if they want to.

The code-partitioning-under-user-control is the only part of the C preprocessor I'm suggesting to copy, none of the other stuff you said, since those uses already have alternatives.

It's mentioned briefly:

The compilation condition can include the true and false Boolean literals, an identifier used with the -D command line flag, or any of the platform conditions listed in the table below.

I've used it to distinguish between debug and release builds by configuring Xcode to set a flag only in debug mode:

#if DEBUG
    // -DDEBUG flag is set; we're in debug mode
#else
    // flag is not set; we're in release mode
#endif

This is very rarely a good idea. A piece of code with n unique conditional compilation predicates (the predicates used within #if statements) has 2^n possible pre-compilation states, each of which needs to be tested, which gets really crazy really fast.

Also, it breaks the idea that "green means go". Your code may have compiled, but that compilation is no longer parameterized solely on your source file content, but now also the values of the conditional compilation predicates. You need multiple build schemes to build, in order to validate that all possible the configurations of your code actually build successfully.

It's much better to just keep conditional behaviour in the language, rather than hoisting it in the pre-processor. Even things like #if DEBUG should be used very sparingly. The more differences you introduce between your debug and release builds, the more chances you have of finding a bug that exclusively occurs in prod.

Aside from very rare, very tight performance considerations, and the situations where its necessary (as I mentioned earlier, e.g. when an API doesn't exist on a given platform), I've never seen conditional compilation that would be preferable over using polymorphism.

2 Likes