diff --git a/blockchain-sys/build.rs b/blockchain-sys/build.rs index b6287c0209b73ccf5a88e9337d2221cf7c24589a..cc0a5cd8d40d654b76441c7286dbeef5db138dc5 100644 --- a/blockchain-sys/build.rs +++ b/blockchain-sys/build.rs @@ -4,7 +4,8 @@ fn main() { // Rust build scripts can give commands back to cargo using println... (?) // Rerun this script when our C++ library changes. - println!("cargo:rerun-if-changed=../libblockchain/*"); + println!("cargo:rerun-if-changed=../libblockchain/src/lib.h"); + println!("cargo:rerun-if-changed=../libblockchain/src/lib.cpp"); // Build the library. We use the cmake crate to build. Beyond simply invoking our CmakeLists.txt, it respects profiles (Debug/Release buids) and also targets (for cross compiling). let dst = cmake::Config::new("../libblockchain").build(); @@ -23,7 +24,7 @@ fn main() { .with_codegen_config(bindgen::CodegenConfig::FUNCTIONS | bindgen::CodegenConfig::TYPES) // Only generate functions that have our prefix. Otherwise bindgen will generate a bunch of stuff from c std headers that we don't want. .whitelist_function("^libbc_.+$") - .whitelist_type("^libbc_.+$") + .whitelist_type("^LibBc.+$") .size_t_is_usize(true) .generate() .expect("Error generating Bindings"); diff --git a/blockchain-sys/src/bindings.rs b/blockchain-sys/src/bindings.rs index 15103972e5f2d2fb54b90469cf79b116d088e7f6..454d6b951203d3f3d17fd1040f44bb48cfc34ce0 100644 --- a/blockchain-sys/src/bindings.rs +++ b/blockchain-sys/src/bindings.rs @@ -4,43 +4,54 @@ pub type __uint8_t = ::std::os::raw::c_uchar; pub type __uint64_t = ::std::os::raw::c_ulong; #[repr(C)] #[derive(Debug, Copy, Clone)] -pub struct libbc_Payload { +pub struct LibBcBlockchain { + _unused: [u8; 0], +} +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct LibBcPayload { pub data: *const u8, pub len: usize, } #[test] -fn bindgen_test_layout_libbc_Payload() { +fn bindgen_test_layout_LibBcPayload() { assert_eq!( - ::std::mem::size_of::<libbc_Payload>(), + ::std::mem::size_of::<LibBcPayload>(), 16usize, - concat!("Size of: ", stringify!(libbc_Payload)) + concat!("Size of: ", stringify!(LibBcPayload)) ); assert_eq!( - ::std::mem::align_of::<libbc_Payload>(), + ::std::mem::align_of::<LibBcPayload>(), 8usize, - concat!("Alignment of ", stringify!(libbc_Payload)) + concat!("Alignment of ", stringify!(LibBcPayload)) ); assert_eq!( - unsafe { &(*(::std::ptr::null::<libbc_Payload>())).data as *const _ as usize }, + unsafe { &(*(::std::ptr::null::<LibBcPayload>())).data as *const _ as usize }, 0usize, concat!( "Offset of field: ", - stringify!(libbc_Payload), + stringify!(LibBcPayload), "::", stringify!(data) ) ); assert_eq!( - unsafe { &(*(::std::ptr::null::<libbc_Payload>())).len as *const _ as usize }, + unsafe { &(*(::std::ptr::null::<LibBcPayload>())).len as *const _ as usize }, 8usize, concat!( "Offset of field: ", - stringify!(libbc_Payload), + stringify!(LibBcPayload), "::", stringify!(len) ) ); } extern "C" { - pub fn libbc_handle_message(id: u64, payload: libbc_Payload); + pub fn libbc_new_blockchain() -> *mut LibBcBlockchain; +} +extern "C" { + pub fn libbc_blockchain_add(bc: *mut LibBcBlockchain, id: u64, payload: LibBcPayload); +} +extern "C" { + pub fn libbc_blockchain_print(bc: *mut LibBcBlockchain); } diff --git a/libblockchain/src/lib.cpp b/libblockchain/src/lib.cpp index b04c898ab4cd86edd2f2756e4fc1e8c512e2919e..c1d2b5ddac8c4ad8395df6fdaa828401bd6c4e39 100644 --- a/libblockchain/src/lib.cpp +++ b/libblockchain/src/lib.cpp @@ -1,28 +1,41 @@ #include <iostream> #include <string> +#include <vector> #include "lib.h" -class Blockchain -{ +class LibBcBlockchain { private: - std::string message; + std::vector<std::string> data; public: - Blockchain(std::string &&message) : message(message) {} + LibBcBlockchain(); + void add_data(const std::string &&data); + void print(); +}; + +LibBcBlockchain::LibBcBlockchain() : data() {} + +void LibBcBlockchain::add_data(const std::string &&data) { + this->data.emplace_back(data); +} - const std::string &get() - { - return this->message; +LibBcBlockchain *libbc_new_blockchain() { + return new LibBcBlockchain(); +} + +void LibBcBlockchain::print() { + for (auto &data : this->data) { + std::cout << data << std::endl; } -}; +} -void libbc_handle_message(uint64_t id, struct libbc_Payload payload) -{ - // Some C++ code in the implementation - auto s = std::string(payload.data, payload.data + payload.len); +void libbc_blockchain_add(LibBcBlockchain *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)); +} - Blockchain blockchain(std::move(s)); - - std::cout << "C++ says: " << blockchain.get() << std::endl; +void libbc_blockchain_print(LibBcBlockchain *bc) { + bc->print(); } \ No newline at end of file diff --git a/libblockchain/src/lib.h b/libblockchain/src/lib.h index abacc6a6e9d593c374ed51ceea8806cd7853a5f6..ff839d1e50a836110c8ce509c00382e1a044467b 100644 --- a/libblockchain/src/lib.h +++ b/libblockchain/src/lib.h @@ -5,6 +5,13 @@ //size_t #include <stddef.h> +// https://isocpp.org/wiki/faq/mixing-c-and-cpp#cpp-objs-passed-to-c +#ifdef __cplusplus +class LibBcBlockchain; +#else +typedef struct LibBcBlockchain LibBcBlockchain; +#endif + // Tell C++ to export the function without name-mangling and with C calling conventions. // If we omit this, the linker will not find our function. // The extern block should only contain C functions and types. No C++. @@ -13,14 +20,18 @@ extern "C" { #endif - struct libbc_Payload { - const uint8_t *data; + struct LibBcPayload + { + const uint8_t *data; size_t len; }; + LibBcBlockchain *libbc_new_blockchain(); + // Its a good idea to prefix our C functions with something due to the lack of namespaces. // Prefixes also makes usign bindgen easier (see blockchain-sys/build.rs). - void libbc_handle_message(uint64_t id, struct libbc_Payload payload); + void libbc_blockchain_add(LibBcBlockchain *bc, uint64_t id, struct LibBcPayload payload); + void libbc_blockchain_print(LibBcBlockchain *bc); #ifdef __cplusplus } diff --git a/program/src/main.rs b/program/src/main.rs index 1cffa683c247c393c0aa323607e500c5e76cf432..313abb1271970fc79e131003b45ccd5f878582b6 100644 --- a/program/src/main.rs +++ b/program/src/main.rs @@ -1,22 +1,42 @@ -use blockchain_sys::libbc_handle_message; -use blockchain_sys::libbc_Payload; - -// It's a good idea to wrap unsafe functions in safe functions that express the ownership semantics for Rust. -// In this case we don't pass ownership to C, so write a function that borrows a slice. -fn handle_message(id: u64, message: &[u8]) { - let payload = libbc_Payload { - data: message.as_ptr(), - len: message.len(), - }; - - // Safety: message is borrowed for the duration of this function. - unsafe { - libbc_handle_message(id, payload); +use blockchain_sys::LibBcBlockchain; +use blockchain_sys::LibBcPayload; +use blockchain_sys::libbc_new_blockchain; +use blockchain_sys::libbc_blockchain_add; + +struct Blockchain { + inner: *mut LibBcBlockchain, +} + +impl Blockchain { + pub fn new() -> Self { + Self { + inner: unsafe { libbc_new_blockchain() } + } + } + + // It's a good idea to wrap unsafe functions in safe functions that express the ownership semantics for Rust. + // In this case we don't pass ownership to C, so write a function that borrows a slice. + pub fn add(&mut self, id: u64, message: &[u8]) { + let payload = LibBcPayload { + data: message.as_ptr(), + len: message.len(), + }; + + unsafe { + libbc_blockchain_add(self.inner, id, payload); + } } } + + fn main() { - let s = "hello world"; + let mut blockchain = Blockchain::new(); - handle_message(0, s.as_bytes()); + blockchain.add(0, "hello world".as_bytes()); + blockchain.add(1, "hello again".as_bytes()); + + unsafe { + blockchain_sys::libbc_blockchain_print(blockchain.inner); + } }