Let's think how to swiftify the following C++ class Foo with c++ interoperability feature.
class Foo {
Foo() { ... }
void doAdditionalConfiguration() { ... }
};
// A function to allocate memory in the heap area and returns the raw pointer
Foo* setupFoo() {
Foo* fooPtr = new Foo();
fooPtr -> doAdditionalConfiguration();
return fooPtr;
}
Foo will be like the following with Swift.
// In "FooModule"
@_expose(Cxx) public struct Foo {
public init() { ... }
public func doAdditionalConfiguration() { ... }
}
In this case, how can we update setupFoo() c++ function? The initializer of the swiftified Foo is FooModule::Foo::init, and the c++ constructor Foo() is disabled. So, we cannot use new FooModule::Foo() syntax to allocate the memory.
Once I came up with the idea of the following code, but it cannot be compiled.
Foo* setupFoo() {
Foo* fooPtr = (Foo*)malloc(sizeof(Foo));
*fooPtr = FooModule::Foo:init(); // object of type 'FooModule::Foo' cannot be assigned because its copy assignment operator is implicitly deleted
fooPtr -> doAdditionalConfiguration();
return fooPtr;
}
There's no good way to do this because at the moment we can't move the Swift values in C++. Once we have move support in the future, you should be able to do:
Unlike malloc, calloc returns memory initialized to zeroes. If "all zeroes" is valid for the struct (which it won't always be -- see below), you could do something like this:
@_expose(Cxx) public struct Foo {
// let's say this just holds some integers
var bar: Int
var baz: Int
// and that the default init does this:
public init() {
self.bar = 0
self.baz = 1
}
// Then, C++ code can call this:
public mutating func initializeAssumingZeroed() {
self.baz = 1
}
}
This kind of approach should not be used. It violates the language semantics of both C++ and Swift because both the Swift value and the C++ isn't constructed correctly, so the C++ compiler is free to remove this code as it's a source of undefined behavior. Furthermore, structures that don't have a fixed layout and/or non-trivial structures might end up either having additional fields in C++ that require correct initialization in order to be passed to Swift correctly and/or might actually be allocated on the heap if their layout is not known at compile time.
Actually I tried it, and surprisingly, allocate was not exported to the module header and not visible from C++.
And, I wonder why a swift struct is not copyable on C++. Since it is a value type and the instance is not managed by ARC or some other memory management systems, it looks like there is not problem to simply copy the struct.
If any of its fields are, it can't be copied byte-for-byte without retaining that field. A "trivial" struct, like @Alex_L mentioned above, can only have integers, booleans, C structs (but not necessarily other Swift structs), and certain enums. Even Swift's value-semantic collections like String, Set, and Array aren't trivial.