Skip to content

Commit f2d9779

Browse files
Added CBOR support; #53
1 parent 5a34c9b commit f2d9779

File tree

86 files changed

+2334
-23
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+2334
-23
lines changed

.github/workflows/test.yaml

+4-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ jobs:
2020
- name: Run test
2121
run: |
2222
g++ --version
23-
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
23+
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_CBOR=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
2424
cmake --build build -j 4
2525
./build/tests/bson/reflect-cpp-bson-tests
26+
./build/tests/cbor/reflect-cpp-cbor-tests
2627
./build/tests/flexbuffers/reflect-cpp-flexbuffers-tests
2728
./build/tests/json/reflect-cpp-json-tests
2829
./build/tests/xml/reflect-cpp-xml-tests
@@ -52,9 +53,10 @@ jobs:
5253
CXX: clang++
5354
run: |
5455
clang --version
55-
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
56+
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_CBOR=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
5657
cmake --build build -j 4
5758
./build/tests/bson/reflect-cpp-bson-tests
59+
./build/tests/cbor/reflect-cpp-cbor-tests
5860
./build/tests/flexbuffers/reflect-cpp-flexbuffers-tests
5961
./build/tests/json/reflect-cpp-json-tests
6062
./build/tests/xml/reflect-cpp-xml-tests

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
# Output files
4444
*.bson
45+
*.cbor
4546
*.json
4647
*.fb
4748
*.xml

CMakeLists.txt

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ cmake_minimum_required(VERSION 3.15)
22

33
option(REFLECTCPP_BUILD_SHARED "Build shared library" OFF)
44
option(REFLECTCPP_BSON "Enable BSON support" OFF)
5+
option(REFLECTCPP_CBOR "Enable CBOR support" OFF)
56
option(REFLECTCPP_FLEXBUFFERS "Enable flexbuffers support" OFF)
67
option(REFLECTCPP_XML "Enable XML support" OFF)
78
option(REFLECTCPP_YAML "Enable YAML support" OFF)
89

910
option(REFLECTCPP_BUILD_TESTS "Build tests" OFF)
1011

1112
# enable vcpkg if require features other than JSON
12-
if (REFLECTCPP_BSON OR REFLECTCPP_FLEXBUFFERS OR REFLECTCPP_XML OR REFLECTCPP_YAML)
13+
if (REFLECTCPP_BSON OR REFLECTCPP_CBOR OR REFLECTCPP_FLEXBUFFERS OR REFLECTCPP_XML OR REFLECTCPP_YAML)
1314
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake CACHE STRING "Vcpkg toolchain file")
1415
endif ()
1516

@@ -30,6 +31,14 @@ if (REFLECTCPP_BSON)
3031
target_link_libraries(reflectcpp PRIVATE $<IF:$<TARGET_EXISTS:mongo::bson_static>,mongo::bson_static,mongo::bson_shared>)
3132
endif ()
3233

34+
if (REFLECTCPP_CBOR)
35+
if (MSVC)
36+
target_link_libraries(reflectcpp PRIVATE "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/tinycbor${CMAKE_STATIC_LIBRARY_SUFFIX}")
37+
else ()
38+
target_link_libraries(reflectcpp PRIVATE "${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/libtinycbor${CMAKE_STATIC_LIBRARY_SUFFIX}")
39+
endif ()
40+
endif ()
41+
3342
if (REFLECTCPP_FLEXBUFFERS)
3443
find_package(flatbuffers CONFIG REQUIRED)
3544
target_link_libraries(reflectcpp INTERFACE flatbuffers::flatbuffers)

README.md

+25-7
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ The following table lists the serialization formats currently supported by refle
3232
| Format | Library | Version | License | Remarks |
3333
|--------------|------------------------------------------------------|--------------|------------| -----------------------------------------------------|
3434
| JSON | [yyjson](https://github.com/ibireme/yyjson) | >= 0.8.0 | MIT | out-of-the-box support, included in this repository |
35-
| BSON | [libbson](https://github.com/mongodb/libbson) | >= 1.25.1 | Apache 2.0 | |
36-
| flexbuffers | [flatbuffers](https://github.com/google/flatbuffers) | >= 23.5.26 | Apache 2.0 | |
37-
| XML | [pugixml](https://github.com/zeux/pugixml) | >= 1.14 | MIT | |
38-
| YAML | [yaml-cpp](https://github.com/jbeder/yaml-cpp) | >= 0.8.0 | MIT | |
35+
| BSON | [libbson](https://github.com/mongodb/libbson) | >= 1.25.1 | Apache 2.0 | JSON-like binary format |
36+
| CBOR | [tinycbor](https://github.com/intel/tinycbor) | >= 0.6.0 | MIT | JSON-like binary format |
37+
| flexbuffers | [flatbuffers](https://github.com/google/flatbuffers) | >= 23.5.26 | Apache 2.0 | Schema-less version of flatbuffers, binary format |
38+
| XML | [pugixml](https://github.com/zeux/pugixml) | >= 1.14 | MIT | Textual format used in many legacy projects |
39+
| YAML | [yaml-cpp](https://github.com/jbeder/yaml-cpp) | >= 0.8.0 | MIT | Textual format with an emphasis on readability |
3940

4041
Support for more serialization formats is in development. Refer to the [issues](https://github.com/getml/reflect-cpp/issues) for details.
4142

@@ -69,8 +70,7 @@ The resulting JSON string looks like this:
6970
{"first_name":"Homer","last_name":"Simpson","age":45}
7071
```
7172

72-
Or you can use another format, such as YAML. This will work for just about
73-
any example in the entire documentation or any supported format.
73+
Or you can use another format, such as YAML.
7474

7575
```cpp
7676
#include <rfl/yaml.hpp>
@@ -89,6 +89,21 @@ last_name: Simpson
8989
age: 45
9090
```
9191
92+
This will work for just about any example in the entire documentation
93+
and any supported format:
94+
95+
```cpp
96+
rfl::bson::write(homer);
97+
rfl::cbor::write(homer);
98+
rfl::flexbuf::write(homer);
99+
rfl::xml::write(homer);
100+
101+
rfl::bson::read<Person>(bson_bytes);
102+
rfl::cbor::read<Person>(cbor_bytes);
103+
rfl::flexbuf::read<Person>(flexbuf_bytes);
104+
rfl::xml::read<Person>(xml_string);
105+
```
106+
92107
## More Comprehensive Example
93108

94109
```cpp
@@ -450,6 +465,7 @@ To use reflect-cpp in your project:
450465
add_subdirectory(reflect-cpp) # Add this project as a subdirectory
451466
452467
set(REFLECTCPP_BSON ON) # Optional
468+
set(REFLECTCPP_CBOR ON) # Optional
453469
set(REFLECTCPP_FLEXBUFFERS ON) # Optional
454470
set(REFLECTCPP_XML ON) # Optional
455471
set(REFLECTCPP_YAML ON) # Optional
@@ -496,14 +512,16 @@ git submodule update --init
496512
./vcpkg/bootstrap-vcpkg.bat # Windows
497513
# You may be prompted to install additional dependencies.
498514

499-
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
515+
cmake -S . -B build -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_BSON=ON -DREFLECTCPP_CBOR=ON -DREFLECTCPP_FLEXBUFFERS=ON -DREFLECTCPP_XML=ON -DREFLECTCPP_YAML=ON -DCMAKE_BUILD_TYPE=Release
500516
cmake --build build -j 4 # gcc, clang
501517
cmake --build build --config Release -j 4 # MSVC
502518
```
503519

504520
To run the tests, do the following:
505521

506522
```
523+
./build/tests/bson/reflect-cpp-bson-tests
524+
./build/tests/cbor/reflect-cpp-cbor-tests
507525
./build/tests/flexbuffers/reflect-cpp-flexbuffers-tests
508526
./build/tests/json/reflect-cpp-json-tests
509527
./build/tests/xml/reflect-cpp-xml-tests

docs/README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@
5858

5959
5.2) [BSON](https://github.com/getml/reflect-cpp/blob/main/docs/bson.md)
6060

61-
5.3) [flexbuffers](https://github.com/getml/reflect-cpp/blob/main/docs/flexbuffers.md)
61+
5.3) [CBOR](https://github.com/getml/reflect-cpp/blob/main/docs/cbor.md)
6262

63-
5.4) [XML](https://github.com/getml/reflect-cpp/blob/main/docs/xml.md)
63+
5.4) [flexbuffers](https://github.com/getml/reflect-cpp/blob/main/docs/flexbuffers.md)
6464

65-
5.5) [YAML](https://github.com/getml/reflect-cpp/blob/main/docs/yaml.md)
65+
5.5) [XML](https://github.com/getml/reflect-cpp/blob/main/docs/xml.md)
66+
67+
5.6) [YAML](https://github.com/getml/reflect-cpp/blob/main/docs/yaml.md)
6668

6769
## 6) Advanced topics
6870

docs/cbor.md

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# CBOR
2+
3+
For CBOR support, you must also include the header `<rfl/cbor.hpp>` and link to the tinycbor library (https://github.com/intel/tinycbor).
4+
5+
CBOR or Concise Binary Object Representation, is a JSON-like binary format with an emphasis on small binary sizes.
6+
7+
## Reading and writing
8+
9+
Suppose you have a struct like this:
10+
11+
```cpp
12+
struct Person {
13+
std::string first_name;
14+
std::string last_name;
15+
rfl::Timestamp<"%Y-%m-%d"> birthday;
16+
std::vector<Person> children;
17+
};
18+
```
19+
20+
A `Person` can be turned into a bytes vector like this:
21+
22+
```cpp
23+
const auto person = Person{...};
24+
const std::vector<char> bytes = rfl::cbor::write(person);
25+
```
26+
27+
You can parse bytes like this:
28+
29+
```cpp
30+
const rfl::Result<Person> result = rfl::cbor::read<Person>(bytes);
31+
```
32+
33+
## Loading and saving
34+
35+
You can also load and save to disc using a very similar syntax:
36+
37+
```cpp
38+
const rfl::Result<Person> result = rfl::cbor::load<Person>("/path/to/file.cbor");
39+
40+
const auto person = Person{...};
41+
rfl::cbor::save("/path/to/file.cbor", person);
42+
```
43+
44+
## Reading from and writing into streams
45+
46+
You can also read from and write into any `std::istream` and `std::ostream` respectively.
47+
48+
```cpp
49+
const rfl::Result<Person> result = rfl::cbor::read<Person>(my_istream);
50+
51+
const auto person = Person{...};
52+
rfl::cbor::write(person, my_ostream);
53+
```
54+
55+
Note that `std::cout` is also an ostream, so this works as well:
56+
57+
```cpp
58+
rfl::cbor::write(person, std::cout) << std::endl;
59+
```
60+
61+
(Since CBOR is a binary format, the readability of this will be limited, but it might be useful for debugging).
62+
63+
## Custom constructors
64+
65+
One of the great things about C++ is that it gives you control over
66+
when and how you code is compiled.
67+
68+
For large and complex systems of structs, it is often a good idea to split up
69+
your code into smaller compilation units. You can do so using custom constructors.
70+
71+
For the CBOR format, these must be a static function on your struct or class called
72+
`from_cbor` that take a `rfl::cbor::Reader::InputVarType` as input and return
73+
the class or the class wrapped in `rfl::Result`.
74+
75+
In your header file you can write something like this:
76+
77+
```cpp
78+
struct Person {
79+
rfl::Rename<"firstName", std::string> first_name;
80+
rfl::Rename<"lastName", std::string> last_name;
81+
rfl::Timestamp<"%Y-%m-%d"> birthday;
82+
83+
using InputVarType = typename rfl::cbor::Reader::InputVarType;
84+
static rfl::Result<Person> from_cbor(const InputVarType& _obj);
85+
};
86+
```
87+
88+
And in your source file, you implement `from_cbor` as follows:
89+
90+
```cpp
91+
rfl::Result<Person> Person::from_cbor(const InputVarType& _obj) {
92+
const auto from_nt = [](auto&& _nt) {
93+
return rfl::from_named_tuple<Person>(std::move(_nt));
94+
};
95+
return rfl::cbor::read<rfl::named_tuple_t<Person>>(_obj)
96+
.transform(from_nt);
97+
}
98+
```
99+
100+
This will force the compiler to only compile the CBOR parsing when the source file is compiled.

include/rfl/cbor.hpp

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef RFL_CBOR_HPP_
2+
#define RFL_CBOR_HPP_
3+
4+
#include "cbor/Parser.hpp"
5+
#include "cbor/Reader.hpp"
6+
#include "cbor/Writer.hpp"
7+
#include "cbor/load.hpp"
8+
#include "cbor/read.hpp"
9+
#include "cbor/save.hpp"
10+
#include "cbor/write.hpp"
11+
12+
#endif

include/rfl/cbor/Parser.hpp

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef RFL_CBOR_PARSER_HPP_
2+
#define RFL_CBOR_PARSER_HPP_
3+
4+
#include "../parsing/Parser.hpp"
5+
#include "Reader.hpp"
6+
#include "Writer.hpp"
7+
8+
namespace rfl {
9+
namespace parsing {
10+
11+
/// CBOR requires us to explicitly set the number of fields in advance. Because
12+
/// of that, we require all of the fields and then set them to nullptr, if
13+
/// necessary.
14+
template <class... FieldTypes>
15+
requires AreReaderAndWriter<cbor::Reader, cbor::Writer,
16+
NamedTuple<FieldTypes...>>
17+
struct Parser<cbor::Reader, cbor::Writer, NamedTuple<FieldTypes...>>
18+
: public NamedTupleParser<cbor::Reader, cbor::Writer,
19+
/*_ignore_empty_containers=*/false,
20+
/*_all_required=*/true, FieldTypes...> {
21+
};
22+
23+
template <class... Ts>
24+
requires AreReaderAndWriter<cbor::Reader, cbor::Writer, std::tuple<Ts...>>
25+
struct Parser<cbor::Reader, cbor::Writer, std::tuple<Ts...>>
26+
: public TupleParser<cbor::Reader, cbor::Writer,
27+
/*_ignore_empty_containers=*/false,
28+
/*_all_required=*/true, Ts...> {
29+
};
30+
31+
} // namespace parsing
32+
} // namespace rfl
33+
34+
namespace rfl {
35+
namespace cbor {
36+
37+
template <class T>
38+
using Parser = parsing::Parser<Reader, Writer, T>;
39+
40+
}
41+
} // namespace rfl
42+
43+
#endif

0 commit comments

Comments
 (0)