From e1a46cb61decb044f6ab0a58a77211beb3630340 Mon Sep 17 00:00:00 2001 From: "Ralph J. Steinhagen" <46007894+RalphSteinhagen@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:37:27 +0100 Subject: [PATCH] refactored refl-cpp and (de-)serialiser to be an optional dependency (#113) * fixed some implicit conversion warnings * suppressed clang and gcc warnings for external dependencies N.B. GR4 uses now it's own mini reflection library and yaml-(de-)serialiser -> perhaps this could be eventually upstreamed as well. Signed-off-by: Ralph J. Steinhagen --- bench/bm_pmt_dict_pack_unpack.cpp | 16 +- bench/bm_pmt_dict_ref.cpp | 16 +- bench/bm_pmt_serialize_uvec.cpp | 17 +- include/pmtv/meson.build | 1 + include/pmtv/pmt.hpp | 598 ++++------------------------ include/pmtv/serialiser.hpp | 431 ++++++++++++++++++++ python/pmtv/bindings/pmt_python.cc | 7 +- python/pmtv/bindings/pmtv_python.cc | 9 +- test/qa_map.cpp | 63 ++- test/qa_reflection.cpp | 1 + test/qa_scalar.cpp | 93 ++--- test/qa_string.cpp | 1 + test/qa_uniform_vector.cpp | 1 + test/qa_vector_of_pmts.cpp | 15 +- 14 files changed, 641 insertions(+), 628 deletions(-) create mode 100644 include/pmtv/serialiser.hpp diff --git a/bench/bm_pmt_dict_pack_unpack.cpp b/bench/bm_pmt_dict_pack_unpack.cpp index 6eba198..31faa9d 100644 --- a/bench/bm_pmt_dict_pack_unpack.cpp +++ b/bench/bm_pmt_dict_pack_unpack.cpp @@ -2,10 +2,24 @@ #include #include +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push // ignore warning of external libraries that from this lib-context we do not have any control over +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wold-style-cast" +#pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion" +#ifndef __clang__ // only for GCC, not Clang +#pragma GCC diagnostic ignored "-Wuseless-cast" +#endif +#endif + #include "CLI/App.hpp" #include "CLI/Config.hpp" #include "CLI/Formatter.hpp" +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + #include #include @@ -74,7 +88,7 @@ int main(int argc, char* argv[]) auto valid = run_test(samples, items); auto t2 = std::chrono::steady_clock::now(); - auto time = 1e-9 * static_cast(std::chrono::duration_cast(t2 - t1).count()); + auto time = static_cast(1e-9) * static_cast(std::chrono::duration_cast(t2 - t1).count()); std::cout << "[PROFILE_TIME]" << time << "[PROFILE_TIME]" << std::endl; std::cout << "[PROFILE_VALID]" << valid << "[PROFILE_VALID]" << std::endl; diff --git a/bench/bm_pmt_dict_ref.cpp b/bench/bm_pmt_dict_ref.cpp index 6f057c8..b82eeec 100644 --- a/bench/bm_pmt_dict_ref.cpp +++ b/bench/bm_pmt_dict_ref.cpp @@ -2,10 +2,24 @@ #include #include +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push // ignore warning of external libraries that from this lib-context we do not have any control over +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wold-style-cast" +#pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion" +#ifndef __clang__ // only for GCC, not Clang +#pragma GCC diagnostic ignored "-Wuseless-cast" +#endif +#endif + #include "CLI/App.hpp" #include "CLI/Config.hpp" #include "CLI/Formatter.hpp" +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + #include #include @@ -66,7 +80,7 @@ int main(int argc, char* argv[]) auto valid = run_test(samples, d, index); auto t2 = std::chrono::steady_clock::now(); - auto time = 1e-9 * static_cast(std::chrono::duration_cast(t2 - t1).count()); + auto time = static_cast(1e-9) * static_cast(std::chrono::duration_cast(t2 - t1).count()); std::cout << "[PROFILE_TIME]" << time << "[PROFILE_TIME]" << std::endl; std::cout << "[PROFILE_VALID]" << valid << "[PROFILE_VALID]" << std::endl; diff --git a/bench/bm_pmt_serialize_uvec.cpp b/bench/bm_pmt_serialize_uvec.cpp index 00f2303..b327e77 100644 --- a/bench/bm_pmt_serialize_uvec.cpp +++ b/bench/bm_pmt_serialize_uvec.cpp @@ -1,11 +1,26 @@ #include #include +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push // ignore warning of external libraries that from this lib-context we do not have any control over +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wold-style-cast" +#pragma GCC diagnostic ignored "-Wimplicit-int-float-conversion" +#ifndef __clang__ // only for GCC, not Clang +#pragma GCC diagnostic ignored "-Wuseless-cast" +#endif +#endif + #include "CLI/App.hpp" #include "CLI/Config.hpp" #include "CLI/Formatter.hpp" +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + #include +#include using namespace pmtv; @@ -49,7 +64,7 @@ int main(int argc, char* argv[]) auto valid = run_test(samples, data); auto t2 = std::chrono::steady_clock::now(); - auto time = 1e-9 * static_cast(std::chrono::duration_cast(t2 - t1).count()); + auto time = static_cast(1e-9) * static_cast(std::chrono::duration_cast(t2 - t1).count()); std::cout << "[PROFILE_TIME]" << time << "[PROFILE_TIME]" << std::endl; std::cout << "[PROFILE_VALID]" << valid << "[PROFILE_VALID]" << std::endl; diff --git a/include/pmtv/meson.build b/include/pmtv/meson.build index f67cc41..2b2d936 100644 --- a/include/pmtv/meson.build +++ b/include/pmtv/meson.build @@ -3,6 +3,7 @@ files = [ 'pmt.hpp', 'rva_variant.hpp', 'type_helpers.hpp', + 'serialiser.hpp', 'version.hpp' ] diff --git a/include/pmtv/pmt.hpp b/include/pmtv/pmt.hpp index 9326a95..ade5906 100644 --- a/include/pmtv/pmt.hpp +++ b/include/pmtv/pmt.hpp @@ -1,7 +1,6 @@ #pragma once -#include "base64/base64.h" #include #include #include @@ -9,538 +8,89 @@ #include #include -#ifdef __GNUC__ -#pragma GCC diagnostic push // ignore warning of external libraries that from this lib-context we do not have any control over -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wsign-conversion" -#endif - -#include - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - namespace pmtv { -using pmt = pmt_var_t; -using map_t = std::map>; + using pmt = pmt_var_t; + using map_t = std::map>; -template -inline constexpr std::in_place_type_t> vec_t{}; + template + inline constexpr std::in_place_type_t> vec_t{}; -template -concept IsPmt = std::is_same_v; + template + concept IsPmt = std::is_same_v; // template // auto get_vector(V value) -> decltype(std::get>(value) { // return std::get>(value); // } -template -std::vector& get_vector(V value) -{ - return std::get>(value); -} - -template -std::span get_span(V& value) -{ - return std::span(std::get>(value)); -} - -template -map_t& get_map(V& value) -{ - return std::get(value); -} - -template -size_t elements(const P& value) -{ - return std::visit( - [](const auto& arg) -> size_t { - using T = std::decay_t; - if constexpr (std::same_as) - return 0; - else if constexpr (std::ranges::range) - return arg.size(); - return 1; - }, - value.get_base()); -} - -template -size_t bytes_per_element(const P& value) -{ - return std::visit( - [](const auto& arg) -> size_t { - using T = std::decay_t; - if constexpr (std::same_as) - return 0; - else if constexpr (std::ranges::range) - return sizeof(typename T::value_type); - return sizeof(T); - }, - value.get_base()); -} - - -/* - Functions for converting between structures and pmts. Each member of the structure must be - convertible to a pmt. No data interpretation is done. (For example a pointer and a length - won't work because we don't know that they are related fields. A span or a vector would.) - An extra requirement is that we must use a macro to declare structures and fields that we - want to use this way. - -For example, - struct my_data { - float x; - int y; - std::complex z; - }; - - REFL_AUTO(type(my_data), field(x), field(y), field(z)) - - Note that any members not declared in the `REFL_AUTO` call will not be transferred with the - data. - -*/ - -template -constexpr auto readable_members = filter(refl::member_list{}, [](auto member) { return is_readable(member); }); - - -/*********************Map Conversion Functions*****************************************/ - -// Functions for converting between pmt stuctures and maps. -template -constexpr void map_from_struct(const T& value, map_t& result) { - // iterate over the members of T - for_each(refl::reflect(value).members, [&](auto member) - { - if constexpr (is_readable(member)) - { - result[get_display_name(member)] = member(value); - } - }); -} - -template -auto map_from_struct(const T& value) { - // iterate over the members of T - map_t result; - map_from_struct(value, result); - return result; -} - -template -void to_struct(const map_t& value, T& result) { - // iterate over the members of T - for_each(refl::reflect(result).members, [&](auto member) - { - if constexpr (is_readable(member)) - { - using member_type = std::decay_t; - member(result) = std::get(value.at(get_display_name(member))); - } - }); -} - -template -T to_struct(const map_t& value) { - T result; - to_struct(value, result); - return result; -} - -template -bool validate_map(const map_t& value, bool exact=false) { - // Ensure that the map contains the members of the struct with the correct types. - // iterate over the members of T - T temp; - if (exact && value.size() != readable_members.size) return false; - bool result = true; - for_each(refl::reflect(temp).members, [&](auto member) - { - if constexpr (is_readable(member)) - { - using member_type = std::decay_t; - // Does the map contain the key and hold the correct type? - if (! value.count(get_display_name(member)) || - ! std::holds_alternative(value.at(get_display_name(member)))) - result = false; - } - }); - return result; -} - - -template -constexpr uint8_t pmtTypeIndex() -{ - if constexpr (std::same_as) - return 0; - else if constexpr (std::same_as) - return 1; - else if constexpr (std::signed_integral) - return 2; - else if constexpr (std::unsigned_integral) - return 3; - else if constexpr (std::floating_point) - return 4; - else if constexpr (Complex) - return 5; - else if constexpr (std::same_as) - return 6; - else if constexpr (std::same_as>>) - return 7; - else if constexpr (std::same_as>) - return 8; - else if constexpr (std::ranges::range) { - if constexpr (UniformVector) { - return pmtTypeIndex() << 4; - } - else { - return 9; // for vector of PMTs - } - } -} - -template -constexpr uint16_t serialId() -{ - if constexpr (Complex) { - return (pmtTypeIndex() << 8) | sizeof(typename T::value_type); - } - else if constexpr (Scalar || std::same_as) { - static_assert(sizeof(T) < 32, "Can't serial data wider than 16 bytes"); - if constexpr (support_size_t && std::is_same_v){ - return (pmtTypeIndex() << 8) | sizeof(uint64_t); - } else { - return (pmtTypeIndex < T > () << 8) | sizeof(T); - } - } - else if constexpr (UniformVector) { - static_assert(sizeof(typename T::value_type) < 32, - "Can't serial data wider than 16 bytes"); - return (pmtTypeIndex() << 8) | sizeof(typename T::value_type); - } - else - return pmtTypeIndex() << 8; -} - -// Forward decalaration so we can recursively serialize. -template -std::streamsize serialize(std::streambuf& sb, const P& value); - -template -struct serialInfo { - using value_type = std::conditional_t, uint64_t, T>; - static constexpr uint16_t value = serialId(); -}; - -inline std::streamsize _serialize_version(std::streambuf& sb) { - return sb.sputn(reinterpret_cast(&pmt_version), 2); -} - -template -std::streamsize _serialize_id(std::streambuf& sb) { - using Td = std::decay_t; - auto id = serialInfo::value; - return sb.sputn(reinterpret_cast(&id), 2); -} - -template -std::streamsize _serialize(std::streambuf& sb, const T& arg) { - auto length = _serialize_id(sb); - uint64_t sz = arg.size(); - length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); - for (auto& value: arg) { - length += serialize(sb, value); - } - return length; -} - -template -std::streamsize _serialize(std::streambuf& sb, const T& arg) { - auto length = _serialize_id(sb); - uint64_t sz = arg.size(); - length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); - char one = 1; - char zero = 0; - for (auto value : arg) { - length += sb.sputn(value ? &one : &zero, sizeof(char)); - } - return length; -} - -template -std::streamsize _serialize(std::streambuf& sb, const T& arg) { - auto length = _serialize_id(sb); - uint64_t sz = arg.size(); - length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); - for (auto& value: arg) { - // Send length then value - sz = value.size(); - length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); - length += sb.sputn(value.data(), static_cast(value.size())); - } - return length; -} - -template -std::streamsize _serialize(std::streambuf& sb, const T& arg) { - auto length = _serialize_id(sb); - uint64_t sz = arg.size(); - length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); - length += sb.sputn(reinterpret_cast(arg.data()),static_cast(arg.size() * sizeof(arg[0]))); - return length; -} - -template -std::streamsize _serialize(std::streambuf& sb, [[maybe_unused]] const T& arg) { - return _serialize_id(sb); -} - -template -std::streamsize _serialize(std::streambuf& sb, const T& arg) { - if constexpr (support_size_t && std::is_same_v) { - uint64_t arg64 {arg}; - return _serialize_id(sb) + sb.sputn(reinterpret_cast(&arg64), sizeof(arg64)); - } else { - return _serialize_id(sb) + sb.sputn(reinterpret_cast(&arg), sizeof(arg)); - } -} - -template -std::streamsize _serialize(std::streambuf& sb, const T& arg) { - auto length = _serialize_id(sb); - uint32_t nkeys = uint32_t(arg.size()); - length += sb.sputn(reinterpret_cast(&nkeys), sizeof(nkeys)); - uint32_t ksize; - for (const auto& [k, v] : arg) { - // For right now just prefix the size to the key and send it - ksize = uint32_t(k.size()); - length += - sb.sputn(reinterpret_cast(&ksize), sizeof(ksize)); - length += sb.sputn(k.c_str(), ksize); - length += serialize(sb, v); - } - return length; -} - -// FIXME - make this consistent endianness -template -std::streamsize serialize(std::streambuf& sb, const P& value) -{ - auto length = _serialize_version(sb); - - std::visit( - [&length, &sb](auto&& arg) { - length += _serialize(sb, arg); - }, - value); - - return length; -} - -template -T _deserialize_val(std::streambuf& sb); - -static pmt deserialize(std::streambuf& sb) -{ - uint16_t version; - // pmt_container_type container; - sb.sgetn(reinterpret_cast(&version), sizeof(version)); - // sb.sgetn(reinterpret_cast(&container), sizeof(container)); - - uint16_t receivedId; - sb.sgetn(reinterpret_cast(&receivedId), sizeof(receivedId)); - - pmt ret; - - switch (receivedId) { - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - - // case serialInfo>::value: return - // _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>>::value: - return _deserialize_val>>(sb); - case serialInfo>>::value: - return _deserialize_val>>(sb); - - case serialInfo::value: - return _deserialize_val(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo>::value: - return _deserialize_val>(sb); - case serialInfo::value: - return _deserialize_val(sb); - default: - throw std::runtime_error("pmt::deserialize: Invalid PMT type type"); - } - - return ret; -} - -template -T _deserialize_val(std::streambuf& sb) -{ - if constexpr (Scalar) { - T val; - sb.sgetn(reinterpret_cast(&val), sizeof(val)); - return val; - } - else if constexpr (PmtVector) { - std::vector val; - uint64_t nelems; - sb.sgetn(reinterpret_cast(&nelems), sizeof(nelems)); - for (uint64_t n = 0; n < nelems; n++) { - val.push_back(deserialize(sb)); - } - return val; - } - else if constexpr (UniformVector && !String) { - uint64_t sz; - sb.sgetn(reinterpret_cast(&sz), sizeof(uint64_t)); - std::vector val(sz); - sb.sgetn(reinterpret_cast(val.data()), static_cast(sz * sizeof(val[0]))); - return val; - } - else if constexpr (String) { - uint64_t sz; - sb.sgetn(reinterpret_cast(&sz), sizeof(uint64_t)); - std::string val(sz, '0'); - sb.sgetn(reinterpret_cast(val.data()), static_cast(sz)); - return val; - } - else if constexpr (UniformStringVector) { - uint64_t sz; - sb.sgetn(reinterpret_cast(&sz), sizeof(uint64_t)); - std::vector val(sz); - for (size_t i = 0; i < val.size(); i++) { - sb.sgetn(reinterpret_cast(&sz), sizeof(uint64_t)); - val[i].resize(sz); - sb.sgetn(val[i].data(), static_cast(sz)); - } - return val; - } - else if constexpr (PmtMap) { - map_t val; - - uint32_t nkeys; - sb.sgetn(reinterpret_cast(&nkeys), static_cast(sizeof(nkeys))); - for (uint32_t n = 0; n < nkeys; n++) { - uint32_t ksize; - sb.sgetn(reinterpret_cast(&ksize), static_cast(sizeof(ksize))); - std::vector data; - data.resize(ksize); - sb.sgetn(data.data(), ksize); - - val[std::string(data.begin(), data.end())] = deserialize(sb); - } - return val; - } - else { - throw std::runtime_error( - "pmt::_deserialize_value: attempted to deserialize invalid PMT type"); + template + std::vector &get_vector(V value) { + return std::get>(value); + } + + template + std::span get_span(V &value) { + return std::span(std::get>(value)); + } + + template + map_t &get_map(V &value) { + return std::get(value); + } + + template + size_t elements(const P &value) { + return std::visit( + [](const auto &arg) -> size_t { + using T = std::decay_t; + if constexpr (std::same_as) + return 0; + else if constexpr (std::ranges::range) + return arg.size(); + return 1; + }, + value.get_base()); + } + + template + size_t bytes_per_element(const P &value) { + return std::visit( + [](const auto &arg) -> size_t { + using T = std::decay_t; + if constexpr (std::same_as) + return 0; + else if constexpr (std::ranges::range) + return sizeof(typename T::value_type); + return sizeof(T); + }, + value.get_base()); + } + + // Allows us to cast from a pmt like this: auto x = cast(mypmt); + template + T cast(const P &value) { + return std::visit( + [](const auto &arg) -> T { + using U = std::decay_t; + if constexpr (std::convertible_to || (Complex < T > && Complex < U > )) { + if constexpr (Complex < T >) { + if constexpr (std::integral || std::floating_point) { + return std::complex(static_cast(arg)); + } else { + return static_cast(arg); + } + } else { + return static_cast(arg); + } + } + // else if constexpr (PmtMap && PmtMap) { + // return std::get>>(arg); + // } + else + throw std::runtime_error("Invalid PMT Cast " + std::string(typeid(T).name()) + " " + + std::string(typeid(U).name())); + }, + value); } -} - -template -std::string to_base64(const P& value) -{ - std::stringbuf sb; - auto nbytes = serialize(sb, value); - std::string pre_encoded_str(static_cast(nbytes), '0'); - sb.sgetn(pre_encoded_str.data(), nbytes); - auto nencoded_bytes = Base64encode_len(static_cast(nbytes)); - std::string encoded_str(static_cast(nencoded_bytes), '0'); - auto nencoded = Base64encode(encoded_str.data(), pre_encoded_str.data(), static_cast(nbytes)); - encoded_str.resize(static_cast(nencoded - 1)); // because it null terminates - return encoded_str; } - -[[maybe_unused]] static pmt from_base64(const std::string& encoded_str) -{ - std::string bufplain(encoded_str.size(), '0'); - Base64decode(bufplain.data(), encoded_str.data()); - std::stringbuf sb(bufplain); - return deserialize(sb); -} - -// Allows us to cast from a pmt like this: auto x = cast(mypmt); -template -T cast(const P& value) -{ - return std::visit( - [](const auto& arg) -> T { - using U = std::decay_t; - if constexpr (std::convertible_to || (Complex && Complex)) { - if constexpr(Complex) { - if constexpr (std::integral || std::floating_point) { - return std::complex(static_cast(arg)); - } else { - return static_cast(arg); - } - } else { - return static_cast(arg); - } - } - // else if constexpr (PmtMap && PmtMap) { - // return std::get>>(arg); - // } - else - throw std::runtime_error("Invalid PMT Cast " + std::string(typeid(T).name()) + " " + std::string(typeid(U).name())); - }, - value); -} - -} // namespace pmtv diff --git a/include/pmtv/serialiser.hpp b/include/pmtv/serialiser.hpp new file mode 100644 index 0000000..0bffb2c --- /dev/null +++ b/include/pmtv/serialiser.hpp @@ -0,0 +1,431 @@ +#ifndef PMT_SERIALISER_HPP +#define PMT_SERIALISER_HPP + +#include "pmt.hpp" +#include "base64/base64.h" + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic push // ignore warning of external libraries that from this lib-context we do not have any control over +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +#include + +#if defined(__clang__) || defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +namespace pmtv { + /* + Functions for converting between structures and pmts. Each member of the structure must be + convertible to a pmt. No data interpretation is done. (For example a pointer and a length + won't work because we don't know that they are related fields. A span or a vector would.) + An extra requirement is that we must use a macro to declare structures and fields that we + want to use this way. + +For example, + struct my_data { + float x; + int y; + std::complex z; + }; + + REFL_AUTO(type(my_data), field(x), field(y), field(z)) + + Note that any members not declared in the `REFL_AUTO` call will not be transferred with the + data. + +*/ + + template + constexpr auto readable_members = filter(refl::member_list{}, [](auto member) { return is_readable(member); }); + + +/*********************Map Conversion Functions*****************************************/ + +// Functions for converting between pmt stuctures and maps. + template + constexpr void map_from_struct(const T &value, map_t &result) { + // iterate over the members of T + for_each(refl::reflect(value).members, [&](auto member) { + if constexpr (is_readable(member)) { + result[get_display_name(member)] = member(value); + } + }); + } + + template + auto map_from_struct(const T &value) { + // iterate over the members of T + map_t result; + map_from_struct(value, result); + return result; + } + + template + void to_struct(const map_t &value, T &result) { + // iterate over the members of T + for_each(refl::reflect(result).members, [&](auto member) { + if constexpr (is_readable(member)) { + using member_type = std::decay_t; + member(result) = std::get(value.at(get_display_name(member))); + } + }); + } + + template + T to_struct(const map_t &value) { + T result; + to_struct(value, result); + return result; + } + + template + bool validate_map(const map_t &value, bool exact = false) { + // Ensure that the map contains the members of the struct with the correct types. + // iterate over the members of T + T temp; + if (exact && value.size() != readable_members.size) return false; + bool result = true; + for_each(refl::reflect(temp).members, [&](auto member) { + if constexpr (is_readable(member)) { + using member_type = std::decay_t; + // Does the map contain the key and hold the correct type? + if (!value.count(get_display_name(member)) || + !std::holds_alternative(value.at(get_display_name(member)))) + result = false; + } + }); + return result; + } + + + template + constexpr uint8_t pmtTypeIndex() { + if constexpr (std::same_as) + return 0; + else if constexpr (std::same_as) + return 1; + else if constexpr (std::signed_integral) + return 2; + else if constexpr (std::unsigned_integral) + return 3; + else if constexpr (std::floating_point) + return 4; + else if constexpr (Complex) + return 5; + else if constexpr (std::same_as) + return 6; + else if constexpr (std::same_as>>) + return 7; + else if constexpr (std::same_as>) + return 8; + else if constexpr (std::ranges::range) { + if constexpr (UniformVector) { + return pmtTypeIndex() << 4; + } else { + return 9; // for vector of PMTs + } + } + } + + template + constexpr uint16_t serialId() { + if constexpr (Complex) { + return (pmtTypeIndex() << 8) | sizeof(typename T::value_type); + } else if constexpr (Scalar || std::same_as) { + static_assert(sizeof(T) < 32, "Can't serial data wider than 16 bytes"); + if constexpr (support_size_t && std::is_same_v) { + return (pmtTypeIndex() << 8) | sizeof(uint64_t); + } else { + return (pmtTypeIndex() << 8) | sizeof(T); + } + } else if constexpr (UniformVector) { + static_assert(sizeof(typename T::value_type) < 32, + "Can't serial data wider than 16 bytes"); + return (pmtTypeIndex() << 8) | sizeof(typename T::value_type); + } else + return pmtTypeIndex() << 8; + } + +// Forward decalaration so we can recursively serialize. + template + std::streamsize serialize(std::streambuf &sb, const P &value); + + template + struct serialInfo { + using value_type = std::conditional_t, uint64_t, T>; + static constexpr uint16_t value = serialId(); + }; + + inline std::streamsize _serialize_version(std::streambuf &sb) { + return sb.sputn(reinterpret_cast(&pmt_version), 2); + } + + template + std::streamsize _serialize_id(std::streambuf &sb) { + using Td = std::decay_t; + auto id = serialInfo::value; + return sb.sputn(reinterpret_cast(&id), 2); + } + + template + std::streamsize _serialize(std::streambuf &sb, const T &arg) { + auto length = _serialize_id(sb); + uint64_t sz = arg.size(); + length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); + for (auto &value: arg) { + length += serialize(sb, value); + } + return length; + } + + template + std::streamsize _serialize(std::streambuf &sb, const T &arg) { + auto length = _serialize_id(sb); + uint64_t sz = arg.size(); + length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); + char one = 1; + char zero = 0; + for (auto value: arg) { + length += sb.sputn(value ? &one : &zero, sizeof(char)); + } + return length; + } + + template + std::streamsize _serialize(std::streambuf &sb, const T &arg) { + auto length = _serialize_id(sb); + uint64_t sz = arg.size(); + length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); + for (auto &value: arg) { + // Send length then value + sz = value.size(); + length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); + length += sb.sputn(value.data(), static_cast(value.size())); + } + return length; + } + + template + std::streamsize _serialize(std::streambuf &sb, const T &arg) { + auto length = _serialize_id(sb); + uint64_t sz = arg.size(); + length += sb.sputn(reinterpret_cast(&sz), sizeof(uint64_t)); + length += sb.sputn(reinterpret_cast(arg.data()), + static_cast(arg.size() * sizeof(arg[0]))); + return length; + } + + template + std::streamsize _serialize(std::streambuf &sb, [[maybe_unused]] const T &arg) { + return _serialize_id(sb); + } + + template + std::streamsize _serialize(std::streambuf &sb, const T &arg) { + if constexpr (support_size_t && std::is_same_v) { + uint64_t arg64{arg}; + return _serialize_id(sb) + sb.sputn(reinterpret_cast(&arg64), sizeof(arg64)); + } else { + return _serialize_id(sb) + sb.sputn(reinterpret_cast(&arg), sizeof(arg)); + } + } + + template + std::streamsize _serialize(std::streambuf &sb, const T &arg) { + auto length = _serialize_id(sb); + uint32_t nkeys = uint32_t(arg.size()); + length += sb.sputn(reinterpret_cast(&nkeys), sizeof(nkeys)); + uint32_t ksize; + for (const auto &[k, v]: arg) { + // For right now just prefix the size to the key and send it + ksize = uint32_t(k.size()); + length += + sb.sputn(reinterpret_cast(&ksize), sizeof(ksize)); + length += sb.sputn(k.c_str(), ksize); + length += serialize(sb, v); + } + return length; + } + +// FIXME - make this consistent endianness + template + std::streamsize serialize(std::streambuf &sb, const P &value) { + auto length = _serialize_version(sb); + + std::visit( + [&length, &sb](auto &&arg) { + length += _serialize(sb, arg); + }, + value); + + return length; + } + + template + T _deserialize_val(std::streambuf &sb); + + static pmt deserialize(std::streambuf &sb) { + uint16_t version; + // pmt_container_type container; + sb.sgetn(reinterpret_cast(&version), sizeof(version)); + // sb.sgetn(reinterpret_cast(&container), sizeof(container)); + + uint16_t receivedId; + sb.sgetn(reinterpret_cast(&receivedId), sizeof(receivedId)); + + pmt ret; + + switch (receivedId) { + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + + // case serialInfo>::value: return + // _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>>::value: + return _deserialize_val>>(sb); + case serialInfo>>::value: + return _deserialize_val>>(sb); + + case serialInfo::value: + return _deserialize_val(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo>::value: + return _deserialize_val>(sb); + case serialInfo::value: + return _deserialize_val(sb); + default: + throw std::runtime_error("pmt::deserialize: Invalid PMT type type"); + } + + return ret; + } + + template + T _deserialize_val(std::streambuf &sb) { + if constexpr (Scalar) { + T val; + sb.sgetn(reinterpret_cast(&val), sizeof(val)); + return val; + } else if constexpr (PmtVector) { + std::vector val; + uint64_t nelems; + sb.sgetn(reinterpret_cast(&nelems), sizeof(nelems)); + for (uint64_t n = 0; n < nelems; n++) { + val.push_back(deserialize(sb)); + } + return val; + } else if constexpr (UniformVector && !String) { + uint64_t sz; + sb.sgetn(reinterpret_cast(&sz), sizeof(uint64_t)); + std::vector val(sz); + sb.sgetn(reinterpret_cast(val.data()), static_cast(sz * sizeof(val[0]))); + return val; + } else if constexpr (String) { + uint64_t sz; + sb.sgetn(reinterpret_cast(&sz), sizeof(uint64_t)); + std::string val(sz, '0'); + sb.sgetn(reinterpret_cast(val.data()), static_cast(sz)); + return val; + } else if constexpr (UniformStringVector) { + uint64_t sz; + sb.sgetn(reinterpret_cast(&sz), sizeof(uint64_t)); + std::vector val(sz); + for (size_t i = 0; i < val.size(); i++) { + sb.sgetn(reinterpret_cast(&sz), sizeof(uint64_t)); + val[i].resize(sz); + sb.sgetn(val[i].data(), static_cast(sz)); + } + return val; + } else if constexpr (PmtMap) { + map_t val; + + uint32_t nkeys; + sb.sgetn(reinterpret_cast(&nkeys), static_cast(sizeof(nkeys))); + for (uint32_t n = 0; n < nkeys; n++) { + uint32_t ksize; + sb.sgetn(reinterpret_cast(&ksize), static_cast(sizeof(ksize))); + std::vector data; + data.resize(ksize); + sb.sgetn(data.data(), ksize); + + val[std::string(data.begin(), data.end())] = deserialize(sb); + } + return val; + } else { + throw std::runtime_error( + "pmt::_deserialize_value: attempted to deserialize invalid PMT type"); + } + } + + + template + std::string to_base64(const P &value) { + std::stringbuf sb; + auto nbytes = serialize(sb, value); + std::string pre_encoded_str(static_cast(nbytes), '0'); + sb.sgetn(pre_encoded_str.data(), nbytes); + auto nencoded_bytes = Base64encode_len(static_cast(nbytes)); + std::string encoded_str(static_cast(nencoded_bytes), '0'); + auto nencoded = Base64encode(encoded_str.data(), pre_encoded_str.data(), static_cast(nbytes)); + encoded_str.resize(static_cast(nencoded - 1)); // because it null terminates + return encoded_str; + } + + [[maybe_unused]] static pmt from_base64(const std::string &encoded_str) { + std::string bufplain(encoded_str.size(), '0'); + Base64decode(bufplain.data(), encoded_str.data()); + std::stringbuf sb(bufplain); + return deserialize(sb); + } + +} // namespace pmtv + +#endif //PMT_SERIALISER_HPP diff --git a/python/pmtv/bindings/pmt_python.cc b/python/pmtv/bindings/pmt_python.cc index 7302f8a..2383d8a 100644 --- a/python/pmtv/bindings/pmt_python.cc +++ b/python/pmtv/bindings/pmt_python.cc @@ -8,10 +8,9 @@ */ /* This file is automatically generated using bindtool */ -#ifdef __GNUC__ +#if defined(__clang__) || defined(__GNUC__) #pragma GCC diagnostic push // ignore warning of external libraries that from this lib-context we do not have any control over #pragma GCC diagnostic ignored "-Wall" -#pragma GCC diagnostic ignored "-Wuseless-cast" #pragma GCC diagnostic ignored "-Wold-style-cast" #endif @@ -23,7 +22,7 @@ #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include -#ifdef __GNUC__ +#if defined(__clang__) || defined(__GNUC__) #pragma GCC diagnostic pop #endif @@ -31,6 +30,7 @@ namespace py = pybind11; #include #include +#include // pydoc.h is automatically generated in the build directory // #include @@ -61,7 +61,6 @@ py::object create_numpy_scalar(T val) #ifdef __GNUC__ #pragma GCC diagnostic push // ignore warning of external libraries that from this lib-context we do not have any control over #pragma GCC diagnostic ignored "-Wall" -#pragma GCC diagnostic ignored "-Wuseless-cast" #pragma GCC diagnostic ignored "-Wold-style-cast" #endif PyObject* scal = diff --git a/python/pmtv/bindings/pmtv_python.cc b/python/pmtv/bindings/pmtv_python.cc index d8534fe..9d60478 100644 --- a/python/pmtv/bindings/pmtv_python.cc +++ b/python/pmtv/bindings/pmtv_python.cc @@ -8,10 +8,13 @@ * */ -#ifdef __GNUC__ +#if defined(__clang__) || defined(__GNUC__) #pragma GCC diagnostic push // ignore warning of external libraries that from this lib-context we do not have any control over -#pragma GCC diagnostic ignored "-Wuseless-cast" +#pragma GCC diagnostic ignored "-Wall" #pragma GCC diagnostic ignored "-Wold-style-cast" +#ifndef __clang__ // only for GCC, not Clang +#pragma GCC diagnostic ignored "-Wuseless-cast" +#endif #endif #include @@ -19,7 +22,7 @@ #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include -#ifdef __GNUC__ +#if defined(__clang__) || defined(__GNUC__) #pragma GCC diagnostic pop #endif diff --git a/test/qa_map.cpp b/test/qa_map.cpp index 80dae56..a8bb26d 100644 --- a/test/qa_map.cpp +++ b/test/qa_map.cpp @@ -11,16 +11,16 @@ #include #include +#include #include using namespace pmtv; -TEST(PmtMap, EmptyMap) -{ +TEST(PmtMap, EmptyMap) { auto empty = pmt(map_t{}); auto v = get_map(empty); v["abc"] = pmt(uint64_t(4)); - v["xyz"] = pmt(std::vector{ 1, 2, 3, 4, 5 }); + v["xyz"] = pmt(std::vector{1, 2, 3, 4, 5}); using namespace std::literals; using namespace std::string_literals; @@ -29,7 +29,7 @@ TEST(PmtMap, EmptyMap) std::string keyString = keyStringLiteral; std::string_view keyStringView = keyStringLiteral; EXPECT_TRUE(std::get(v.at(keyString)) == uint64_t(4)); - EXPECT_TRUE(std::get(v.find(keyString)->second) == uint64_t(4)); + EXPECT_TRUE(std::get(v.find(keyString)->second) == uint64_t(4)); EXPECT_TRUE(std::get(v.find(keyStringView)->second) == uint64_t(4)); EXPECT_TRUE(std::get(v.find(keyStringLiteral)->second) == uint64_t(4)); EXPECT_TRUE(std::get(v.at("abc"s)) == uint64_t(4)); @@ -41,16 +41,15 @@ TEST(PmtMap, EmptyMap) } -TEST(PmtMap, PmtMapTests) -{ +TEST(PmtMap, PmtMapTests) { std::complex val1(1.2f, -3.4f); - std::vector val2{ 44, 34563, -255729, 4402 }; + std::vector val2{44, 34563, -255729, 4402}; // Create the PMT map pmtv::map_t input_map({ - { "key1", val1 }, - { "key2", val2 }, - }); + {"key1", val1}, + {"key2", val2}, + }); pmt map_pmt = input_map; std::cout << map_pmt << std::endl; @@ -68,16 +67,15 @@ TEST(PmtMap, PmtMapTests) std::cout << map_pmt << std::endl; } -TEST(PmtMap, MapSerialize) -{ +TEST(PmtMap, MapSerialize) { std::complex val1(1.2f, -3.4f); - std::vector val2{ 44, 34563, -255729, 4402 }; + std::vector val2{44, 34563, -255729, 4402}; // Create the PMT map map_t input_map({ - { "key1", val1 }, - { "key2", val2 }, - }); + {"key1", val1}, + {"key2", val2}, + }); pmt map_pmt(input_map); std::stringbuf sb; serialize(sb, map_pmt); @@ -86,16 +84,15 @@ TEST(PmtMap, MapSerialize) EXPECT_TRUE(map_pmt == y); } -TEST(PmtMap, get_as) -{ +TEST(PmtMap, get_as) { std::complex val1(1.2f, -3.4f); - std::vector val2{ 44, 34563, -255729, 4402 }; + std::vector val2{44, 34563, -255729, 4402}; // Create the PMT map pmtv::map_t input_map({ - { "key1", val1 }, - { "key2", val2 }, - }); + {"key1", val1}, + {"key2", val2}, + }); auto x = pmt(input_map); // Make sure that we can get the value back out // auto y = std::map>(x); @@ -106,16 +103,15 @@ TEST(PmtMap, get_as) // EXPECT_ANY_THROW(float(x)); } -TEST(PmtMap, base64) -{ +TEST(PmtMap, base64) { std::complex val1(1.2f, -3.4f); - std::vector val2{ 44, 34563, -255729, 4402 }; + std::vector val2{44, 34563, -255729, 4402}; // Create the PMT map pmtv::map_t input_map({ - { "key1", val1 }, - { "key2", val2 }, - }); + {"key1", val1}, + {"key2", val2}, + }); pmt x = input_map; // Make sure that we can get the value back out @@ -125,16 +121,15 @@ TEST(PmtMap, base64) EXPECT_TRUE(x == y); } -TEST(PmtMap, fmt) -{ +TEST(PmtMap, fmt) { std::complex val1(1.2f, -3.4f); - std::vector val2{ 44, 34563, -255729, 4402 }; + std::vector val2{44, 34563, -255729, 4402}; // Create the PMT map pmtv::map_t input_map({ - { "key1", val1 }, - { "key2", val2 }, - }); + {"key1", val1}, + {"key2", val2}, + }); pmt x = input_map; EXPECT_EQ(fmt::format("{}", x), fmt::format("{{{}}}", fmt::join(input_map, ", "))); diff --git a/test/qa_reflection.cpp b/test/qa_reflection.cpp index ef9ee67..0cf97d7 100644 --- a/test/qa_reflection.cpp +++ b/test/qa_reflection.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include diff --git a/test/qa_scalar.cpp b/test/qa_scalar.cpp index c5d0fdc..11b4f2f 100644 --- a/test/qa_scalar.cpp +++ b/test/qa_scalar.cpp @@ -10,67 +10,63 @@ #include #include +#include #include #include using namespace pmtv; using testing_types = ::testing::Types, - std::complex>; - -template -class PmtScalarFixture : public ::testing::Test -{ + int8_t, + uint16_t, + int16_t, + uint32_t, + int32_t, + uint64_t, + int64_t, + std::size_t, + float, + double, + std::complex, + std::complex>; + +template +class PmtScalarFixture : public ::testing::Test { public: T get_value() { return T(4); } + T zero_value() { return T(0); } }; -template <> -float PmtScalarFixture::get_value() -{ +template<> +float PmtScalarFixture::get_value() { return 4.1f; } -template <> -double PmtScalarFixture::get_value() -{ + +template<> +double PmtScalarFixture::get_value() { return 4.1; } -template <> -std::complex PmtScalarFixture>::get_value() -{ +template<> +std::complex PmtScalarFixture>::get_value() { return std::complex(4.1f, -4.1f); } -template <> -std::complex PmtScalarFixture>::get_value() -{ +template<> +std::complex PmtScalarFixture>::get_value() { return std::complex(4.1, -4.1); } TYPED_TEST_SUITE(PmtScalarFixture, testing_types); -TYPED_TEST(PmtScalarFixture, PmtScalarNull) -{ +TYPED_TEST(PmtScalarFixture, PmtScalarNull) { // Should initialize to nullptr pmt x; //{this->get_value()}; EXPECT_TRUE(x == pmt_null()); } -TYPED_TEST(PmtScalarFixture, PmtScalarConstruction) -{ +TYPED_TEST(PmtScalarFixture, PmtScalarConstruction) { // We should be able to do: // a = 4; // a(4); @@ -103,8 +99,7 @@ TYPED_TEST(PmtScalarFixture, PmtScalarConstruction) EXPECT_TRUE(e == b); } -TYPED_TEST(PmtScalarFixture, PmtScalarValue) -{ +TYPED_TEST(PmtScalarFixture, PmtScalarValue) { // Get the value, change the value auto value = this->get_value(); pmt x(value); @@ -113,11 +108,10 @@ TYPED_TEST(PmtScalarFixture, PmtScalarValue) x = value; EXPECT_TRUE(x == value); // pmt e({{"abc", 123}, {"you and me", "baby"}}); - pmt e(std::vector({ 4, 5, 6 })); + pmt e(std::vector({4, 5, 6})); } -TYPED_TEST(PmtScalarFixture, PmtScalarPrint) -{ +TYPED_TEST(PmtScalarFixture, PmtScalarPrint) { // Send to string stream and make sure it works. auto value = this->get_value(); pmt x(value); @@ -125,22 +119,21 @@ TYPED_TEST(PmtScalarFixture, PmtScalarPrint) std::stringstream ss_check; ss << x; // Annoying special cases - if constexpr(Complex) { + if constexpr (Complex) { if (value.imag() >= 0) ss_check << value.real() << "+j" << value.imag(); else ss_check << value.real() << "-j" << -value.imag(); - } else if constexpr(std::same_as) + } else if constexpr (std::same_as) ss_check << int(value); - else if constexpr(std::same_as) + else if constexpr (std::same_as) ss_check << unsigned(value); else ss_check << value; EXPECT_EQ(ss.str(), ss_check.str()); } -TYPED_TEST(PmtScalarFixture, PmtScalarSerialize) -{ +TYPED_TEST(PmtScalarFixture, PmtScalarSerialize) { // Serialize/Deserialize and make sure that it works auto value = this->get_value(); pmt x(value); @@ -156,8 +149,7 @@ TYPED_TEST(PmtScalarFixture, PmtScalarSerialize) } } -TYPED_TEST(PmtScalarFixture, explicit_cast) -{ +TYPED_TEST(PmtScalarFixture, explicit_cast) { pmt x = this->get_value(); // Make sure that we can get the value back out auto y = pmtv::cast(x); @@ -188,10 +180,9 @@ TYPED_TEST(PmtScalarFixture, explicit_cast) // EXPECT_ANY_THROW(std::vector(x)); } -TYPED_TEST(PmtScalarFixture, wrong_cast) -{ - if constexpr (Scalar && !Complex ) { - TypeParam p0 {54}; +TYPED_TEST(PmtScalarFixture, wrong_cast) { + if constexpr (Scalar && !Complex) { + TypeParam p0{54}; pmt p1 = p0; EXPECT_TRUE(p0 == p1); @@ -204,8 +195,7 @@ TYPED_TEST(PmtScalarFixture, wrong_cast) } } -TYPED_TEST(PmtScalarFixture, base64) -{ +TYPED_TEST(PmtScalarFixture, base64) { auto value = this->get_value(); pmt x(value); // Make sure that we can get the value back out @@ -219,8 +209,7 @@ TYPED_TEST(PmtScalarFixture, base64) } } -TYPED_TEST(PmtScalarFixture, element_size) -{ +TYPED_TEST(PmtScalarFixture, element_size) { pmt x = this->get_value(); EXPECT_TRUE(elements(x) == 1); EXPECT_TRUE(bytes_per_element(x) == sizeof(TypeParam)); diff --git a/test/qa_string.cpp b/test/qa_string.cpp index 2225eb6..75d7410 100644 --- a/test/qa_string.cpp +++ b/test/qa_string.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include diff --git a/test/qa_uniform_vector.cpp b/test/qa_uniform_vector.cpp index 0a73979..6b6c2d9 100644 --- a/test/qa_uniform_vector.cpp +++ b/test/qa_uniform_vector.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include diff --git a/test/qa_vector_of_pmts.cpp b/test/qa_vector_of_pmts.cpp index 2efcaf5..8810a87 100644 --- a/test/qa_vector_of_pmts.cpp +++ b/test/qa_vector_of_pmts.cpp @@ -13,6 +13,7 @@ #include #include +#include #include @@ -35,15 +36,14 @@ operator[] (lookup and add) Cheap copies (could be moves) */ -TEST(PmtVectorPmt, Constructor) -{ +TEST(PmtVectorPmt, Constructor) { // Empty Constructor - pmt empty_vec{ std::vector() }; + pmt empty_vec{std::vector()}; EXPECT_EQ(std::get>(empty_vec).size(), 0); - pmt il_vec{ std::vector{1.0, 2, "abc"}}; + pmt il_vec{std::vector{1.0, 2, "abc"}}; std::vector vec; vec.push_back(pmt(1)); - vec.push_back(pmt(std::vector{ 1, 2, 3 })); + vec.push_back(pmt(std::vector{1, 2, 3})); auto p = pmt(vec); @@ -53,11 +53,10 @@ TEST(PmtVectorPmt, Constructor) EXPECT_TRUE(vec[1] == vec2[1]); } -TEST(PmtVectorPmt, fmt) -{ +TEST(PmtVectorPmt, fmt) { std::vector vec; vec.push_back(pmt(1)); - vec.push_back(pmt(std::vector{ 1, 2, 3 })); + vec.push_back(pmt(std::vector{1, 2, 3})); EXPECT_EQ(fmt::format("{}", pmt(vec)), fmt::format("[{}]", fmt::join(vec, ", "))); }