Sjors / libwally-swift

Swift wrapper for LibWally, a collection of useful primitives for cryptocurrency wallets
MIT License
40 stars 18 forks source link

Reduce boiler plate c-bridge code #2

Open Sjors opened 5 years ago

Sjors commented 5 years ago

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

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".

pavel-bc commented 4 years ago

@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
}
jgriffiths commented 4 years ago

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.

Sjors commented 4 years ago

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.

jgriffiths commented 4 years ago

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.