This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 149edd550 feat(bindings/cpp): init cpp binding (#2980)
149edd550 is described below
commit 149edd550f03756e954f40f385c4b34de87ee306
Author: Mingzhuo Yin <[email protected]>
AuthorDate: Thu Aug 31 09:55:37 2023 +0800
feat(bindings/cpp): init cpp binding (#2980)
* feat(bindings/cpp): init cpp binding
Signed-off-by: silver-ymz <[email protected]>
* add error handle
Signed-off-by: silver-ymz <[email protected]>
* update readme
Signed-off-by: silver-ymz <[email protected]>
---------
Signed-off-by: silver-ymz <[email protected]>
---
Cargo.lock | 79 +++++++++++++++++++++++++++++++++++
Cargo.toml | 1 +
bindings/cpp/.gitignore | 3 ++
bindings/cpp/CMakeLists.txt | 77 ++++++++++++++++++++++++++++++++++
Cargo.toml => bindings/cpp/Cargo.toml | 53 ++++++++---------------
bindings/cpp/README.md | 37 ++++++++++++++++
bindings/cpp/build.rs | 22 ++++++++++
bindings/cpp/include/opendal.hpp | 53 +++++++++++++++++++++++
bindings/cpp/src/lib.rs | 62 +++++++++++++++++++++++++++
bindings/cpp/src/opendal.cpp | 49 ++++++++++++++++++++++
bindings/cpp/tests/basic_test.cpp | 54 ++++++++++++++++++++++++
11 files changed, 455 insertions(+), 35 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index ea91cdd01..69576424d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -962,6 +962,16 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
[[package]]
name = "colorchoice"
version = "1.0.0"
@@ -1223,6 +1233,50 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
+[[package]]
+name = "cxx"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe98ba1789d56fb3db3bee5e032774d4f421b685de7ba703643584ba24effbe"
+dependencies = [
+ "cc",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4ce20f6b8433da4841b1dadfb9468709868022d829d5ca1f2ffbda928455ea3"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn 2.0.23",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20888d9e1d2298e2ff473cee30efe7d5036e437857ab68bbfea84c74dba91da2"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fa16a70dd58129e4dfffdff535fb1bce66673f7bbeec4a5a1765a504e1ccd84"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.23",
+]
+
[[package]]
name = "darling"
version = "0.14.4"
@@ -2625,6 +2679,15 @@ dependencies = [
"vcpkg",
]
+[[package]]
+name = "link-cplusplus"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "linked-hash-map"
version = "0.5.6"
@@ -3508,6 +3571,16 @@ dependencies = [
"opendal",
]
+[[package]]
+name = "opendal-cpp"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "cxx",
+ "cxx-build",
+ "opendal",
+]
+
[[package]]
name = "opendal-dotnet"
version = "0.1.0"
@@ -5150,6 +5223,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+[[package]]
+name = "scratch"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152"
+
[[package]]
name = "sct"
version = "0.6.1"
diff --git a/Cargo.toml b/Cargo.toml
index 1b891db45..5bc5b746e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,6 +35,7 @@ members = [
"bindings/dotnet",
"bindings/ocaml",
"bindings/php",
+ "bindings/cpp",
"bin/oli",
"bin/oay",
diff --git a/bindings/cpp/.gitignore b/bindings/cpp/.gitignore
new file mode 100644
index 000000000..c1d315b0e
--- /dev/null
+++ b/bindings/cpp/.gitignore
@@ -0,0 +1,3 @@
+compile_commands.json
+.cache
+build
\ No newline at end of file
diff --git a/bindings/cpp/CMakeLists.txt b/bindings/cpp/CMakeLists.txt
new file mode 100644
index 000000000..b8b5abc1d
--- /dev/null
+++ b/bindings/cpp/CMakeLists.txt
@@ -0,0 +1,77 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+cmake_minimum_required(VERSION 3.20)
+project(opendal-cpp CXX)
+
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+set(CARGO_MANIFEST ${CMAKE_SOURCE_DIR}/Cargo.toml)
+set(CARGO_TARGET_DIR ${CMAKE_SOURCE_DIR}/../../target)
+set(RUST_SOURCE_FILE ${CMAKE_SOURCE_DIR}/src/lib.rs)
+set(RUST_BRIDGE_CPP ${CARGO_TARGET_DIR}/cxxbridge/opendal-cpp/src/lib.rs.cc)
+set(RUST_LIB
${CARGO_TARGET_DIR}/debug/${CMAKE_STATIC_LIBRARY_PREFIX}opendal_cpp${CMAKE_STATIC_LIBRARY_SUFFIX})
+set(CPP_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/include
${CARGO_TARGET_DIR}/cxxbridge/opendal-cpp/src)
+file(GLOB_RECURSE CPP_SOURCE_FILE src/*.cpp)
+
+add_custom_command(
+ OUTPUT ${RUST_BRIDGE_CPP} ${RUST_LIB}
+ COMMAND cargo build --manifest-path ${CARGO_MANIFEST}
+ DEPENDS ${RUST_SOURCE_FILE}
+ USES_TERMINAL
+ COMMENT "Running cargo..."
+)
+
+add_library(opendal_cpp STATIC ${CPP_SOURCE_FILE} ${RUST_BRIDGE_CPP})
+target_include_directories(opendal_cpp PUBLIC ${CPP_INCLUDE_DIR})
+target_link_libraries(opendal_cpp PUBLIC ${RUST_LIB})
+set_target_properties(opendal_cpp
+ PROPERTIES ADDITIONAL_CLEAN_FILES ${CARGO_TARGET_DIR}
+)
+
+# Platform-specific test configuration
+if(WIN32)
+ target_link_libraries(opendal_cpp userenv ws2_32 bcrypt)
+ set_target_properties(
+ opendal_cpp
+ PROPERTIES
+ MSVC_RUNTIME_LIBRARY "MultiThreadedDLL"
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}
+ )
+endif()
+
+# Tests
+enable_testing()
+find_package(GTest REQUIRED)
+file(GLOB_RECURSE TEST_SOURCE_FILE tests/*.cpp)
+add_executable(opendal_cpp_test ${TEST_SOURCE_FILE})
+target_include_directories(opendal_cpp_test PUBLIC ${CPP_INCLUDE_DIR}
${GTEST_INCLUDE_DIRS})
+target_link_libraries(opendal_cpp_test ${GTEST_LDFLAGS} GTest::gtest_main
opendal_cpp)
+target_compile_options(opendal_cpp_test PRIVATE ${GTEST_CFLAGS})
+
+# Platform-specific test configuration
+if(WIN32)
+ target_link_libraries(opendal_cpp_test userenv ws2_32 bcrypt)
+endif()
+if(APPLE)
+ target_link_libraries(opendal_cpp_test "-framework CoreFoundation
-framework Security")
+endif()
+
+include(GoogleTest)
+gtest_discover_tests(opendal_cpp_test)
\ No newline at end of file
diff --git a/Cargo.toml b/bindings/cpp/Cargo.toml
similarity index 51%
copy from Cargo.toml
copy to bindings/cpp/Cargo.toml
index 1b891db45..55fcce62f 100644
--- a/Cargo.toml
+++ b/bindings/cpp/Cargo.toml
@@ -15,42 +15,25 @@
# specific language governing permissions and limitations
# under the License.
-[profile.bench]
-debug = true
+[package]
+name = "opendal-cpp"
+publish = false
+version = "0.1.0"
-[workspace]
-default-members = ["core"]
-exclude = ["examples"]
-members = [
- "core",
- "core/fuzz",
+authors.workspace = true
+edition.workspace = true
+homepage.workspace = true
+license.workspace = true
+repository.workspace = true
+rust-version.workspace = true
- "bindings/c",
- "bindings/nodejs",
- "bindings/python",
- "bindings/ruby",
- "bindings/java",
- "bindings/haskell",
- "bindings/lua",
- "bindings/dotnet",
- "bindings/ocaml",
- "bindings/php",
+[lib]
+crate-type = ["staticlib"]
- "bin/oli",
- "bin/oay",
+[dependencies]
+opendal.workspace = true
+cxx = "1.0"
+anyhow = "1.0"
- "integrations/object_store",
-]
-resolver = "2"
-
-[workspace.package]
-authors = ["OpenDAL Contributors <[email protected]>"]
-edition = "2021"
-homepage = "https://opendal.apache.org/"
-license = "Apache-2.0"
-repository = "https://github.com/apache/incubator-opendal"
-rust-version = "1.65"
-version = "0.39.0"
-
-[workspace.dependencies]
-opendal = { version = "0.39", path = "core" }
+[build-dependencies]
+cxx-build = "1.0"
diff --git a/bindings/cpp/README.md b/bindings/cpp/README.md
new file mode 100644
index 000000000..259930acd
--- /dev/null
+++ b/bindings/cpp/README.md
@@ -0,0 +1,37 @@
+# OpenDAL CPP Binding (WIP)
+
+
+
+## Example
+
+```cpp
+#include "opendal.hpp"
+#include <vector>
+
+int main() {
+ auto op = opendal::Operator("memory");
+ std::vector<uint8_t> data = {1, 2, 3, 4, 5};
+ op.write("test", data);
+ auto result = op.read("test"); // result == data
+}
+```
+
+## Build
+
+```bash
+mkdir build
+cd build
+
+# Add -DCMAKE_EXPORT_COMPILE_COMMANDS=1 to generate compile_commands.json for
clangd
+cmake -DCMAKE_BUILD_TYPE=Debug -GNinja ..
+
+ninja
+```
+
+## Test
+
+You should build the project first. Then run:
+
+```bash
+ninja test
+```
\ No newline at end of file
diff --git a/bindings/cpp/build.rs b/bindings/cpp/build.rs
new file mode 100644
index 000000000..7d5d10f7b
--- /dev/null
+++ b/bindings/cpp/build.rs
@@ -0,0 +1,22 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+fn main() {
+ let _ = cxx_build::bridge("src/lib.rs");
+
+ println!("cargo:rerun-if-changed=src/lib.rs");
+}
diff --git a/bindings/cpp/include/opendal.hpp b/bindings/cpp/include/opendal.hpp
new file mode 100644
index 000000000..2d42b8390
--- /dev/null
+++ b/bindings/cpp/include/opendal.hpp
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#pragma once
+#include "lib.rs.h"
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace opendal {
+
+class Operator : std::enable_shared_from_this<Operator> {
+public:
+ Operator() = default;
+ Operator(std::string_view scheme,
+ const std::unordered_map<std::string, std::string> &config = {});
+
+ // Disable copy and assign
+ Operator(const Operator &) = delete;
+ Operator &operator=(const Operator &) = delete;
+
+ // Enable move
+ Operator(Operator &&) = default;
+ Operator &operator=(Operator &&) = default;
+ ~Operator() = default;
+
+ bool available() const;
+ std::vector<uint8_t> read(std::string_view path);
+ void write(std::string_view path, const std::vector<uint8_t> &data);
+
+private:
+ std::optional<rust::Box<opendal::ffi::Operator>> operator_;
+};
+
+} // namespace opendal
\ No newline at end of file
diff --git a/bindings/cpp/src/lib.rs b/bindings/cpp/src/lib.rs
new file mode 100644
index 000000000..ff9f68e12
--- /dev/null
+++ b/bindings/cpp/src/lib.rs
@@ -0,0 +1,62 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use anyhow::Result;
+use opendal as od;
+use std::collections::HashMap;
+use std::str::FromStr;
+
+#[cxx::bridge(namespace = "opendal::ffi")]
+mod ffi {
+ struct HashMapValue {
+ key: String,
+ value: String,
+ }
+
+ extern "Rust" {
+ type Operator;
+
+ fn new_operator(scheme: &str, configs: Vec<HashMapValue>) ->
Result<Box<Operator>>;
+ fn read(&self, path: &str) -> Result<Vec<u8>>;
+ fn write(&self, path: &str, bs: &[u8]) -> Result<()>;
+ }
+}
+
+struct Operator(od::BlockingOperator);
+
+fn new_operator(scheme: &str, configs: Vec<ffi::HashMapValue>) ->
Result<Box<Operator>> {
+ let scheme = od::Scheme::from_str(scheme)?;
+
+ let map = configs
+ .into_iter()
+ .map(|value| (value.key, value.value))
+ .collect::<HashMap<_, _>>();
+
+ let op = Box::new(Operator(od::Operator::via_map(scheme,
map)?.blocking()));
+
+ Ok(op)
+}
+
+impl Operator {
+ fn read(&self, path: &str) -> Result<Vec<u8>> {
+ Ok(self.0.read(path)?)
+ }
+
+ fn write(&self, path: &str, bs: &[u8]) -> Result<()> {
+ Ok(self.0.write(path, bs.to_owned())?)
+ }
+}
diff --git a/bindings/cpp/src/opendal.cpp b/bindings/cpp/src/opendal.cpp
new file mode 100644
index 000000000..92c240c3e
--- /dev/null
+++ b/bindings/cpp/src/opendal.cpp
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "opendal.hpp"
+
+using namespace opendal;
+
+Operator::Operator(std::string_view scheme,
+ const std::unordered_map<std::string, std::string> &config)
{
+ auto rust_map = rust::Vec<ffi::HashMapValue>();
+ rust_map.reserve(config.size());
+ for (const auto &[k, v] : config) {
+ rust_map.push_back(ffi::HashMapValue{
+ rust::String(k.data()),
+ rust::String(v.data()),
+ });
+ }
+
+ operator_ = opendal::ffi::new_operator(rust::Str(scheme.data()), rust_map);
+}
+
+bool Operator::available() const { return operator_.has_value(); }
+
+std::vector<uint8_t> Operator::read(std::string_view path) {
+ auto res = operator_.value()->read(rust::Str(path.data()));
+ return std::vector<uint8_t>(res.data(), res.data() + res.size());
+}
+
+void Operator::write(std::string_view path, const std::vector<uint8_t> &data) {
+ operator_.value()->write(
+ rust::Str(path.data()),
+ rust::Slice<const uint8_t>(data.data(), data.size()));
+}
\ No newline at end of file
diff --git a/bindings/cpp/tests/basic_test.cpp
b/bindings/cpp/tests/basic_test.cpp
new file mode 100644
index 000000000..32811b75f
--- /dev/null
+++ b/bindings/cpp/tests/basic_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "opendal.hpp"
+#include "gtest/gtest.h"
+#include <string>
+#include <unordered_map>
+
+class OpendalTest : public ::testing::Test {
+protected:
+ opendal::Operator op;
+
+ std::string scheme;
+ std::unordered_map<std::string, std::string> config;
+
+ void SetUp() override {
+ this->scheme = "memory";
+ op = opendal::Operator(this->scheme, this->config);
+
+ EXPECT_TRUE(this->op.available());
+ }
+};
+
+// Scenario: OpenDAL Blocking Operations
+TEST_F(OpendalTest, BasicTest) {
+ std::string path = "test";
+ std::vector<uint8_t> data = {1, 2, 3, 4, 5};
+
+ op.write("test", data);
+
+ auto res = op.read("test");
+ EXPECT_EQ(res, data);
+}
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}