diff --git a/.gitignore b/.gitignore index ea8c4bf7f35f6f77f75d92ad8ce8349f6e81ddba..fb8fe3bdc30f6d1ba5dd0145584630f61debb0af 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /target +**/build +**/.vscode \ No newline at end of file diff --git a/blockchain-sys/src/bindings.rs b/blockchain-sys/src/bindings.rs index b6a150d91ecdd5f78697e18a9c16af33cc76704f..b9bf7077ca25aba5a2531fe70334a56dc3154c85 100644 --- a/blockchain-sys/src/bindings.rs +++ b/blockchain-sys/src/bindings.rs @@ -46,6 +46,53 @@ fn bindgen_test_layout_LibBcPayload() { ) ); } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct LibBcCallback { + pub target: *mut ::std::os::raw::c_void, + pub callback: ::std::option::Option< + unsafe extern "C" fn(target: *mut ::std::os::raw::c_void, payload: LibBcPayload), + >, +} +#[test] +fn bindgen_test_layout_LibBcCallback() { + assert_eq!( + ::std::mem::size_of::<LibBcCallback>(), + 16usize, + concat!("Size of: ", stringify!(LibBcCallback)) + ); + assert_eq!( + ::std::mem::align_of::<LibBcCallback>(), + 8usize, + concat!("Alignment of ", stringify!(LibBcCallback)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<LibBcCallback>())).target as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(LibBcCallback), + "::", + stringify!(target) + ) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::<LibBcCallback>())).callback as *const _ as usize }, + 8usize, + concat!( + "Offset of field: ", + stringify!(LibBcCallback), + "::", + stringify!(callback) + ) + ); +} +extern "C" { + pub fn libbc_callback_call(cb: *const LibBcCallback, payload: LibBcPayload); +} +extern "C" { + pub fn libbc_pass_callback(cb: LibBcCallback); +} extern "C" { pub fn libbc_new_blockchain() -> *mut LibBcBlockchain; } diff --git a/libblockchain/src/lib.cpp b/libblockchain/src/lib.cpp index 60b5937e238e9b00106f27a7d82ad2adbba7c1ee..55480d0ab6d2b4bdd5004e3bd1c5363d47446fe4 100644 --- a/libblockchain/src/lib.cpp +++ b/libblockchain/src/lib.cpp @@ -4,42 +4,52 @@ #include "lib.h" -class LibBcBlockchain { +class Blockchain { private: std::vector<std::string> data; public: - LibBcBlockchain(); + Blockchain(); void add_data(const std::string &&data); void print(); }; -LibBcBlockchain::LibBcBlockchain() : data() {} +Blockchain::Blockchain() : data() {} -void LibBcBlockchain::add_data(const std::string &&data) { +void Blockchain::add_data(const std::string &&data) { this->data.emplace_back(data); } -LibBcBlockchain *libbc_new_blockchain() { - return new LibBcBlockchain(); +Blockchain *libbc_new_blockchain() { + return new Blockchain(); } -void LibBcBlockchain::print() { +void Blockchain::print() { for (auto &data : this->data) { std::cout << data << std::endl; } } -void libbc_blockchain_add(LibBcBlockchain *bc, uint64_t id, struct LibBcPayload payload) { +void libbc_blockchain_add(Blockchain *bc, uint64_t id, struct LibBcPayload payload) { std::string s(payload.data, payload.data + payload.len); std::cout << "Added data " << id << " to Blockchain: " << s << std::endl; bc->add_data(std::move(s)); } -void libbc_blockchain_print(LibBcBlockchain *bc) { +void libbc_blockchain_print(Blockchain *bc) { bc->print(); } -void libbc_delete_blockchain(LibBcBlockchain *bc) { +void libbc_delete_blockchain(Blockchain *bc) { delete bc; +} + +void libbc_callback_call(const struct LibBcCallback *cb, struct LibBcPayload payload) { + cb->callback(cb->target, payload); +} + +void libbc_pass_callback(const struct LibBcCallback cb) { + LibBcPayload payload{(uint8_t *)"hello rust", 10}; + + libbc_callback_call(&cb, payload); } \ No newline at end of file diff --git a/libblockchain/src/lib.h b/libblockchain/src/lib.h index 35a2b9d944a721db0b4f269f74c0c867e4697d2c..40071812a4bc5cae3cfdb7acb18ac178bb8c83f5 100644 --- a/libblockchain/src/lib.h +++ b/libblockchain/src/lib.h @@ -7,7 +7,7 @@ // https://isocpp.org/wiki/faq/mixing-c-and-cpp#cpp-objs-passed-to-c #ifdef __cplusplus -class LibBcBlockchain; +typedef class Blockchain LibBcBlockchain; #else typedef struct LibBcBlockchain LibBcBlockchain; #endif @@ -19,13 +19,22 @@ typedef struct LibBcBlockchain LibBcBlockchain; extern "C" { #endif - struct LibBcPayload { const uint8_t *data; size_t len; }; + // Our callback includes a target ("self" in Rust, "this" in C++) that store state (such as a channel) on the Rust side. + // target will be the first parameter to our callback function + struct LibBcCallback { + void *target; + void (*callback)(void *target, struct LibBcPayload payload); + }; + void libbc_callback_call(const struct LibBcCallback *cb, struct LibBcPayload payload); + void libbc_pass_callback(const struct LibBcCallback cb); + + LibBcBlockchain *libbc_new_blockchain(); // Its a good idea to prefix our C functions with something due to the lack of namespaces. diff --git a/program/src/main.rs b/program/src/main.rs index d0f25a4298805637d375a05d4bc1c09ae13b7780..59e72a57d533a072e4df79d701f2928388017b8a 100644 --- a/program/src/main.rs +++ b/program/src/main.rs @@ -1,7 +1,11 @@ +use blockchain_sys::libbc_blockchain_add; +use blockchain_sys::libbc_new_blockchain; use blockchain_sys::LibBcBlockchain; +use blockchain_sys::LibBcCallback; use blockchain_sys::LibBcPayload; -use blockchain_sys::libbc_new_blockchain; -use blockchain_sys::libbc_blockchain_add; +use std::io::Write; +use std::slice; +use std::str; struct Blockchain { inner: *mut LibBcBlockchain, @@ -10,7 +14,7 @@ struct Blockchain { impl Blockchain { pub fn new() -> Self { Self { - inner: unsafe { libbc_new_blockchain() } + inner: unsafe { libbc_new_blockchain() }, } } @@ -28,8 +32,6 @@ impl Blockchain { } } - - fn main() { let mut blockchain = Blockchain::new(); @@ -40,4 +42,44 @@ fn main() { blockchain_sys::libbc_blockchain_print(blockchain.inner); blockchain_sys::libbc_delete_blockchain(blockchain.inner); } + + let mut sink = Sink::new(); + let sink: *mut Sink = &mut sink; + let callback = LibBcCallback { + target: sink.cast(), + callback: Some(call_sink), + }; + + unsafe { + blockchain_sys::libbc_pass_callback(callback); + } +} + +//Callback implementation + +struct Sink { + // this could be a channel that forwards data to the rest of the Rust program + channel: std::io::Sink, +} + +impl Sink { + fn new() -> Self { + Self { + channel: std::io::sink(), + } + } +} + +unsafe extern "C" fn call_sink(this: *mut std::ffi::c_void, payload: LibBcPayload) { + // Safety first. Does not happen, if we do everythign right. + let sink = match this.cast::<Sink>().as_mut() { + Some(s) => s, + None => return, + }; + + let slice = slice::from_raw_parts(payload.data, payload.len); + + println!("Received message from C++: {:?}", str::from_utf8(slice)); + + let _ = sink.channel.write(slice); }