3

Using #if, #endif in Swift (using Xcode) produces errors if it cuts into the flow of an operation. This screenshot says it all:

Swift #if, #endif error

Does anyone know a solution to make this example work, without repeating the entire code block twice? There can easily be situations where the entire block can be very large.

EDIT: My sample was a bit too simple. Here is a new sample where the "else if" depends on the same define (DEBUG). The "else if" must also be within the #if and #endif. And other samples can be much more complex than this.

enter image description here

msedore
  • 91
  • 2
  • 10
  • 1
    Unlike of `C`/`Objective C` `#` keywords are parsed not by `pre-processor`, but by the same parser as the rest of the `swift` code. So they must follow same syntactic rules and can't just cut into code. – user28434'mstep Mar 12 '19 at 15:43
  • Post code instead of screenshots. – Dharmesh Kheni Mar 12 '19 at 15:50
  • I hear what you're saying user28434. It is a real limitation. I'm hoping someone has a way to make it work without repeating a large block of code. – msedore Mar 12 '19 at 15:53
  • Dharmesh, I used a screenshot so that it would also show the errors displayed by Xcode. I could have stated the errors, but sometimes a picture just makes things more clear. – msedore Mar 12 '19 at 15:55

3 Answers3

5

Ideally, limit the usage of #if as much as possible. Using preprocessor directives is always a bit of a code smell. In this case, you can simply use a boolean variable:

#if DEBUG
let debug = true
#else
let debug = false
#endif

Then simply use the variable:

var a = 0
var b = 0

...

else if debug && a == b {
}

In release mode the code will become unreachable and the optimizer will remove it anyway.

With a bit of imagination, we can find other solutions, for example, we can move the check to a function:

func isDebugCheck(a: Int, b: Int) -> Bool {
   #if DEBUG
      return a == b
   #else
      return false
   #endif
}

or we can move the whole code to a separate function and replace if-else by a return (or continue, depending on you needs), e.g.:

if a == 7 {
  ...
  return
}

#if DEBUG
  if a == b {
     return
  }
#endif

if ...
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • 1
    Thanks Sulthan. I was using a solution something like this. It just seems silly to set up the result of an "if else" before execution actually reaches the "if else". But it looks like there is no other way. – msedore Mar 12 '19 at 16:11
  • @msedore You can move the ifs to a `function` and replace `if-else` by an `if-return-else`. Or just move the check to a function `func isDebugCheck(a, b) { #if DEBUG return a == b #else return false }`. – Sulthan Mar 12 '19 at 16:19
  • Excellent idea! – msedore Mar 12 '19 at 16:20
2

As @user28434 notes, there is no source-level pre-processor. This has gotten rid of a lot of very tricky pre-processor problems in C (such as bizarre needs for parentheses to make things work).

However, #if is integrated well into the language, and specifically supports switch for exactly these kinds of cases.

var a = 0

#if DEBUG
let b = 0
#endif

switch a {
case 7: a += 1
    #if DEBUG
case b: a += 2
    #endif
case 5: a += 3
default:
    break
}
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 1
    Thanks, but a switch statement only works because my sample is simple. As I've been trying to say, real-world samples can be a lot more complex, not just comparing a single integer. – msedore Mar 12 '19 at 16:06
1

You can simply achieve this case with below code:

if a == b {
    #if DEBUG
    a += 2
    #else
    a += 1
    #endif
} else if a == c {
    a += 3
}
Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
  • Thanks for the response. However, that solution won't work if the conditions in the "else if" statement also depend on the same define (DEBUG). My sample was a bit too simple. – msedore Mar 12 '19 at 15:44