Skip to content

Commit ee1da7b

Browse files
authored
cbor supports std::optional; fixes #135 (#462)
1 parent 238a197 commit ee1da7b

File tree

7 files changed

+114
-53
lines changed

7 files changed

+114
-53
lines changed

include/rfl/cbor/Parser.hpp

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,6 @@
66
#include "Reader.hpp"
77
#include "Writer.hpp"
88

9-
namespace rfl::parsing {
10-
11-
/// CBOR requires us to explicitly set the number of fields in advance.
12-
/// Because of that, we require all of the fields and then set them to nullptr,
13-
/// if necessary.
14-
template <class ProcessorsType, class... FieldTypes>
15-
requires AreReaderAndWriter<cbor::Reader, cbor::Writer,
16-
NamedTuple<FieldTypes...>>
17-
struct Parser<cbor::Reader, cbor::Writer, NamedTuple<FieldTypes...>,
18-
ProcessorsType>
19-
: public NamedTupleParser<
20-
cbor::Reader, cbor::Writer,
21-
/*_ignore_empty_containers=*/false,
22-
/*_all_required=*/true,
23-
/*_no_field_names=*/ProcessorsType::no_field_names_, ProcessorsType,
24-
FieldTypes...> {};
25-
26-
template <class ProcessorsType, class... Ts>
27-
requires AreReaderAndWriter<cbor::Reader, cbor::Writer, rfl::Tuple<Ts...>>
28-
struct Parser<cbor::Reader, cbor::Writer, rfl::Tuple<Ts...>, ProcessorsType>
29-
: public TupleParser<cbor::Reader, cbor::Writer,
30-
/*_ignore_empty_containers=*/false,
31-
/*_all_required=*/true, ProcessorsType,
32-
rfl::Tuple<Ts...>> {};
33-
34-
template <class ProcessorsType, class... Ts>
35-
requires AreReaderAndWriter<cbor::Reader, cbor::Writer, std::tuple<Ts...>>
36-
struct Parser<cbor::Reader, cbor::Writer, std::tuple<Ts...>, ProcessorsType>
37-
: public TupleParser<cbor::Reader, cbor::Writer,
38-
/*_ignore_empty_containers=*/false,
39-
/*_all_required=*/true, ProcessorsType,
40-
std::tuple<Ts...>> {};
41-
42-
} // namespace rfl::parsing
43-
449
namespace rfl::cbor {
4510

4611
template <class T, class ProcessorsType>

include/rfl/cbor/Writer.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Writer {
4141

4242
OutputArrayType array_as_root(const size_t _size) const noexcept;
4343

44-
OutputObjectType object_as_root(const size_t _size) const noexcept;
44+
OutputObjectType object_as_root(const size_t) const noexcept;
4545

4646
OutputVarType null_as_root() const noexcept;
4747

@@ -57,11 +57,11 @@ class Writer {
5757
const size_t _size,
5858
OutputObjectType* _parent) const noexcept;
5959

60-
OutputObjectType add_object_to_array(const size_t _size,
60+
OutputObjectType add_object_to_array(const size_t,
6161
OutputArrayType* _parent) const noexcept;
6262

6363
OutputObjectType add_object_to_object(
64-
const std::string_view& _name, const size_t _size,
64+
const std::string_view& _name, const size_t,
6565
OutputObjectType* _parent) const noexcept;
6666

6767
template <class T>
@@ -90,7 +90,7 @@ class Writer {
9090
private:
9191
OutputArrayType new_array(const size_t _size) const noexcept;
9292

93-
OutputObjectType new_object(const size_t _size) const noexcept;
93+
OutputObjectType new_object() const noexcept;
9494

9595
template <class T>
9696
OutputVarType new_value(const T& _var) const noexcept {

src/rfl/cbor/Writer.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ Writer::OutputArrayType Writer::array_as_root(
1212
}
1313

1414
Writer::OutputObjectType Writer::object_as_root(
15-
const size_t _size) const noexcept {
16-
return new_object(_size);
15+
const size_t) const noexcept {
16+
return new_object();
1717
}
1818

1919
Writer::OutputVarType Writer::null_as_root() const noexcept {
@@ -34,15 +34,15 @@ Writer::OutputArrayType Writer::add_array_to_object(
3434
}
3535

3636
Writer::OutputObjectType Writer::add_object_to_array(
37-
const size_t _size, OutputArrayType* _parent) const noexcept {
38-
return new_object(_size);
37+
const size_t, OutputArrayType* _parent) const noexcept {
38+
return new_object();
3939
}
4040

4141
Writer::OutputObjectType Writer::add_object_to_object(
42-
const std::string_view& _name, const size_t _size,
42+
const std::string_view& _name, const size_t,
4343
OutputObjectType* _parent) const noexcept {
4444
encoder_->key(_name);
45-
return new_object(_size);
45+
return new_object();
4646
}
4747

4848
Writer::OutputVarType Writer::add_null_to_array(
@@ -71,8 +71,8 @@ Writer::OutputArrayType Writer::new_array(const size_t _size) const noexcept {
7171
return OutputArrayType{};
7272
}
7373

74-
Writer::OutputObjectType Writer::new_object(const size_t _size) const noexcept {
75-
encoder_->begin_object(_size);
74+
Writer::OutputObjectType Writer::new_object() const noexcept {
75+
encoder_->begin_object();
7676
return OutputObjectType{};
7777
}
7878

tests/cbor/test_error_messages.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ TEST(cbor, test_decode_error_without_exception) {
4747
});
4848

4949
// A proposal: A generic prefix, followed by the underlying library's error output
50-
const std::string expected = R"(Could not parse CBOR: An unknown type was found in the stream at position 1)";
50+
const std::string expected = R"(Found 4 errors:
51+
1) Field named 'firstName' not found.
52+
2) Field named 'lastName' not found.
53+
3) Field named 'birthday' not found.
54+
4) Field named 'children' not found.)";
5155
EXPECT_EQ(result.error().what(), expected);
5256

5357
EXPECT_TRUE(!result.has_value() && true);

tests/cbor/test_integers.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,21 @@ TEST(cbor, test_integers_signedness) {
1818

1919
std::vector<char> unsigned_buffer = rfl::cbor::write(Unsigned{BIG_INT});
2020
std::vector<unsigned char> unsigned_expected = {
21-
0xA1, 0x63, 0x75, 0x36, 0x34,
22-
0x1B, // Per RFC 8949, Initial byte '0x1B' indicates "unsigned integer (eight-byte uint64_t follows)"
21+
0xBF, 0x63, 0x75, 0x36, 0x34,
22+
0x1B, // Per RFC 8949, Initial byte '0x1B' indicates "unsigned integer (eight-byte uint64_t follows)"
2323
// See: https://www.rfc-editor.org/rfc/rfc8949.html#section-appendix.b
24-
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
24+
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
25+
0xFF
2526
};
2627

2728
EXPECT_EQ(std::vector<char>(unsigned_expected.begin(), unsigned_expected.end()), unsigned_buffer);
2829

2930
std::vector<char> signed_buffer = rfl::cbor::write(Signed{static_cast<int64_t>(BIG_INT)});
3031
std::vector<unsigned char> signed_expected = {
31-
0xA1, 0x63, 0x69, 0x36, 0x34,
32-
0x20 // Per RFC 8949, Initial byte '0x20' indicates "negative integer -1-0x00..-1-0x17 (-1..-24)"
32+
0xBF, 0x63, 0x69, 0x36, 0x34,
33+
0x20, // Per RFC 8949, Initial byte '0x20' indicates "negative integer -1-0x00..-1-0x17 (-1..-24)"
3334
// See: https://www.rfc-editor.org/rfc/rfc8949.html#section-appendix.b
35+
0xFF
3436
};
3537

3638
EXPECT_EQ(std::vector<char>(signed_expected.begin(), signed_expected.end()), signed_buffer);

tests/cbor/test_no_optionals.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <rfl.hpp>
2+
#include <rfl/cbor.hpp>
3+
#include <string>
4+
#include <vector>
5+
6+
#include "write_and_read.hpp"
7+
8+
namespace test_no_optionals {
9+
10+
struct Person {
11+
rfl::Rename<"firstName", std::string> first_name;
12+
rfl::Rename<"lastName", std::string> last_name = "Simpson";
13+
rfl::Rename<"children", std::optional<std::vector<Person>>> children;
14+
};
15+
16+
struct OptionalPerson {
17+
std::optional<Person> opt_person;
18+
};
19+
20+
struct Empty{};
21+
22+
TEST(cbor, test_no_optionals) {
23+
auto empty_struct_cbor = rfl::cbor::write<rfl::NoOptionals>(Empty{});
24+
auto no_person_cbor = rfl::cbor::write<rfl::NoOptionals>(OptionalPerson{});
25+
26+
EXPECT_NE(empty_struct_cbor.size(), no_person_cbor.size());
27+
}
28+
} // namespace test_no_optionals
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <iostream>
2+
#include <rfl.hpp>
3+
#include <rfl/cbor.hpp>
4+
#include <string>
5+
#include <vector>
6+
7+
#include "write_and_read.hpp"
8+
9+
namespace test_optional_fields {
10+
11+
struct Person {
12+
rfl::Rename<"firstName", std::string> first_name;
13+
rfl::Rename<"lastName", std::string> last_name = "Simpson";
14+
rfl::Rename<"children", std::optional<std::vector<Person>>> children;
15+
16+
bool operator==(const Person& other) const {
17+
return first_name.get() == other.first_name.get() &&
18+
last_name.get() == other.last_name.get() &&
19+
children.get() == other.children.get();
20+
}
21+
};
22+
23+
struct OptionalPerson {
24+
std::optional<Person> opt_person;
25+
};
26+
27+
struct Empty{};
28+
29+
TEST(cbor, test_optional_no_fields) {
30+
auto empty_struct_cbor = rfl::cbor::write(Empty{});
31+
auto no_person_cbor = rfl::cbor::write(OptionalPerson{});
32+
33+
EXPECT_EQ(empty_struct_cbor.size(), no_person_cbor.size());
34+
}
35+
36+
TEST(cbor, test_optional_fields) {
37+
const auto bart = Person{.first_name = "Bart"};
38+
39+
const auto lisa = Person{.first_name = "Lisa"};
40+
41+
const auto maggie = Person{.first_name = "Maggie"};
42+
43+
const auto homer =
44+
Person{.first_name = "Homer",
45+
.children = std::vector<Person>({bart, lisa, maggie})};
46+
47+
const OptionalPerson homer_optional = {
48+
.opt_person = homer
49+
};
50+
51+
const auto homer_optional_cbor = rfl::cbor::write(homer_optional);
52+
const auto res = rfl::cbor::read<OptionalPerson>(homer_optional_cbor);
53+
54+
EXPECT_TRUE(res && true) << "Test failed on read. Error: "
55+
<< res.error().what();
56+
57+
const auto actual_homer = res.value();
58+
59+
EXPECT_TRUE(actual_homer.opt_person.has_value());
60+
EXPECT_EQ(*actual_homer.opt_person, homer);
61+
}
62+
} // namespace test_optional_fields

0 commit comments

Comments
 (0)