[swift-dev] C Macros and Variadic functions
jordan_rose at apple.com
Wed Jan 13 12:07:51 CST 2016
Hi, all. I'll try to shed some light on both macros and variadic functions.
Macros are the easy ones. Obviously, a macro like this can't be imported as a function:
#define UNIQUE_NAME(prefix) prefix##__COUNTER__
But here's a trickier one:
#define NAME(windowStruct) windowStruct.attrs->name
In this case, there's probably a pair of structs in mind, one with an 'attrs' field, and one with a 'name' field. But the compiler can't know that just from the macro. In fact, the macro may be intended for use over many structs, all with an 'attrs' field (much like C++ templates). Swift can't express this.
We'd definitely be glad to improve our handling of macros that are unambiguous, but importing all macros is not a goal we can ever reach. In most cases, the best workaround is to write a static inline C function that just calls through to the macro.
Okay, C variadics. The main problem with C variadics is that they're antithetical to Swift's idea of memory safety. I mean, sure, calling any C function means you're stepping outside Swift's safety, but at least the call is well-typed, and if you're trafficking in UnsafePointer, the function says so (and you probably had to make or get one at the call site). With a variadic function, you can pass total garbage and not be aware of it, and the function will just crash.
A practical problem with C variadics is that they're at odds with Swift's variadics model. Swift variadics, like C# or Java, specify a particular type for all of the variadic arguments—they're all Strings, or Ints, or AnyObjects. C variadics, on the other hand, allow a mix of types, and they're all passed differently. Now, we can certainly do this—we do have Clang, after all—but it's Yet Another Difference between Swift and C that people might have to think about. (For variadics with null sentinels <https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007bsentinel_007d-function-attribute-3219>, does the compiler insert the sentinel? Does it check whether one of the values is nil? What if it's alternating value-key pairs <https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/index.html#//apple_ref/occ/instm/NSDictionary/initWithObjectsAndKeys:>?)
That's mostly it. They're even less safe than the rest of C, and they make things more complicated in Swift. As of Swift 2.2 we do at least import them as unavailable instead of missing entirely, so you get a reasonable error message (as Ryan noticed). It's a lot easier to do that than to actually implement it, though; we basically fake it with a Swift variadic function that has the same signature.
We do import the 'va_list' type, and there's a helper in the standard library, 'withVaList', to safely build a va_list that you can pass to a C function. That's mostly intended for providing a Swift variadic interface that calls through to the underlying implementation. (It's very platform-specific code.) If a function doesn't have a va_list variant, however, Joe's right that the only correct workaround today is, again, a static inline C function. (If you look at the particular pull requests Ryan called out, you'll see they all shim to a C function to do exactly this.)
I hope this clears up some of the issues in both areas. They're not happy answers, and like everything there's probably room for improvement, but they are areas with enough complexity that we'd need to be careful about changing them.
> On Jan 6, 2016, at 16:28 , Thomas Catterall via swift-dev <swift-dev at swift.org> wrote:
> It seems to me that an interesting tool in the swift toolchain would be one that could generate implementations of methods in circumstances like this, using, for instance, CPP macros as hints for function prototypes. Then again, I might be entirely wrong and I'd be happy to be corrected!
> Sent from my iPhone
> On 5 Jan 2016, at 16:57, Kate Stone <katherine_stone at apple.com <mailto:katherine_stone at apple.com>> wrote:
>>> On Jan 5, 2016, at 12:32 PM, Ryan Lovelett via swift-dev <swift-dev at swift.org <mailto:swift-dev at swift.org>> wrote:
>>> Just to be clear though the intent of my question was not to quibble
>>> with compiler error messages. My real question is how are we meant to do
>>> systems programming with Swift on Linux if we cannot call ioctl?
>> In the absence of an automatic mechanism for importing the definition of variadic functions you can still define your own prototypes and bind them to the system implementation. For example, this declaration:
>> @_silgen_name("ioctl") func ioctl(fildes: CInt, request: UInt64, result: UnsafePointer<Int>) -> Int
>> … gives you a non-variadic interface to ioctl that you can use for invocations that conform to this specific convention. You can define as many overloads as you wish, and so long as you’re cautious about which one you’re using for a given request you should be able to make progress.
>> The same basic strategy can be applied to any variadic functional interfaces. Ideally you’d want to hide this implementation detail behind a more Swift-friendly API where the request type is implied to create a more type-safe interface.
>> Kate Stone k8stone at apple.com <mailto:k8stone at apple.com>
>> Xcode Low Level Tools
> swift-dev mailing list
> swift-dev at swift.org <mailto:swift-dev at swift.org>
> https://lists.swift.org/mailman/listinfo/swift-dev <https://lists.swift.org/mailman/listinfo/swift-dev>
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the swift-dev