Browse Source

Create C++ node API library

tags/v0.0.0-test-pr-120
Philipp Oppermann 3 years ago
parent
commit
c56e9ea57e
Failed to extract signature
10 changed files with 105 additions and 65 deletions
  1. +10
    -11
      Cargo.lock
  2. +2
    -1
      Cargo.toml
  3. +7
    -3
      apis/c++/node/Cargo.toml
  4. +4
    -0
      apis/c++/node/build.rs
  5. +32
    -21
      apis/c++/node/src/lib.rs
  6. +1
    -1
      examples/c++-dataflow/dataflow.yml
  7. +0
    -10
      examples/c++-dataflow/node-rust-api/build.rs
  8. +10
    -6
      examples/c++-dataflow/node-rust-api/main.cc
  9. +0
    -4
      examples/c++-dataflow/node-rust-api/src/main.h
  10. +39
    -8
      examples/c++-dataflow/run.rs

+ 10
- 11
Cargo.lock View File

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


+ 2
- 1
Cargo.toml View File

@@ -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",


examples/c++-dataflow/node-rust-api/Cargo.toml → apis/c++/node/Cargo.toml View File

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

+ 4
- 0
apis/c++/node/build.rs View File

@@ -0,0 +1,4 @@
fn main() {
let _build = cxx_build::bridge("src/lib.rs");
println!("cargo:rerun-if-changed=src/lib.rs");
}

examples/c++-dataflow/node-rust-api/src/main.rs → apis/c++/node/src/lib.rs View File

@@ -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(())
}

+ 1
- 1
examples/c++-dataflow/dataflow.yml View File

@@ -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:


+ 0
- 10
examples/c++-dataflow/node-rust-api/build.rs View File

@@ -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");
}

examples/c++-dataflow/node-rust-api/src/main.cc → examples/c++-dataflow/node-rust-api/main.cc View File

@@ -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;
} }

+ 0
- 4
examples/c++-dataflow/node-rust-api/src/main.h View File

@@ -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);

+ 39
- 8
examples/c++-dataflow/run.rs View File

@@ -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");


Loading…
Cancel
Save