Skip to content

Commit

Permalink
Added very basic structure for BSON support
Browse files Browse the repository at this point in the history
  • Loading branch information
liuzicheng1987 committed Jan 9, 2024
1 parent 05b6955 commit e6b76a7
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 4 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Run test
run: |
g++ --version
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build -j 4
./build/tests/flexbuffers/reflect-cpp-flexbuffers-tests
./build/tests/json/reflect-cpp-json-tests
Expand Down Expand Up @@ -47,7 +47,7 @@ jobs:
CXX: clang++
run: |
clang --version
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build -j 4
./build/tests/flexbuffers/reflect-cpp-flexbuffers-tests
./build/tests/json/reflect-cpp-json-tests
Expand Down
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
cmake_minimum_required(VERSION 3.15)

option(REFLECTCPP_BUILD_SHARED "Build shared library" OFF)
option(REFLECTCPP_BSON "Enable BSON support" OFF)
option(REFLECTCPP_FLEXBUFFERS "Enable flexbuffers support" OFF)
option(REFLECTCPP_XML "Enable XML support" OFF)
option(REFLECTCPP_YAML "Enable YAML support" OFF)

option(REFLECTCPP_BUILD_TESTS "Build tests" OFF)

# enable vcpkg if require features other than JSON
if (REFLECTCPP_FLEXBUFFERS OR REFLECTCPP_XML OR REFLECTCPP_YAML)
if (REFLECTCPP_BSON OR REFLECTCPP_FLEXBUFFERS OR REFLECTCPP_XML OR REFLECTCPP_YAML)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake CACHE STRING "Vcpkg toolchain file")
endif ()

Expand All @@ -24,6 +25,11 @@ endif ()

target_include_directories(reflectcpp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)

if (REFLECTCPP_BSON)
find_package(bson-1.0 CONFIG REQUIRED)
target_link_libraries(reflectcpp INTERFACE mongo::bson_static)
endif ()

if (REFLECTCPP_FLEXBUFFERS)
find_package(flatbuffers CONFIG REQUIRED)
target_link_libraries(reflectcpp INTERFACE flatbuffers::flatbuffers)
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ To use reflect-cpp in your project:
```cmake
add_subdirectory(reflect-cpp) # Add this project as a subdirectory
set(REFLECTCPP_BSON ON) # Optional
set(REFLECTCPP_FLEXBUFFERS ON) # Optional
set(REFLECTCPP_XML ON) # Optional
set(REFLECTCPP_YAML ON) # Optional
Expand Down Expand Up @@ -494,7 +495,7 @@ git submodule update --init
./vcpkg/bootstrap-vcpkg.bat # Windows
# You may be prompted to install additional dependencies.

cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build -j 4 # gcc, clang
cmake --build build --config Release -j 4 # MSVC
```
Expand Down
17 changes: 17 additions & 0 deletions include/rfl/bson/Parser.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef RFL_BSON_PARSER_HPP_
#define RFL_BSON_PARSER_HPP_

#include "../parsing/Parser.hpp"
#include "Reader.hpp"
#include "Writer.hpp"

namespace rfl {
namespace bson {

template <class T>
using Parser = parsing::Parser<Reader, Writer, T>;

}
} // namespace rfl

#endif
70 changes: 70 additions & 0 deletions include/rfl/bson/Reader.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#ifndef RFL_JSON_READER_HPP_
#define RFL_JSON_READER_HPP_

#include <array>
#include <concepts>
#include <exception>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <vector>

#include "../Result.hpp"
#include "../always_false.hpp"

namespace rfl {
namespace bson {

struct Reader {
using InputArrayType = ...;
using InputObjectType = ...;
using InputVarType = ...;

template <class T>
static constexpr bool has_custom_constructor = (requires(InputVarType var) {
T::from_bson_obj(var);
});

rfl::Result<InputVarType> get_field(
const std::string& _name, const InputObjectType _obj) const noexcept {}

bool is_empty(const InputVarType _var) const noexcept {}

template <class T>
rfl::Result<T> to_basic_type(const InputVarType _var) const noexcept {}

rfl::Result<InputArrayType> to_array(const InputVarType _var) const noexcept {
}

template <size_t size, class FunctionType>
std::array<std::optional<InputVarType>, size> to_fields_array(
const FunctionType _fct, const InputObjectType _obj) const noexcept {}

std::vector<std::pair<std::string, InputVarType>> to_map(
const InputObjectType _obj) const noexcept {}

rfl::Result<InputObjectType> to_object(
const InputVarType _var) const noexcept {}

std::vector<InputVarType> to_vec(const InputArrayType _arr) const noexcept {}

template <class T>
rfl::Result<T> use_custom_constructor(
const InputVarType _var) const noexcept {
try {
return T::from_bson_obj(_var);
} catch (std::exception& e) {
return rfl::Error(e.what());
}
}
};

} // namespace bson
} // namespace rfl

#endif // JSON_PARSER_HPP_
166 changes: 166 additions & 0 deletions include/rfl/bson/Writer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#ifndef RFL_BSON_WRITER_HPP_
#define RFL_BSON_WRITER_HPP_

#include <bson/bson.h>

#include <exception>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>

#include "../Result.hpp"
#include "../always_false.hpp"

namespace rfl {
namespace bson {

class Writer {
public:
struct YYBSONOutputArray {
YYBSONOutputArray(yybson_mut_val* _val) : val_(_val) {}
yybson_mut_val* val_;
};

struct YYBSONOutputObject {
YYBSONOutputObject(yybson_mut_val* _val) : val_(_val) {}
yybson_mut_val* val_;
};

struct YYBSONOutputVar {
YYBSONOutputVar(yybson_mut_val* _val) : val_(_val) {}

YYBSONOutputVar(YYBSONOutputArray _arr) : val_(_arr.val_) {}

YYBSONOutputVar(YYBSONOutputObject _obj) : val_(_obj.val_) {}

yybson_mut_val* val_;
};

using OutputArrayType = YYBSONOutputArray;
using OutputObjectType = YYBSONOutputObject;
using OutputVarType = YYBSONOutputVar;

Writer(yybson_mut_doc* _doc) : doc_(_doc) {}

~Writer() = default;

OutputArrayType array_as_root(const size_t _size) const noexcept {
const auto arr = yybson_mut_arr(doc_);
yybson_mut_doc_set_root(doc_, arr);
return OutputArrayType(arr);
}

OutputObjectType object_as_root(const size_t _size) const noexcept {
const auto obj = yybson_mut_obj(doc_);
yybson_mut_doc_set_root(doc_, obj);
return OutputObjectType(obj);
}

OutputVarType null_as_root() const noexcept {
const auto null = yybson_mut_null(doc_);
yybson_mut_doc_set_root(doc_, null);
return OutputVarType(null);
}

template <class T>
OutputVarType value_as_root(const T& _var) const noexcept {
const auto val = from_basic_type(_var);
yybson_mut_doc_set_root(doc_, val.val_);
return OutputVarType(val);
}

OutputArrayType add_array_to_array(const size_t _size,
OutputArrayType* _parent) const noexcept {
const auto arr = yybson_mut_arr(doc_);
yybson_mut_arr_add_val(_parent->val_, arr);
return OutputArrayType(arr);
}

OutputArrayType add_array_to_object(
const std::string& _name, const size_t _size,
OutputObjectType* _parent) const noexcept {
const auto arr = yybson_mut_arr(doc_);
yybson_mut_obj_add(_parent->val_, yybson_mut_strcpy(doc_, _name.c_str()),
arr);
return OutputArrayType(arr);
}

OutputObjectType add_object_to_array(
const size_t _size, OutputArrayType* _parent) const noexcept {
const auto obj = yybson_mut_obj(doc_);
yybson_mut_arr_add_val(_parent->val_, obj);
return OutputObjectType(obj);
}

OutputObjectType add_object_to_object(
const std::string& _name, const size_t _size,
OutputObjectType* _parent) const noexcept {
const auto obj = yybson_mut_obj(doc_);
yybson_mut_obj_add(_parent->val_, yybson_mut_strcpy(doc_, _name.c_str()),
obj);
return OutputObjectType(obj);
}

template <class T>
OutputVarType add_value_to_array(const T& _var,
OutputArrayType* _parent) const noexcept {
const auto val = from_basic_type(_var);
yybson_mut_arr_add_val(_parent->val_, val.val_);
return OutputVarType(val);
}

template <class T>
OutputVarType add_value_to_object(const std::string& _name, const T& _var,
OutputObjectType* _parent) const noexcept {
const auto val = from_basic_type(_var);
yybson_mut_obj_add(_parent->val_, yybson_mut_strcpy(doc_, _name.c_str()),
val.val_);
return OutputVarType(val);
}

OutputVarType add_null_to_array(OutputArrayType* _parent) const noexcept {
const auto null = yybson_mut_null(doc_);
yybson_mut_arr_add_val(_parent->val_, null);
return OutputVarType(null);
}

OutputVarType add_null_to_object(const std::string& _name,
OutputObjectType* _parent) const noexcept {
const auto null = yybson_mut_null(doc_);
yybson_mut_obj_add(_parent->val_, yybson_mut_strcpy(doc_, _name.c_str()),
null);
return OutputVarType(null);
}

void end_array(OutputArrayType* _arr) const noexcept {}

void end_object(OutputObjectType* _obj) const noexcept {}

private:
template <class T>
OutputVarType from_basic_type(const T& _var) const noexcept {
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
return OutputVarType(yybson_mut_strcpy(doc_, _var.c_str()));
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
return OutputVarType(yybson_mut_bool(doc_, _var));
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
return OutputVarType(yybson_mut_real(doc_, static_cast<double>(_var)));
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
return OutputVarType(yybson_mut_int(doc_, static_cast<int>(_var)));
} else {
static_assert(rfl::always_false_v<T>, "Unsupported type.");
}
}

public:
bson_t b;
};

} // namespace bson
} // namespace rfl

#endif // BSON_PARSER_HPP_
20 changes: 20 additions & 0 deletions include/rfl/bson/load.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef RFL_BSON_LOAD_HPP_
#define RFL_BSON_LOAD_HPP_

#include "../Result.hpp"
#include "../io/load_string.hpp"
#include "read.hpp"

namespace rfl {
namespace bson {

template <class T>
Result<T> load(const std::string& _fname) {
const auto read_bytes = [](const auto& _str) { return read<T>(_str); };
return rfl::io::load_string(_fname).and_then(read_bytes);
}

} // namespace bson
} // namespace rfl

#endif
40 changes: 40 additions & 0 deletions include/rfl/bson/read.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef RFL_BSON_READ_HPP_
#define RFL_BSON_READ_HPP_

#include <istream>
#include <string>

#include "../internal/wrap_in_rfl_array_t.hpp"
#include "Parser.hpp"
#include "Reader.hpp"

namespace rfl {
namespace bson {

using InputObjectType = typename Reader::InputObjectType;
using InputVarType = typename Reader::InputVarType;

/// Parses an object from a BSON var.
template <class T>
auto read(const InputVarType& _obj) {
const auto r = Reader();
return Parser<T>::read(r, _obj);
}

/// Parses an object from BSON using reflection.
template <class T>
Result<internal::wrap_in_rfl_array_t<T>> read(const std::vector<char>& _bytes) {
}

/// Parses an object from a stream.
template <class T>
auto read(std::istream& _stream) {
std::istreambuf_iterator<char> begin(_stream), end;
const auto bytes = std::vector<char>(begin, end);
return read<T>(bytes.data(), bytes.size());
}

} // namespace bson
} // namespace rfl

#endif
Loading

0 comments on commit e6b76a7

Please sign in to comment.