I have been using this annotation for a couple of years.
@_cdecl("LIBHelloWorld")
func libHelloWorld() {
World.hello()
}
private class World {
static func hello() {
print("Hello World!")
}
}
Passing parameters also works well.
Following this forum thread, it's not clear to me the status of this proposal, or if I can help out.
In my daily workflow, this code pattern is seen again and again.
I am worried that @_cdecl could be removed from the language without warning. Is that possible, or would someone reply here before doing that?
Yes, the underscore means the annotation is unofficial.
I will rephrase the question.
What would give me a heads up that @_cdecl is being removed from the language? I assume that subscribing to this thread is the best thing, but maybe there is something else.
Hi Joe, a couple of times in this thread you speak about "importing a header". Is there a way to do this that doesn't involve creating a clang modulemap which imports the header? Because that works, but it always feels like jumping through hoops â creating multiple files, targets, build rules, to expose just one function, whose body should be defined in Swift anyway.
We are experiencing sporadic crashes calling functions exposed to the JNI via @_cdecl and we're looking for a "more official" way of doing it.
If you were to expose a function to C today in a non-broken way, how would you do it?
Create thing.h which declares (in C syntax) that MyFunction exists
Create a module map that imports thing.h
Somehow import that module (SwiftPM, Xcode Build Settings, etc.)
In the Swift code, something something (???) â how do we declare the function in Swift, and if we do, what was the point of importing the header?
I don't mean to derail the conversation with this. I am genuinely interested in finding out whether a use-case that I still somehow consider essential to this thread's (pre-)proposal can be achieved otherwise with reasonable effort.
If you're defining the functions in Swift, then @_cdecl should be fine. If it's crashing, then there's either a bug in the compiler, or a calling convention mismatch between what Swift and LLVM are doing and what the JNI expects. You would use headers and modulemaps to declare external C functions to be imported into Swift.
Bumping this thread and wondering if any new decisions have been made on the matter, as well as the viability of continuing to use @_cdecl still without fear of it not being supported in a future release. And if so, how much of an impact would that be (other than refactoring the symbol to @cdecl or something similar)
The implementation is, to the best of my knowledge, functionally complete, so the thing we'd need to advance @_cdecl to an official feature is an evolution proposal. We generally keep existing underscored attribute spellings around for a while for source compatibility, and I don't foresee any massively breaking changes we'd make to an official feature that'd make maintaining the existing behavior for compatibility difficult. The list of issues from Jordan's OP still looks like a good list of topics to hash out in a pitch/review cycle.
I am interested in working on pitching an attribute like @expose that was proposed in this thread. We would like to adopt it for Swift and C++ interop, to control which Swift APIs get exposed to C++ (APIs would have to be explicitly annotated with @expose). I think it could potentially act as a replacement for @_cdecl as well.
So far I have thought about the following behavior for this attribute:
@expose - tells the compiler that this declaration is exposed to a foreign language interface (C/C++/Objective-C++, in one unified header). Most likely when applied to something like a struct, it will expose all its public members too. Note that in this case the Swift declaration will still use the underlying Swift ABI.
@expose(c++, name) - tells the compiler that this declaration is exposed to a foreign language interface. In the generated C++ interface, this declaration will be renamed with the specified name.
@expose(c, name, convention: cdecl) - equivalent to today's @_cdecl(name) essentially.
I am most likely going to work on a formal evolution pitch and proposal for @expose sometime this year, but I'm interested in getting feedback before then as well.
To me, that's a different thingâ@convention specifies a concrete representation for a specific function value, but function declarations in Swift don't have any particular convention by themselves; we generate entry points for them guided by how they're used. The attribute we're talking about here would be saying that we want to export a symbol with the C calling convention and a particular name based on a Swift function, but that wouldn't mean that that is the only way to call the function.
@convention could work. I steered away from it cause the existing conventions âcâ and âblockâ donât match up with the âcâ and âobjcâ and maybe âc++â or âcxxâ weâre going to want, but you could argue that thatâs because one is the convention for a function value (closure) and the other is the convention for a declaration (not necessarily a function). I think weâd want to put more arguments in there, like @available, but the syntactic space is available for that. (I personally still like @expose best of the options in this thread.)
(Separately, I would love to have @convention(thin) formalized, for a function with the Swift calling convention but no closure context. I donât know what to name it though. New thread!)
@c is a bit short, and doesnât follow the Zero-One-Many rule, i.e. if we want to export to more languages in the future we shouldnât go through all of this again.
I mean, not normally, but there are a few parts of the stdlib that do this. I do think itâs worth supporting: âC manglingâ really means âno manglingâ or âcustom symbol nameâ, which in turn means âcan be looked up in a dylibâ. I donât think that has to be supported in the same feature as exporting the C convention to C, though.
What if we had both -emit-c-header and -emit-objc-header that took the same code path, except the former would produce an error if you actually had @objc classes/protocols in your module?
I'd rather have something like @export(c). cdecl is a hangover from the 1980s (the Internet suggests it's Microsoft's, but actually I think it comes from Lattice C). Also, if we have @cdecl, Windows folk are going to want @stdcall et al (which I'd probably spell @export(pascal) because that's what it really means).
-emit-header=c,objc,c++,objc++ would let you specify exactly which language(s) you expected your header to work with. You could even go further (potentially) and specify which version, e.g. -emit-header=c11 or -emit-header=c++23.
In the context of C++ interoperability we don't intend to let the user control which flavor of the C/C++ language they want to expose in the generated header. We would like to generate a single header file that exposes declarations that are defined in the #if defined(__cplusplus) or the #if defined(__OBJC__) blocks, so that the user will be able to use the interface that's right for their translation unit. They will also always have access to the underlying C declarations if they would like to just use plain C and interoperate with Swift manually without proper bridging support that's provided through C++ functions and classes. Furthermore, we don't intend to allow them to control which C++ standard the header should generate. The header will choose to provide certain declarations or utilize certain C++ clauses (e.g. C++ 20 requires clauses) based on the C++ standard specified by the translation unit that's importing the header.
I would like to use something like -emit-clang-header and alias the old -emit-objc-header flag to it. I actually just recently implemented something like that in the frontend for C++ interoperability (although it aliases in the opposite direction), but it's not yet something that can be adopted by the Driver as it will need to get community approval first.
stdcall and pascal are differentâPascal convention pushes the arguments on the stack in FIFO order so they appear "backwards" in memory, opposite from C, and reserves stack space for the return value before the arguments. stdcall was introduced with Win32 and is basically a C convention but with callee-pop arguments: arguments are pushed in C order, and returns are by-register like in C. Pascal is probably irrelevant today (unless someone wants to keep the Mac OS 9 port going), but stdcall would be interesting for Win32 interop to this day. (Although even there it's contained to Win32âon Win64 the conventions have all been combined into one C-style convention).