Skip to content

Commit

Permalink
Merge branch 'main' into cli_negative_number
Browse files Browse the repository at this point in the history
  • Loading branch information
filippobrizzi authored Sep 30, 2024
2 parents f706b9d + bc1ff5b commit 41fe153
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 75 deletions.
20 changes: 10 additions & 10 deletions modules/serdes/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,24 @@ find_package(range-v3 REQUIRED)

# library sources
set(SOURCES
src/serdes.cpp
src/type_info.cpp
src/dynamic_deserializer.cpp
src/protobuf/buffers.cpp
src/protobuf/dynamic_deserializer.cpp
src/protobuf/buffers.cpp
src/protobuf/protobuf.cpp
src/protobuf/enums.cpp
src/protobuf/protobuf_internal.cpp
src/protobuf/protobuf.cpp
src/dynamic_deserializer.cpp
src/serdes.cpp
src/type_info.cpp
README.md
include/hephaestus/serdes/dynamic_deserializer.h
include/hephaestus/serdes/serdes.h
include/hephaestus/serdes/type_info.h
include/hephaestus/serdes/protobuf/buffers.h
include/hephaestus/serdes/protobuf/concepts.h
include/hephaestus/serdes/protobuf/buffers.h
include/hephaestus/serdes/protobuf/dynamic_deserializer.h
include/hephaestus/serdes/protobuf/protobuf.h
include/hephaestus/serdes/protobuf/enums.h
include/hephaestus/serdes/protobuf/protobuf_internal.h
include/hephaestus/serdes/protobuf/protobuf.h
include/hephaestus/serdes/dynamic_deserializer.h
include/hephaestus/serdes/serdes.h
include/hephaestus/serdes/type_info.h
)

# library target
Expand Down
117 changes: 117 additions & 0 deletions modules/serdes/include/hephaestus/serdes/protobuf/enums.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//=================================================================================================
// Copyright (C) 2023-2024 HEPHAESTUS Contributors
//=================================================================================================

#pragma once

#include <string_view>
#include <type_traits>
#include <unordered_map>

#include <fmt/core.h>
#include <magic_enum.hpp>

#include "hephaestus/utils/concepts.h"
#include "hephaestus/utils/exception.h"
#include "hephaestus/utils/format/format.h"
#include "hephaestus/utils/string/string_literal.h"
#include "hephaestus/utils/string/string_utils.h"

namespace heph::serdes::protobuf {

template <EnumType ProtoT, EnumType T>
[[nodiscard]] auto toProtoEnum(const T& enum_value) -> ProtoT;

template <EnumType ProtoT, EnumType T>
auto fromProto(const ProtoT& proto_enum_value, T& enum_value) -> void;

//=================================================================================================
// Implementation
//=================================================================================================

namespace internal {
template <EnumType ProtoT>
[[nodiscard]] auto getProtoPrefix() -> std::string {
const auto enum_type_name = magic_enum::enum_type_name<ProtoT>();

// Underscores indicate nested proto enums, no underscore indicates a top-level proto enum.
if (const auto underscore_pos = std::find(enum_type_name.begin(), enum_type_name.end(), '_');
underscore_pos == enum_type_name.end()) {
// Top-level enums use the enum name as a prefix: ENUM_NAME_ENUM_VALUE.
return utils::string::toScreamingSnakeCase(enum_type_name);
}

// Nested enums have a prefix, and enum values are separated by an underscore:
// ClassName_EnumName_ENUM_VALUE.
return fmt::format("{}", enum_type_name);
}

/// Convert between enums and their protobuf counterparts. The following convention is used:
/// enum class FooExternalEnum : { BAR1, BAR2 };
/// struct Foo {
/// enum class InternalEnum : { BAR1, BAR2 };
/// };
/// will have a protobuf counterpart
/// enum FooExternalEnum : { FOO_EXTERNAL_ENUM_BAR1, FOO_EXTERNAL_ENUM_BAR2 };
/// enum Foo_InternalEnum : { Foo_InternalEnum_BAR1, Foo_InternalEnum_BAR2 };
template <EnumType ProtoT, EnumType T>
[[nodiscard]] auto getAsProtoEnum(T e) -> ProtoT {
const auto proto_enum_name = fmt::format("{}_{}", getProtoPrefix<ProtoT>(), magic_enum::enum_name(e));
const auto proto_enum = magic_enum::enum_cast<ProtoT>(proto_enum_name);

heph::throwExceptionIf<heph::InvalidParameterException>(
!proto_enum.has_value(),
fmt::format("The proto enum does not contain the requested key {}. Proto enum keys are\n{}",
proto_enum_name, utils::format::toString(magic_enum::enum_names<ProtoT>())));

return proto_enum.value(); // NOLINT(bugprone-unchecked-optional-access)
}

template <EnumType ProtoT, EnumType T>
[[nodiscard]] auto createEnumLookupTable() -> std::unordered_map<T, ProtoT> {
std::unordered_map<T, ProtoT> lookup_table;

for (const auto& e : magic_enum::enum_values<T>()) {
lookup_table[e] = getAsProtoEnum<ProtoT, T>(e);
}

return lookup_table;
}

/// @brief Create inverse lookup table. Unique values are guaranteed by the enum layout.
template <EnumType ProtoT, EnumType T>
[[nodiscard]] auto createInverseLookupTable(const std::unordered_map<T, ProtoT>& enum_to_proto_enum)
-> std::unordered_map<ProtoT, T> {
std::unordered_map<ProtoT, T> proto_enum_to_enum;

for (const auto& kvp : enum_to_proto_enum) {
proto_enum_to_enum.insert({ kvp.second, kvp.first });
}

return proto_enum_to_enum;
}
} // namespace internal

template <EnumType ProtoT, EnumType T>
auto toProtoEnum(const T& enum_value) -> ProtoT {
static const auto enum_to_proto_enum = internal::createEnumLookupTable<ProtoT, T>();
const auto it = enum_to_proto_enum.find(enum_value);
throwExceptionIf<InvalidDataException>(
it == enum_to_proto_enum.end(),
fmt::format("Enum {} not found in the lookup table", utils::format::toString(enum_value)));
return it->second;
}

template <EnumType ProtoT, EnumType T>
auto fromProto(const ProtoT& proto_enum_value, T& enum_value) -> void {
static const auto proto_enum_value_to_enum =
internal::createInverseLookupTable(internal::createEnumLookupTable<ProtoT, T>());
const auto it = proto_enum_value_to_enum.find(proto_enum_value);
throwExceptionIf<InvalidDataException>(
it == proto_enum_value_to_enum.end(),
fmt::format("Enum {} not found in the lookup table", utils::format::toString(proto_enum_value)));
enum_value = it->second;
;
}

} // namespace heph::serdes::protobuf
5 changes: 5 additions & 0 deletions modules/serdes/src/protobuf/enums.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//=================================================================================================
// Copyright (C) 2023-2024 HEPHAESTUS Contributors
//=================================================================================================

#include "hephaestus/serdes/protobuf/enums.h" // NOLINT(misc-include-cleaner)
7 changes: 5 additions & 2 deletions modules/types/include/hephaestus/types/dummy_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,22 @@ struct DummyPrimitivesType {

auto operator<<(std::ostream& os, const DummyPrimitivesType& dummy_primitives_type) -> std::ostream&;

enum class ExternalDummyEnum : int8_t { A, B, C, D, E, F, G };

/// @brief Collection of non-primitive types for testing purposes.
/// NOTE: the data needs to be Protobuf serializable
/// NOTE: missing generic non-primitive types can be added to increase the test coverage
struct DummyType {
enum class DummyEnum : int8_t { A, B, C, D, E, F, G };
enum class InternalDummyEnum : int8_t { ALPHA };

[[nodiscard]] auto operator==(const DummyType&) const -> bool = default;

[[nodiscard]] static auto random(std::mt19937_64& mt) -> DummyType;

DummyPrimitivesType dummy_primitives_type{};

DummyEnum dummy_enum{};
InternalDummyEnum internal_dummy_enum{};
ExternalDummyEnum external_dummy_enum{};

std::string dummy_string{};
std::vector<int32_t> dummy_vector{};
Expand Down
8 changes: 4 additions & 4 deletions modules/types/src/dummy_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#include <iostream>
#include <random>

#include <magic_enum.hpp>

#include "hephaestus/random/random_object_creator.h"
#include "hephaestus/utils/format/format.h"

Expand Down Expand Up @@ -48,15 +46,17 @@ auto operator<<(std::ostream& os, const DummyPrimitivesType& dummy_primitives_ty

auto DummyType::random(std::mt19937_64& mt) -> DummyType {
return { .dummy_primitives_type = random::random<decltype(dummy_primitives_type)>(mt),
.dummy_enum = random::random<decltype(dummy_enum)>(mt),
.internal_dummy_enum = random::random<decltype(internal_dummy_enum)>(mt),
.external_dummy_enum = random::random<decltype(external_dummy_enum)>(mt),
.dummy_string = random::random<decltype(dummy_string)>(mt),
.dummy_vector = random::random<decltype(dummy_vector)>(mt) };
}

auto operator<<(std::ostream& os, const DummyType& dummy_type) -> std::ostream& {
return os << "DummyType{\n"
<< " dummy_primitives_type={" << dummy_type.dummy_primitives_type << "}\n"
<< " dummy_enum=" << magic_enum::enum_name(dummy_type.dummy_enum) << "\n"
<< " internal_dummy_enum=" << utils::format::toString(dummy_type.internal_dummy_enum) << "\n"
<< " external_dummy_enum=" << utils::format::toString(dummy_type.external_dummy_enum) << "\n"
<< " dummy_string=" << dummy_type.dummy_string << "\n"
<< " dummy_vector=" << utils::format::toString(dummy_type.dummy_vector) << "\n"
<< "}";
Expand Down
27 changes: 16 additions & 11 deletions modules/types_proto/proto/hephaestus/types/proto/dummy_type.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ syntax = "proto3";

package heph.types.proto;

enum DummyTypeDummyEnum {
A = 0;
B = 1;
C = 2;
D = 3;
E = 4;
F = 5;
G = 6;
enum DummyTypeExternalDummyEnum {
DUMMY_TYPE_EXTERNAL_DUMMY_ENUM_A = 0;
DUMMY_TYPE_EXTERNAL_DUMMY_ENUM_B = 1;
DUMMY_TYPE_EXTERNAL_DUMMY_ENUM_C = 2;
DUMMY_TYPE_EXTERNAL_DUMMY_ENUM_D = 3;
DUMMY_TYPE_EXTERNAL_DUMMY_ENUM_E = 4;
DUMMY_TYPE_EXTERNAL_DUMMY_ENUM_F = 5;
DUMMY_TYPE_EXTERNAL_DUMMY_ENUM_G = 6;
}

message DummyPrimitivesType {
Expand All @@ -34,10 +34,15 @@ message DummyPrimitivesType {
}

message DummyType {
enum InternalDummyEnum {
ALPHA = 0;
}

DummyPrimitivesType dummy_primitives_type = 1;

DummyTypeDummyEnum dummy_enum = 2;
InternalDummyEnum internal_dummy_enum = 2;
DummyTypeExternalDummyEnum external_dummy_enum = 3;

string dummy_string = 3;
repeated int32 dummy_vector = 4;
string dummy_string = 4;
repeated int32 dummy_vector = 5;
}
55 changes: 8 additions & 47 deletions modules/types_proto/src/dummy_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <cstddef>
#include <vector>

#include "hephaestus/serdes/protobuf/enums.h"
#include "hephaestus/types/dummy_type.h"
#include "hephaestus/types/proto/dummy_type.pb.h"

Expand Down Expand Up @@ -35,51 +36,6 @@ auto fromProto(const google::protobuf::RepeatedField<ProtoT>& proto_repeated_fie
}
}

//=================================================================================================
// DummyTypeDummyEnum
//=================================================================================================
auto getAsProto(const DummyType::DummyEnum& dummy_enum) -> proto::DummyTypeDummyEnum {
switch (dummy_enum) {
case DummyType::DummyEnum::A:
return proto::DummyTypeDummyEnum::A;
case DummyType::DummyEnum::B:
return proto::DummyTypeDummyEnum::B;
case DummyType::DummyEnum::C:
return proto::DummyTypeDummyEnum::C;
case DummyType::DummyEnum::D:
return proto::DummyTypeDummyEnum::D;
case DummyType::DummyEnum::E:
return proto::DummyTypeDummyEnum::E;
case DummyType::DummyEnum::F:
return proto::DummyTypeDummyEnum::F;
case DummyType::DummyEnum::G:
return proto::DummyTypeDummyEnum::G;
default: // A default case is needed to suppress warnings about unhandled enum values.
return {};
}
}

auto getFromProto(const proto::DummyTypeDummyEnum& proto_dummy_enum) -> DummyType::DummyEnum {
switch (proto_dummy_enum) {
case proto::DummyTypeDummyEnum::A:
return DummyType::DummyEnum::A;
case proto::DummyTypeDummyEnum::B:
return DummyType::DummyEnum::B;
case proto::DummyTypeDummyEnum::C:
return DummyType::DummyEnum::C;
case proto::DummyTypeDummyEnum::D:
return DummyType::DummyEnum::D;
case proto::DummyTypeDummyEnum::E:
return DummyType::DummyEnum::E;
case proto::DummyTypeDummyEnum::F:
return DummyType::DummyEnum::F;
case proto::DummyTypeDummyEnum::G:
return DummyType::DummyEnum::G;
default: // A default case is needed to capture values autogenerated by protobuf.
return {};
}
}

//=================================================================================================
// DummyPrimitivesType
//=================================================================================================
Expand Down Expand Up @@ -126,10 +82,14 @@ auto fromProto(const proto::DummyPrimitivesType& proto_dummy_primitives_type,
//=================================================================================================
// DummyType
//=================================================================================================

auto toProto(proto::DummyType& proto_dummy_type, const DummyType& dummy_type) -> void {
toProto(*proto_dummy_type.mutable_dummy_primitives_type(), dummy_type.dummy_primitives_type);

proto_dummy_type.set_dummy_enum(getAsProto(dummy_type.dummy_enum));
proto_dummy_type.set_internal_dummy_enum(
serdes::protobuf::toProtoEnum<proto::DummyType::InternalDummyEnum>(dummy_type.internal_dummy_enum));
proto_dummy_type.set_external_dummy_enum(
serdes::protobuf::toProtoEnum<proto::DummyTypeExternalDummyEnum>(dummy_type.external_dummy_enum));

proto_dummy_type.set_dummy_string(dummy_type.dummy_string);

Expand All @@ -139,7 +99,8 @@ auto toProto(proto::DummyType& proto_dummy_type, const DummyType& dummy_type) ->
auto fromProto(const proto::DummyType& proto_dummy_type, DummyType& dummy_type) -> void {
fromProto(proto_dummy_type.dummy_primitives_type(), dummy_type.dummy_primitives_type);

dummy_type.dummy_enum = getFromProto(proto_dummy_type.dummy_enum());
serdes::protobuf::fromProto(proto_dummy_type.internal_dummy_enum(), dummy_type.internal_dummy_enum);
serdes::protobuf::fromProto(proto_dummy_type.external_dummy_enum(), dummy_type.external_dummy_enum);

dummy_type.dummy_string = proto_dummy_type.dummy_string();

Expand Down
3 changes: 2 additions & 1 deletion modules/types_proto/tests/serialization_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ TYPED_TEST(SerializationTests, TestSerialization) {
const auto value = random::random<TypeParam>(mt);

TypeParam value_des{};
if constexpr (!std::is_same_v<TypeParam, bool>) { // sample space of bool is too small
if constexpr (!std::is_same_v<TypeParam, bool> || !std::is_same_v<TypeParam, int8_t> ||
!std::is_same_v<TypeParam, uint8_t>) { // sample space of bool is too small
EXPECT_NE(value, value_des);
}

Expand Down

0 comments on commit 41fe153

Please sign in to comment.