The use of assert() ensures no code is emitted in RELEASE mode, (edit) and that the code doesn't depend on the DEBUG conditional which may or may not be defined for various reasons.
The next iteration is to support variadic arguments along with separator and terminator (though I rarely use them if at all, but for the sake of completeness of implementation):
func dprint(_ items: Any..., separator: String = " ", terminator: String = "\n") {
assert({
if let first = items.first {
print(first, terminator: "")
for item in items.dropFirst() {
print(separator, item, separator: "", terminator: "")
}
}
print(terminator, terminator: "")
return true
}())
}
Seems to work fine but looks like an ugly overkill for a problem like this. Plus, it probably shouldn't be @inlinable any more which means code will be emitted in RELEASE mode.
Can this be rewritten with macros to make it look simpler and more elegant?
I'm willing to sacrifice separator and terminator but still support variadic arguments, and zero-code in release mode is important.
You could perhaps copy the internal implementation of print into the #if block, although interestingly enough the locking is not publicly exposed.
I would be really surprised if #if DEBUG gets ignored in external modules; I'd expect packages to fully support eliminating such blocks in release builds, although I'm no expert.
Even ignoring the atomicity of one print call, which is probably not critical for debug-only printing, it's looking way too complex to implement from scratch, just check out the module that does actual printing of Any. Increasingly looking like a need for a whole framework (just for debug printing!) which honestly I'm not willing to dedicate my time to.
Also, I edited my post to include an explanation why assert() and not #if DEBUG: the latter is not guaranteed to be defined in debug builds.
To approach this from a different POV… what if you defined a com.yourcompany.yourproduct.FeatureDebug argument that can be passed in at launch (and then wrap your print statements inside a check for that flag)? There are pros and cons to both approaches… but would that be a legit option for you?
I honestly don't see what advantages that has vs. ensuring there's always DEBUG for debug builds. Also the usage of assert is not the problem here, at least it frees you from depending on conditionals and works fine.
What's truly lacking in Swift is the ability to pass varargs around as a whole.
This gives me the error Cannot pass value pack expansion to non-pack parameter of type 'Any', which implies that you can't use pack expansion to satisfy a variadic parameter. Hopefully that can be changed so these two seemingly related features can work together.
Yep, that's actually a pretty simple solution with a good chance of being inlined! I'd still stick to assert instead of #if DEBUG but that's not important.