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)
+
+![](https://github.com/apache/incubator-opendal/assets/5351546/87bbf6e5-f19e-449a-b368-3e283016c887)
+
+## 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();
+}

Reply via email to