| @@ -746,17 +746,6 @@ dependencies = [ | |||||
| "syn", | "syn", | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "cxx-dataflow-example-node-rust-api" | |||||
| version = "0.1.0" | |||||
| dependencies = [ | |||||
| "cxx", | |||||
| "cxx-build", | |||||
| "dora-node-api", | |||||
| "eyre", | |||||
| "rand", | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "cxx-dataflow-example-operator-rust-api" | name = "cxx-dataflow-example-operator-rust-api" | ||||
| version = "0.1.0" | version = "0.1.0" | ||||
| @@ -1007,6 +996,16 @@ dependencies = [ | |||||
| "tracing", | "tracing", | ||||
| ] | ] | ||||
| [[package]] | |||||
| name = "dora-node-api-cxx" | |||||
| version = "0.1.0" | |||||
| dependencies = [ | |||||
| "cxx", | |||||
| "cxx-build", | |||||
| "dora-node-api", | |||||
| "eyre", | |||||
| ] | |||||
| [[package]] | [[package]] | ||||
| name = "dora-node-api-python" | name = "dora-node-api-python" | ||||
| version = "0.1.0" | version = "0.1.0" | ||||
| @@ -1,6 +1,7 @@ | |||||
| [workspace] | [workspace] | ||||
| members = [ | members = [ | ||||
| "apis/c/*", | "apis/c/*", | ||||
| "apis/c++/*", | |||||
| "apis/python/node", | "apis/python/node", | ||||
| "apis/python/operator", | "apis/python/operator", | ||||
| "apis/rust/*", | "apis/rust/*", | ||||
| @@ -8,7 +9,7 @@ members = [ | |||||
| "apis/rust/operator/types", | "apis/rust/operator/types", | ||||
| "binaries/*", | "binaries/*", | ||||
| "examples/rust-dataflow/*", | "examples/rust-dataflow/*", | ||||
| "examples/c++-dataflow/*-rust-*", | |||||
| "examples/c++-dataflow/operator-rust-api", | |||||
| "examples/iceoryx/*", | "examples/iceoryx/*", | ||||
| "libraries/communication-layer", | "libraries/communication-layer", | ||||
| "libraries/core", | "libraries/core", | ||||
| @@ -1,15 +1,19 @@ | |||||
| [package] | [package] | ||||
| name = "cxx-dataflow-example-node-rust-api" | |||||
| name = "dora-node-api-cxx" | |||||
| version = "0.1.0" | version = "0.1.0" | ||||
| edition = "2021" | edition = "2021" | ||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||
| [lib] | |||||
| crate-type = ["staticlib"] | |||||
| [dependencies] | [dependencies] | ||||
| cxx = "1.0.73" | cxx = "1.0.73" | ||||
| dora-node-api = { version = "0.1.0", path = "../../../apis/rust/node" } | |||||
| dora-node-api = { version = "0.1.0", path = "../../../apis/rust/node", default-features = false, features = [ | |||||
| "zenoh", | |||||
| ] } | |||||
| eyre = "0.6.8" | eyre = "0.6.8" | ||||
| rand = "0.8.5" | |||||
| [build-dependencies] | [build-dependencies] | ||||
| cxx-build = "1.0.73" | cxx-build = "1.0.73" | ||||
| @@ -0,0 +1,4 @@ | |||||
| fn main() { | |||||
| let _build = cxx_build::bridge("src/lib.rs"); | |||||
| println!("cargo:rerun-if-changed=src/lib.rs"); | |||||
| } | |||||
| @@ -1,7 +1,12 @@ | |||||
| use dora_node_api::{self, DoraNode, Input, Receiver}; | |||||
| use dora_node_api::{self, Input, Receiver}; | |||||
| #[cxx::bridge] | #[cxx::bridge] | ||||
| mod ffi { | mod ffi { | ||||
| struct DoraNode { | |||||
| inputs: Box<Inputs>, | |||||
| send_output: Box<OutputSender>, | |||||
| } | |||||
| struct DoraInput { | struct DoraInput { | ||||
| end_of_input: bool, | end_of_input: bool, | ||||
| id: String, | id: String, | ||||
| @@ -14,22 +19,38 @@ mod ffi { | |||||
| extern "Rust" { | extern "Rust" { | ||||
| type Inputs; | type Inputs; | ||||
| type OutputSender<'a>; | |||||
| type OutputSender; | |||||
| fn next_input(inputs: &mut Inputs) -> DoraInput; | |||||
| fn send_output(output_sender: &mut OutputSender, id: String, data: &[u8]) -> DoraResult; | |||||
| fn init_dora_node() -> Result<DoraNode>; | |||||
| fn free_dora_node(node: DoraNode); | |||||
| fn next_input(inputs: &mut Box<Inputs>) -> DoraInput; | |||||
| fn send_output( | |||||
| output_sender: &mut Box<OutputSender>, | |||||
| id: String, | |||||
| data: &[u8], | |||||
| ) -> DoraResult; | |||||
| } | } | ||||
| } | |||||
| unsafe extern "C++" { | |||||
| include!("cxx-dataflow-example-node-rust-api/src/main.h"); | |||||
| fn init_dora_node() -> eyre::Result<ffi::DoraNode> { | |||||
| let mut node = dora_node_api::DoraNode::init_from_env()?; | |||||
| let input_stream = node.inputs()?; | |||||
| let inputs = Inputs(input_stream); | |||||
| let send_output = OutputSender(node); | |||||
| fn cxx_main(inputs: &mut Inputs, output_sender: &mut OutputSender); | |||||
| } | |||||
| Ok(ffi::DoraNode { | |||||
| inputs: Box::new(inputs), | |||||
| send_output: Box::new(send_output), | |||||
| }) | |||||
| } | |||||
| fn free_dora_node(node: ffi::DoraNode) { | |||||
| let _ = node; | |||||
| } | } | ||||
| pub struct Inputs(Receiver<Input>); | pub struct Inputs(Receiver<Input>); | ||||
| fn next_input(inputs: &mut Inputs) -> ffi::DoraInput { | |||||
| fn next_input(inputs: &mut Box<Inputs>) -> ffi::DoraInput { | |||||
| match inputs.0.recv() { | match inputs.0.recv() { | ||||
| Ok(input) => { | Ok(input) => { | ||||
| let id = input.id.clone().into(); | let id = input.id.clone().into(); | ||||
| @@ -48,9 +69,9 @@ fn next_input(inputs: &mut Inputs) -> ffi::DoraInput { | |||||
| } | } | ||||
| } | } | ||||
| pub struct OutputSender<'a>(&'a mut DoraNode); | |||||
| pub struct OutputSender(dora_node_api::DoraNode); | |||||
| fn send_output(sender: &mut OutputSender, id: String, data: &[u8]) -> ffi::DoraResult { | |||||
| fn send_output(sender: &mut Box<OutputSender>, id: String, data: &[u8]) -> ffi::DoraResult { | |||||
| let result = sender | let result = sender | ||||
| .0 | .0 | ||||
| .send_output(&id.into(), Default::default(), data.len(), |out| { | .send_output(&id.into(), Default::default(), data.len(), |out| { | ||||
| @@ -62,13 +83,3 @@ fn send_output(sender: &mut OutputSender, id: String, data: &[u8]) -> ffi::DoraR | |||||
| }; | }; | ||||
| ffi::DoraResult { error } | ffi::DoraResult { error } | ||||
| } | } | ||||
| fn main() -> eyre::Result<()> { | |||||
| let mut node = DoraNode::init_from_env()?; | |||||
| let input_stream = node.inputs()?; | |||||
| let mut inputs = Inputs(input_stream); | |||||
| let mut outputs = OutputSender(&mut node); | |||||
| ffi::cxx_main(&mut inputs, &mut outputs); | |||||
| Ok(()) | |||||
| } | |||||
| @@ -5,7 +5,7 @@ communication: | |||||
| nodes: | nodes: | ||||
| - id: cxx-node-rust-api | - id: cxx-node-rust-api | ||||
| custom: | custom: | ||||
| source: ../../target/debug/cxx-dataflow-example-node-rust-api | |||||
| source: build/node_rust_api | |||||
| inputs: | inputs: | ||||
| tick: dora/timer/millis/300 | tick: dora/timer/millis/300 | ||||
| outputs: | outputs: | ||||
| @@ -1,10 +0,0 @@ | |||||
| fn main() { | |||||
| cxx_build::bridge("src/main.rs") // returns a cc::Build | |||||
| .file("src/main.cc") | |||||
| .flag_if_supported("-std=c++14") | |||||
| .compile("cxx-example-dataflow-node"); | |||||
| println!("cargo:rerun-if-changed=src/main.rs"); | |||||
| println!("cargo:rerun-if-changed=src/main.cc"); | |||||
| println!("cargo:rerun-if-changed=src/main.h"); | |||||
| } | |||||
| @@ -1,20 +1,22 @@ | |||||
| #include "cxx-dataflow-example-node-rust-api/src/main.h" | |||||
| #include "../build/dora-node-api.h" | |||||
| #include <iostream> | #include <iostream> | ||||
| #include <vector> | #include <vector> | ||||
| void cxx_main(Inputs &inputs, OutputSender &output_sender) | |||||
| int main() | |||||
| { | { | ||||
| std::cout << "HELLO FROM C++" << std::endl; | std::cout << "HELLO FROM C++" << std::endl; | ||||
| unsigned char counter = 0; | unsigned char counter = 0; | ||||
| auto dora_node = init_dora_node(); | |||||
| for (int i = 0; i < 20; i++) | for (int i = 0; i < 20; i++) | ||||
| { | { | ||||
| auto input = next_input(inputs); | |||||
| auto input = next_input(dora_node.inputs); | |||||
| if (input.end_of_input) | if (input.end_of_input) | ||||
| { | { | ||||
| return; | |||||
| break; | |||||
| } | } | ||||
| counter += 1; | counter += 1; | ||||
| @@ -22,12 +24,14 @@ void cxx_main(Inputs &inputs, OutputSender &output_sender) | |||||
| std::vector<unsigned char> out_vec{counter}; | std::vector<unsigned char> out_vec{counter}; | ||||
| rust::Slice<const uint8_t> out_slice{out_vec.data(), out_vec.size()}; | rust::Slice<const uint8_t> out_slice{out_vec.data(), out_vec.size()}; | ||||
| auto result = send_output(output_sender, "counter", out_slice); | |||||
| auto result = send_output(dora_node.send_output, "counter", out_slice); | |||||
| auto error = std::string(result.error); | auto error = std::string(result.error); | ||||
| if (!error.empty()) | if (!error.empty()) | ||||
| { | { | ||||
| std::cerr << "Error: " << error << std::endl; | std::cerr << "Error: " << error << std::endl; | ||||
| return; | |||||
| return -1; | |||||
| } | } | ||||
| } | } | ||||
| return 0; | |||||
| } | } | ||||
| @@ -1,4 +0,0 @@ | |||||
| #pragma once | |||||
| #include "cxx-dataflow-example-node-rust-api/src/main.rs.h" | |||||
| void cxx_main(Inputs &inputs, OutputSender &output_sender); | |||||
| @@ -8,20 +8,46 @@ use std::{ | |||||
| #[tokio::main] | #[tokio::main] | ||||
| async fn main() -> eyre::Result<()> { | async fn main() -> eyre::Result<()> { | ||||
| let root = Path::new(env!("CARGO_MANIFEST_DIR")); | let root = Path::new(env!("CARGO_MANIFEST_DIR")); | ||||
| let target = root.join("target"); | |||||
| std::env::set_current_dir(root.join(file!()).parent().unwrap()) | std::env::set_current_dir(root.join(file!()).parent().unwrap()) | ||||
| .wrap_err("failed to set working dir")?; | .wrap_err("failed to set working dir")?; | ||||
| tokio::fs::create_dir_all("build").await?; | tokio::fs::create_dir_all("build").await?; | ||||
| let build_dir = Path::new("build"); | |||||
| build_package("cxx-dataflow-example-node-rust-api").await?; | |||||
| build_package("cxx-dataflow-example-operator-rust-api").await?; | build_package("cxx-dataflow-example-operator-rust-api").await?; | ||||
| build_package("dora-node-api-cxx").await?; | |||||
| let cxxbridge = target | |||||
| .join("cxxbridge") | |||||
| .join("dora-node-api-cxx") | |||||
| .join("src"); | |||||
| tokio::fs::copy(cxxbridge.join("lib.rs.cc"), build_dir.join("bridge.cc")).await?; | |||||
| tokio::fs::copy( | |||||
| cxxbridge.join("lib.rs.h"), | |||||
| build_dir.join("dora-node-api.h"), | |||||
| ) | |||||
| .await?; | |||||
| build_package("dora-node-api-c").await?; | build_package("dora-node-api-c").await?; | ||||
| build_package("dora-operator-api-c").await?; | build_package("dora-operator-api-c").await?; | ||||
| build_cxx_node( | build_cxx_node( | ||||
| root, | root, | ||||
| &dunce::canonicalize(Path::new("node-c-api").join("main.cc"))?, | |||||
| &[ | |||||
| &dunce::canonicalize(Path::new("node-rust-api").join("main.cc"))?, | |||||
| &dunce::canonicalize(build_dir.join("bridge.cc"))?, | |||||
| ], | |||||
| "node_rust_api", | |||||
| &["-l", "dora_node_api_cxx"], | |||||
| ) | |||||
| .await?; | |||||
| build_cxx_node( | |||||
| root, | |||||
| &[&dunce::canonicalize( | |||||
| Path::new("node-c-api").join("main.cc"), | |||||
| )?], | |||||
| "node_c_api", | "node_c_api", | ||||
| &["-l", "dora_node_api_c"], | |||||
| ) | ) | ||||
| .await?; | .await?; | ||||
| build_cxx_operator( | build_cxx_operator( | ||||
| @@ -52,11 +78,16 @@ async fn build_package(package: &str) -> eyre::Result<()> { | |||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| async fn build_cxx_node(root: &Path, path: &Path, out_name: &str) -> eyre::Result<()> { | |||||
| async fn build_cxx_node( | |||||
| root: &Path, | |||||
| paths: &[&Path], | |||||
| out_name: &str, | |||||
| args: &[&str], | |||||
| ) -> eyre::Result<()> { | |||||
| let mut clang = tokio::process::Command::new("clang++"); | let mut clang = tokio::process::Command::new("clang++"); | ||||
| clang.arg(path); | |||||
| clang.arg("-std=c++14"); | |||||
| clang.arg("-l").arg("dora_node_api_c"); | |||||
| clang.args(paths); | |||||
| clang.arg("-std=c++17"); | |||||
| clang.args(args); | |||||
| #[cfg(target_os = "linux")] | #[cfg(target_os = "linux")] | ||||
| { | { | ||||
| clang.arg("-l").arg("m"); | clang.arg("-l").arg("m"); | ||||
| @@ -110,7 +141,7 @@ async fn build_cxx_node(root: &Path, path: &Path, out_name: &str) -> eyre::Resul | |||||
| clang | clang | ||||
| .arg("--output") | .arg("--output") | ||||
| .arg(Path::new("../build").join(format!("{out_name}{EXE_SUFFIX}"))); | .arg(Path::new("../build").join(format!("{out_name}{EXE_SUFFIX}"))); | ||||
| if let Some(parent) = path.parent() { | |||||
| if let Some(parent) = paths[0].parent() { | |||||
| clang.current_dir(parent); | clang.current_dir(parent); | ||||
| } | } | ||||
| @@ -125,7 +156,7 @@ async fn build_cxx_operator(path: &Path, out_name: &str) -> eyre::Result<()> { | |||||
| let mut compile = tokio::process::Command::new("clang++"); | let mut compile = tokio::process::Command::new("clang++"); | ||||
| compile.arg("-c").arg(path); | compile.arg("-c").arg(path); | ||||
| compile.arg("-std=c++14"); | |||||
| compile.arg("-std=c++17"); | |||||
| compile.arg("-o").arg(&object_file_path); | compile.arg("-o").arg(&object_file_path); | ||||
| #[cfg(unix)] | #[cfg(unix)] | ||||
| compile.arg("-fPIC"); | compile.arg("-fPIC"); | ||||