libwally-swift icon indicating copy to clipboard operation
libwally-swift copied to clipboard

Reduce boiler plate c-bridge code

Open Sjors opened this issue 5 years ago • 4 comments

For example in order to call bip39_mnemonic_from_bytes and convert Data to String:

precondition(entropy.data.count <= MAX_WORDS)
var bytes = UnsafeMutablePointer<UInt8>.allocate(capacity: MAX_WORDS)
let bytes_len = entropy.data.count

var output: UnsafeMutablePointer<Int8>?
defer {
    wally_free_string(output)
}
entropy.data.copyBytes(to: bytes, count: entropy.data.count)

precondition(bip39_mnemonic_from_bytes(nil, bytes, bytes_len, &output) == WALLY_OK)

if let words_c_string = output {
    let words = String(cString: words_c_string)
    self.init(words)
} else {
    return nil
}

This code is currently not reused in any way, even though libwally-core only uses a limited number of input and output data types.

One approach could be similar to how libwally-core Python tests work, with a mapping of C to Python: https://github.com/ElementsProject/libwally-core/blob/master/src/test/util.py

  • ('bip39_mnemonic_from_bytes', c_int, [c_void_p, c_void_p, c_ulong, c_char_p_p])

The Swift Package Manager should be useful ingredient. See e.g. this blog post, which suggests creating a "basic wrapper" and then "create a second Swift wrapper: a wrapper around the wrapper, providing a more natural Swift implementation".

Sjors avatar Jun 13 '19 10:06 Sjors

@Sjors do you think this approach with extensions might be worth it?

Here's the resulting example saving some lines (don't think it's possible to get rid of defer here):

precondition(entropy.data.count <= MAX_BYTES)
        
var output: UnsafeMutablePointer<Int8>?
defer {
    wally_free_string(output)
}
        
precondition(bip39_mnemonic_from_bytes(nil, entropy.data.toPointer(), entropy.data.count, &output) == WALLY_OK)
        
if let words_c_string = output {
    let words = String(cString: words_c_string)
    self.init(words)
} else {
    return nil
}

pavel-bc avatar Feb 10 '20 15:02 pavel-bc

Please note that wally now has a wrapper generator script: https://github.com/ElementsProject/libwally-core/blob/master/tools/build_wrappers.py

This has enough information to generate the c++ wrapper https://github.com/ElementsProject/libwally-core/blob/master/include/wally.hpp, and so should be enough to generate a low-level swift wrapper for you to wrap further.

I'm happy to take patches that do this into wally, if you need help feel free to @ me.

jgriffiths avatar Sep 14 '20 08:09 jgriffiths

Last time I tried it was an unbelievable pain to get C++ to work with Swift, whereas C was easy. But that may just me my bad.

Sjors avatar Sep 14 '20 16:09 Sjors

Last time I tried it was an unbelievable pain to get C++ to work with Swift, whereas C was easy. But that may just me my bad.

Yes, its easier to wrap C - however my comment above is referring to the fact that in wally we can now generate C++ wrappers that call the wally C code. Looking at the wrapper code posted above, I think the wally code generation framework would be easy to modify to fully generate a low-level swift wrapper.

jgriffiths avatar Sep 14 '20 22:09 jgriffiths