-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Co-authored-by: Patrick Urbanke <[email protected]>
- Loading branch information
1 parent
32a345d
commit 15f2c63
Showing
157 changed files
with
2,522 additions
and
556 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# Processors | ||
|
||
Processors can be used to apply transformations to struct serialization and deserialization. | ||
|
||
For instance, C++ [usually](http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rl-camel) uses `snake_case`, but JSON uses `camelCase`. One way to handle this is `rfl::Rename`, but a more automated way would be to use a *processor*: | ||
|
||
```cpp | ||
struct Person { | ||
std::string first_name; | ||
std::string last_name; | ||
std::vector<Person> children; | ||
}; | ||
|
||
const auto homer = | ||
Person{.first_name = "Homer", | ||
.last_name = "Simpson", | ||
.age = 45}; | ||
|
||
const auto json_string = | ||
rfl::json::write<rfl::SnakeCaseToCamelCase>(homer); | ||
|
||
const auto homer2 = | ||
rfl::json::read<Person, rfl::SnakeCaseToCamelCase>(json_string).value(); | ||
``` | ||
The resulting JSON string looks like this: | ||
```json | ||
{"firstName":"Homer","lastName":"Simpson","age":45} | ||
``` | ||
|
||
If you want `PascalCase` instead, you can use the appropriate processor: | ||
|
||
```cpp | ||
const auto json_string = | ||
rfl::json::write<rfl::SnakeCaseToPascalCase>(homer); | ||
|
||
const auto homer2 = | ||
rfl::json::read<Person, rfl::SnakeCaseToPascalCase>(json_string).value(); | ||
``` | ||
|
||
The resulting JSON string looks like this: | ||
|
||
```json | ||
{"FirstName":"Homer","LastName":"Simpson","Age":45} | ||
``` | ||
|
||
It is also possible to add the struct name as an addtional field, like this: | ||
|
||
```cpp | ||
const auto json_string = | ||
rfl::json::write<rfl::AddStructName<"type">>(homer); | ||
|
||
const auto homer2 = | ||
rfl::json::read<Person, rfl::AddStructName<"type">>(json_string).value(); | ||
``` | ||
|
||
The resulting JSON string looks like this: | ||
|
||
```json | ||
{"type":"Person","first_name":"Homer","last_name":"Simpson","age":45} | ||
``` | ||
|
||
You can also combine several processors: | ||
|
||
It is also possible to add the struct name as an addtional field, like this: | ||
|
||
```cpp | ||
const auto json_string = | ||
rfl::json::write<rfl::SnakeCaseToCamelCase, rfl::AddStructName<"type">>(homer); | ||
|
||
const auto homer2 = | ||
rfl::json::read<Person, rfl::SnakeCaseToCamelCase, rfl::AddStructName<"type">>(json_string).value(); | ||
``` | ||
|
||
The resulting JSON string looks like this: | ||
|
||
```json | ||
{"type":"Person","firstName":"Homer","lastName":"Simpson","age":45} | ||
``` | ||
|
||
When you have several processors, it is probably more convenient to combine them like this: | ||
|
||
```cpp | ||
using Processors = rfl::Processors< | ||
rfl::SnakeCaseToCamelCase, rfl::AddStructName<"type">>; | ||
|
||
const auto json_string = rfl::json::write<Processors>(homer); | ||
|
||
const auto homer2 = rfl::json::read<Person, Processors>(json_string).value(); | ||
``` | ||
|
||
The resulting JSON string looks like this: | ||
|
||
```json | ||
{"type":"Person","firstName":"Homer","lastName":"Simpson","age":45} | ||
``` | ||
|
||
## Writing your own processors | ||
|
||
In principle, writing your own processors is not very difficult. You need to define a struct, which takes has a static method called `process` taking a named tuple as an input and then returning a modified named tuple. The `process` method should accept the type of the original struct as a template parameter. | ||
|
||
```cpp | ||
struct MyOwnProcessor { | ||
template <class StructType> | ||
static auto process(auto&& _named_tuple) {...} | ||
}; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#ifndef RFL_ADDSTRUCTNAME_HPP_ | ||
#define RFL_ADDSTRUCTNAME_HPP_ | ||
|
||
#include <tuple> | ||
|
||
#include "Field.hpp" | ||
#include "Literal.hpp" | ||
#include "internal/StringLiteral.hpp" | ||
#include "internal/get_type_name.hpp" | ||
#include "internal/remove_namespaces.hpp" | ||
#include "make_named_tuple.hpp" | ||
|
||
namespace rfl { | ||
|
||
template <internal::StringLiteral field_name_> | ||
struct AddStructName { | ||
/// Adds the name of the struct as a new field. | ||
template <class StructType> | ||
static auto process(auto&& _view) { | ||
using LiteralType = Literal< | ||
internal::remove_namespaces<internal::get_type_name<StructType>()>()>; | ||
using FieldType = Field<field_name_, LiteralType>; | ||
const auto add_new_field = [](auto&&... _fields) { | ||
return make_named_tuple(FieldType(LiteralType()), std::move(_fields)...); | ||
}; | ||
return std::apply(add_new_field, std::move(_view.fields())); | ||
} | ||
}; | ||
|
||
} // namespace rfl | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#ifndef RFL_INTERNAL_PROCESSORS_HPP_ | ||
#define RFL_INTERNAL_PROCESSORS_HPP_ | ||
|
||
namespace rfl { | ||
|
||
template <class... Ps> | ||
struct Processors; | ||
|
||
template <> | ||
struct Processors<> { | ||
template <class T, class NamedTupleType> | ||
static auto process(NamedTupleType&& _named_tuple) { | ||
return _named_tuple; | ||
} | ||
}; | ||
|
||
template <class Head, class... Tail> | ||
struct Processors<Head, Tail...> { | ||
template <class T, class NamedTupleType> | ||
static auto process(NamedTupleType&& _named_tuple) { | ||
return Processors<Tail...>::template process<T>( | ||
Head::template process<T>(std::move(_named_tuple))); | ||
} | ||
}; | ||
|
||
} // namespace rfl | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#ifndef RFL_SNAKECASETOCAMELCASE_HPP_ | ||
#define RFL_SNAKECASETOCAMELCASE_HPP_ | ||
|
||
#include "Field.hpp" | ||
#include "internal/transform_snake_case.hpp" | ||
|
||
namespace rfl { | ||
|
||
struct SnakeCaseToCamelCase { | ||
public: | ||
/// Replaces all instances of snake_case field names with camelCase. | ||
template <class StructType> | ||
static auto process(auto&& _named_tuple) { | ||
const auto handle_one = []<class FieldType>(FieldType&& _f) { | ||
if constexpr (FieldType::name() != "xml_content") { | ||
return handle_one_field(std::move(_f)); | ||
} else { | ||
return std::move(_f); | ||
} | ||
}; | ||
return _named_tuple.transform(handle_one); | ||
} | ||
|
||
private: | ||
/// Applies the logic to a single field. | ||
template <class FieldType> | ||
static auto handle_one_field(FieldType&& _f) { | ||
using NewFieldType = | ||
Field<internal::transform_snake_case<FieldType::name_, | ||
/*capitalize=*/false>(), | ||
typename FieldType::Type>; | ||
return NewFieldType(_f.value()); | ||
} | ||
}; | ||
|
||
} // namespace rfl | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#ifndef RFL_SNAKECASETOPASCALCASE_HPP_ | ||
#define RFL_SNAKECASETOPASCALCASE_HPP_ | ||
|
||
#include "Field.hpp" | ||
#include "internal/transform_snake_case.hpp" | ||
|
||
namespace rfl { | ||
|
||
struct SnakeCaseToPascalCase { | ||
public: | ||
/// Replaces all instances of snake_case field names with PascalCase. | ||
template <class StructType> | ||
static auto process(auto&& _named_tuple) { | ||
const auto handle_one = []<class FieldType>(FieldType&& _f) { | ||
if constexpr (FieldType::name() != "xml_content") { | ||
return handle_one_field(std::move(_f)); | ||
} else { | ||
return std::move(_f); | ||
} | ||
}; | ||
return _named_tuple.transform(handle_one); | ||
} | ||
|
||
private: | ||
/// Applies the logic to a single field. | ||
template <class FieldType> | ||
static auto handle_one_field(FieldType&& _f) { | ||
using NewFieldType = | ||
Field<internal::transform_snake_case<FieldType::name_, | ||
/*capitalize=*/true>(), | ||
typename FieldType::Type>; | ||
return NewFieldType(_f.value()); | ||
} | ||
}; | ||
|
||
} // namespace rfl | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.