Add the ability to derive message types from one another.
`DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT` and `DAP_STRUCT_TYPEINFO_EXT` are two new flavors of `DAP_IMPLEMENT_STRUCT_TYPEINFO` and `DAP_STRUCT_TYPEINFO` that allow you to derive message types. This involved a bit of reworking on the serializer interfaces. Added test. Issue: #32
This commit is contained in:
parent
c9187480d1
commit
cc93ba9747
@ -194,6 +194,7 @@ if(CPPDAP_BUILD_TESTS)
|
||||
${CPPDAP_SRC_DIR}/network_test.cpp
|
||||
${CPPDAP_SRC_DIR}/optional_test.cpp
|
||||
${CPPDAP_SRC_DIR}/session_test.cpp
|
||||
${CPPDAP_SRC_DIR}/typeinfo_test.cpp
|
||||
${CPPDAP_SRC_DIR}/variant_test.cpp
|
||||
${CPPDAP_GOOGLETEST_DIR}/googletest/src/gtest-all.cc
|
||||
)
|
||||
|
@ -78,10 +78,6 @@ class Deserializer {
|
||||
template <typename T0, typename... Types>
|
||||
inline bool deserialize(dap::variant<T0, Types...>*) const;
|
||||
|
||||
// deserialize() decodes a list of fields and stores them into the object.
|
||||
inline bool deserialize(void* object,
|
||||
const std::initializer_list<Field>&) const;
|
||||
|
||||
// deserialize() decodes the struct field f with the given name.
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* f) const;
|
||||
@ -117,20 +113,6 @@ bool Deserializer::deserialize(dap::variant<T0, Types...>* var) const {
|
||||
return deserialize(&var->value);
|
||||
}
|
||||
|
||||
bool Deserializer::deserialize(
|
||||
void* object,
|
||||
const std::initializer_list<Field>& fields) const {
|
||||
for (auto const& f : fields) {
|
||||
if (!field(f.name, [&](Deserializer* d) {
|
||||
auto ptr = reinterpret_cast<uint8_t*>(object) + f.offset;
|
||||
return f.type->deserialize(d, ptr);
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool Deserializer::field(const std::string& name, T* v) const {
|
||||
return this->field(name,
|
||||
@ -140,6 +122,7 @@ bool Deserializer::field(const std::string& name, T* v) const {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Serializer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
class FieldSerializer;
|
||||
|
||||
// Serializer is the interface used to encode data to structured storage.
|
||||
// A Serializer is associated with a single storage object, whos type and value
|
||||
@ -149,16 +132,12 @@ bool Deserializer::field(const std::string& name, T* v) const {
|
||||
// Methods that return a bool use this to indicate success.
|
||||
class Serializer {
|
||||
public:
|
||||
using FieldSerializer = std::function<bool(Serializer*)>;
|
||||
template <typename T>
|
||||
using IsFieldSerializer = std::is_convertible<T, FieldSerializer>;
|
||||
|
||||
// serialization methods for simple data types.
|
||||
virtual bool serialize(boolean) = 0;
|
||||
virtual bool serialize(integer) = 0;
|
||||
virtual bool serialize(number) = 0;
|
||||
virtual bool serialize(const string&) = 0;
|
||||
virtual bool serialize(const object&) = 0;
|
||||
virtual bool serialize(const dap::object&) = 0;
|
||||
virtual bool serialize(const any&) = 0;
|
||||
|
||||
// array() encodes count array elements to the array object referenced by this
|
||||
@ -166,14 +145,10 @@ class Serializer {
|
||||
// Serializer that should be used to encode the n'th array element's data.
|
||||
virtual bool array(size_t count, const std::function<bool(Serializer*)>&) = 0;
|
||||
|
||||
// fields() encodes all the provided fields of the given object.
|
||||
virtual bool fields(const void* object,
|
||||
const std::initializer_list<Field>&) = 0;
|
||||
|
||||
// field() encodes a field to the struct object referenced by this Serializer.
|
||||
// The FieldSerializer will be called with a Serializer used to encode the
|
||||
// field's data.
|
||||
virtual bool field(const std::string& name, const FieldSerializer&) = 0;
|
||||
// object() begins encoding the object referenced by this Serializer.
|
||||
// The std::function will be called with a FieldSerializer to serialize the
|
||||
// object's fields.
|
||||
virtual bool object(const std::function<bool(dap::FieldSerializer*)>&) = 0;
|
||||
|
||||
// remove() deletes the object referenced by this Serializer.
|
||||
// remove() can be used to serialize optionals with no value assigned.
|
||||
@ -198,12 +173,6 @@ class Serializer {
|
||||
|
||||
// deserialize() encodes the given string.
|
||||
inline bool serialize(const char* v);
|
||||
|
||||
// field() encodes the field with the given name and value.
|
||||
template <
|
||||
typename T,
|
||||
typename = typename std::enable_if<!IsFieldSerializer<T>::value>::type>
|
||||
inline bool field(const std::string& name, const T& v);
|
||||
};
|
||||
|
||||
template <typename T, typename>
|
||||
@ -235,8 +204,31 @@ bool Serializer::serialize(const char* v) {
|
||||
return serialize(std::string(v));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FieldSerializer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// FieldSerializer is the interface used to serialize fields of an object.
|
||||
class FieldSerializer {
|
||||
public:
|
||||
using SerializeFunc = std::function<bool(Serializer*)>;
|
||||
template <typename T>
|
||||
using IsSerializeFunc = std::is_convertible<T, SerializeFunc>;
|
||||
|
||||
// field() encodes a field to the struct object referenced by this Serializer.
|
||||
// The SerializeFunc will be called with a Serializer used to encode the
|
||||
// field's data.
|
||||
virtual bool field(const std::string& name, const SerializeFunc&) = 0;
|
||||
|
||||
// field() encodes the field with the given name and value.
|
||||
template <
|
||||
typename T,
|
||||
typename = typename std::enable_if<!IsSerializeFunc<T>::value>::type>
|
||||
inline bool field(const std::string& name, const T& v);
|
||||
};
|
||||
|
||||
template <typename T, typename>
|
||||
bool Serializer::field(const std::string& name, const T& v) {
|
||||
bool FieldSerializer::field(const std::string& name, const T& v) {
|
||||
return this->field(name, [&](Serializer* s) { return s->serialize(v); });
|
||||
}
|
||||
|
||||
|
@ -136,35 +136,73 @@ M member_type(M T::*);
|
||||
// NAME is the serialized name of the field, as described by the DAP
|
||||
// specification.
|
||||
#define DAP_FIELD(FIELD, NAME) \
|
||||
dap::Field { \
|
||||
::dap::Field { \
|
||||
NAME, DAP_OFFSETOF(StructTy, FIELD), \
|
||||
TypeOf<DAP_TYPEOF(StructTy, FIELD)>::type(), \
|
||||
}
|
||||
|
||||
// DAP_DECLARE_STRUCT_TYPEINFO() declares a TypeOf<> specialization for STRUCT.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_DECLARE_STRUCT_TYPEINFO(STRUCT) \
|
||||
template <> \
|
||||
struct TypeOf<STRUCT> { \
|
||||
static constexpr bool has_custom_serialization = true; \
|
||||
static const TypeInfo* type(); \
|
||||
static bool deserializeFields(const Deserializer*, void* obj); \
|
||||
static bool serializeFields(FieldSerializer*, const void* obj); \
|
||||
}
|
||||
|
||||
// DAP_DECLARE_STRUCT_TYPEINFO() implements the type() member function for the
|
||||
// DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION() implements the deserializeFields()
|
||||
// and serializeFields() static methods of a TypeOf<> specialization. Used
|
||||
// internally by DAP_IMPLEMENT_STRUCT_TYPEINFO() and
|
||||
// DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT().
|
||||
// You probably do not want to use this directly.
|
||||
#define DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, ...) \
|
||||
bool TypeOf<STRUCT>::deserializeFields(const Deserializer* d, void* obj) { \
|
||||
using StructTy = STRUCT; \
|
||||
(void)sizeof(StructTy); /* avoid unused 'using' warning */ \
|
||||
for (auto field : std::initializer_list<Field>{__VA_ARGS__}) { \
|
||||
if (!d->field(field.name, [&](Deserializer* d) { \
|
||||
auto ptr = reinterpret_cast<uint8_t*>(obj) + field.offset; \
|
||||
return field.type->deserialize(d, ptr); \
|
||||
})) { \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
return true; \
|
||||
} \
|
||||
bool TypeOf<STRUCT>::serializeFields(FieldSerializer* s, const void* obj) { \
|
||||
using StructTy = STRUCT; \
|
||||
(void)sizeof(StructTy); /* avoid unused 'using' warning */ \
|
||||
for (auto field : std::initializer_list<Field>{__VA_ARGS__}) { \
|
||||
if (!s->field(field.name, [&](Serializer* s) { \
|
||||
auto ptr = reinterpret_cast<const uint8_t*>(obj) + field.offset; \
|
||||
return field.type->serialize(s, ptr); \
|
||||
})) { \
|
||||
return false; \
|
||||
} \
|
||||
} \
|
||||
return true; \
|
||||
}
|
||||
|
||||
// DAP_IMPLEMENT_STRUCT_TYPEINFO() implements the type() member function for the
|
||||
// TypeOf<> specialization for STRUCT.
|
||||
// STRUCT is the structure typename.
|
||||
// NAME is the serialized name of the structure, as described by the DAP
|
||||
// specification. The variadic (...) parameters should be a repeated list of
|
||||
// DAP_FIELD()s, one for each field of the struct.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
|
||||
const TypeInfo* TypeOf<STRUCT>::type() { \
|
||||
using StructTy = STRUCT; \
|
||||
struct TI : BasicTypeInfo<StructTy> { \
|
||||
TI() : BasicTypeInfo<StructTy>(NAME) {} \
|
||||
bool deserialize(const Deserializer* d, void* ptr) const override { \
|
||||
return d->deserialize(ptr, {__VA_ARGS__}); \
|
||||
DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \
|
||||
const ::dap::TypeInfo* TypeOf<STRUCT>::type() { \
|
||||
struct TI : BasicTypeInfo<STRUCT> { \
|
||||
TI() : BasicTypeInfo<STRUCT>(NAME) {} \
|
||||
bool deserialize(const Deserializer* d, void* obj) const override { \
|
||||
return deserializeFields(d, obj); \
|
||||
} \
|
||||
bool serialize(Serializer* s, const void* ptr) const override { \
|
||||
return s->fields(ptr, {__VA_ARGS__}); \
|
||||
bool serialize(Serializer* s, const void* obj) const override { \
|
||||
return s->object( \
|
||||
[&](FieldSerializer* fs) { return serializeFields(fs, obj); }); \
|
||||
} \
|
||||
}; \
|
||||
static TI typeinfo; \
|
||||
@ -173,10 +211,53 @@ M member_type(M T::*);
|
||||
|
||||
// DAP_STRUCT_TYPEINFO() is a helper for declaring and implementing a TypeOf<>
|
||||
// specialization for STRUCT in a single statement.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_STRUCT_TYPEINFO(STRUCT, NAME, ...) \
|
||||
DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO(STRUCT, NAME, __VA_ARGS__)
|
||||
|
||||
// DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT() implements the type() member function for
|
||||
// the TypeOf<> specialization for STRUCT that derives from BASE.
|
||||
// STRUCT is the structure typename.
|
||||
// BASE is the base structure typename.
|
||||
// NAME is the serialized name of the structure, as described by the DAP
|
||||
// specification. The variadic (...) parameters should be a repeated list of
|
||||
// DAP_FIELD()s, one for each field of the struct.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \
|
||||
static_assert(std::is_base_of<BASE, STRUCT>::value, \
|
||||
#STRUCT " does not derive from " #BASE); \
|
||||
DAP_IMPLEMENT_STRUCT_FIELD_SERIALIZATION(STRUCT, NAME, __VA_ARGS__) \
|
||||
const ::dap::TypeInfo* TypeOf<STRUCT>::type() { \
|
||||
struct TI : BasicTypeInfo<STRUCT> { \
|
||||
TI() : BasicTypeInfo<STRUCT>(NAME) {} \
|
||||
bool deserialize(const Deserializer* d, void* obj) const override { \
|
||||
auto derived = static_cast<STRUCT*>(obj); \
|
||||
auto base = static_cast<BASE*>(obj); \
|
||||
return TypeOf<BASE>::deserializeFields(d, base) && \
|
||||
deserializeFields(d, derived); \
|
||||
} \
|
||||
bool serialize(Serializer* s, const void* obj) const override { \
|
||||
return s->object([&](FieldSerializer* fs) { \
|
||||
auto derived = static_cast<const STRUCT*>(obj); \
|
||||
auto base = static_cast<const BASE*>(obj); \
|
||||
return TypeOf<BASE>::serializeFields(fs, base) && \
|
||||
serializeFields(fs, derived); \
|
||||
}); \
|
||||
} \
|
||||
}; \
|
||||
static TI typeinfo; \
|
||||
return &typeinfo; \
|
||||
}
|
||||
|
||||
// DAP_STRUCT_TYPEINFO_EXT() is a helper for declaring and implementing a
|
||||
// TypeOf<> specialization for STRUCT that derives from BASE in a single
|
||||
// statement.
|
||||
// Must be used within the 'dap' namespace.
|
||||
#define DAP_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, ...) \
|
||||
DAP_DECLARE_STRUCT_TYPEINFO(STRUCT); \
|
||||
DAP_IMPLEMENT_STRUCT_TYPEINFO_EXT(STRUCT, BASE, NAME, __VA_ARGS__)
|
||||
|
||||
} // namespace dap
|
||||
|
||||
#endif // dap_typeof_h
|
||||
|
@ -224,27 +224,24 @@ bool Serializer::array(size_t count,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Serializer::fields(const void* object,
|
||||
const std::initializer_list<Field>& fields) {
|
||||
*json = nlohmann::json({}, false, nlohmann::json::value_t::object);
|
||||
for (auto const& f : fields) {
|
||||
if (!field(f.name, [&](dap::Serializer* d) {
|
||||
auto ptr = reinterpret_cast<const uint8_t*>(object) + f.offset;
|
||||
return f.type->serialize(d, ptr);
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Serializer::object(const std::function<bool(dap::FieldSerializer*)>& cb) {
|
||||
struct FS : public FieldSerializer {
|
||||
nlohmann::json* const json;
|
||||
|
||||
bool Serializer::field(const std::string& name,
|
||||
const std::function<bool(dap::Serializer*)>& cb) {
|
||||
FS(nlohmann::json* json) : json(json) {}
|
||||
bool field(const std::string& name, const SerializeFunc& cb) override {
|
||||
Serializer s(&(*json)[name]);
|
||||
auto res = cb(&s);
|
||||
if (s.removed) {
|
||||
json->erase(name);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
*json = nlohmann::json({}, false, nlohmann::json::value_t::object);
|
||||
FS fs{json};
|
||||
return cb(&fs);
|
||||
}
|
||||
|
||||
void Serializer::remove() {
|
||||
|
@ -67,11 +67,6 @@ struct Deserializer : public dap::Deserializer {
|
||||
return dap::Deserializer::deserialize(v);
|
||||
}
|
||||
|
||||
inline bool deserialize(void* o,
|
||||
const std::initializer_list<Field>& f) const {
|
||||
return dap::Deserializer::deserialize(o, f);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline bool field(const std::string& name, T* v) const {
|
||||
return dap::Deserializer::deserialize(name, v);
|
||||
@ -94,23 +89,14 @@ struct Serializer : public dap::Serializer {
|
||||
bool serialize(integer v) override;
|
||||
bool serialize(number v) override;
|
||||
bool serialize(const string& v) override;
|
||||
bool serialize(const object& v) override;
|
||||
bool serialize(const dap::object& v) override;
|
||||
bool serialize(const any& v) override;
|
||||
bool array(size_t count,
|
||||
const std::function<bool(dap::Serializer*)>&) override;
|
||||
bool fields(const void* object,
|
||||
const std::initializer_list<Field>& fields) override;
|
||||
bool field(const std::string& name, const FieldSerializer&) override;
|
||||
bool object(const std::function<bool(dap::FieldSerializer*)>&) override;
|
||||
void remove() override;
|
||||
|
||||
// Unhide base overloads
|
||||
template <
|
||||
typename T,
|
||||
typename = typename std::enable_if<!IsFieldSerializer<T>::value>::type>
|
||||
inline bool field(const std::string& name, const T& v) {
|
||||
return dap::Serializer::field(name, v);
|
||||
}
|
||||
|
||||
template <typename T,
|
||||
typename = std::enable_if<TypeOf<T>::has_custom_serialization>>
|
||||
inline bool serialize(const T& v) {
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace dap {
|
||||
|
||||
struct JSONInnerTestObject {
|
||||
|
@ -89,22 +89,31 @@ class Impl : public dap::Session {
|
||||
handlers.put(seq, responseTypeInfo, responseHandler);
|
||||
|
||||
dap::json::Serializer s;
|
||||
s.field("seq", dap::integer(seq));
|
||||
s.field("type", "request");
|
||||
s.field("command", requestTypeInfo->name());
|
||||
s.field("arguments", [&](dap::Serializer* s) {
|
||||
if (!s.object([&](dap::FieldSerializer* fs) {
|
||||
return fs->field("seq", dap::integer(seq)) &&
|
||||
fs->field("type", "request") &&
|
||||
fs->field("command", requestTypeInfo->name()) &&
|
||||
fs->field("arguments", [&](dap::Serializer* s) {
|
||||
return requestTypeInfo->serialize(s, request);
|
||||
});
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
return send(s.dump());
|
||||
}
|
||||
|
||||
bool send(const dap::TypeInfo* typeinfo, const void* event) override {
|
||||
dap::json::Serializer s;
|
||||
s.field("seq", dap::integer(nextSeq++));
|
||||
s.field("type", "event");
|
||||
s.field("event", typeinfo->name());
|
||||
s.field("body",
|
||||
[&](dap::Serializer* s) { return typeinfo->serialize(s, event); });
|
||||
if (!s.object([&](dap::FieldSerializer* fs) {
|
||||
return fs->field("seq", dap::integer(nextSeq++)) &&
|
||||
fs->field("type", "event") &&
|
||||
fs->field("event", typeinfo->name()) &&
|
||||
fs->field("body", [&](dap::Serializer* s) {
|
||||
return typeinfo->serialize(s, event);
|
||||
});
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
return send(s.dump());
|
||||
}
|
||||
|
||||
@ -319,14 +328,16 @@ class Impl : public dap::Session {
|
||||
[&](const dap::TypeInfo* typeinfo, const void* data) {
|
||||
// onSuccess
|
||||
dap::json::Serializer s;
|
||||
s.field("seq", dap::integer(nextSeq++));
|
||||
s.field("type", "response");
|
||||
s.field("request_seq", sequence);
|
||||
s.field("success", dap::boolean(true));
|
||||
s.field("command", command);
|
||||
s.field("body", [&](dap::Serializer* s) {
|
||||
s.object([&](dap::FieldSerializer* fs) {
|
||||
return fs->field("seq", dap::integer(nextSeq++)) &&
|
||||
fs->field("type", "response") &&
|
||||
fs->field("request_seq", sequence) &&
|
||||
fs->field("success", dap::boolean(true)) &&
|
||||
fs->field("command", command) &&
|
||||
fs->field("body", [&](dap::Serializer* s) {
|
||||
return typeinfo->serialize(s, data);
|
||||
});
|
||||
});
|
||||
send(s.dump());
|
||||
|
||||
if (auto handler = handlers.responseSent(typeinfo)) {
|
||||
@ -336,12 +347,14 @@ class Impl : public dap::Session {
|
||||
[&](const dap::TypeInfo* typeinfo, const dap::Error& error) {
|
||||
// onError
|
||||
dap::json::Serializer s;
|
||||
s.field("seq", dap::integer(nextSeq++));
|
||||
s.field("type", "response");
|
||||
s.field("request_seq", sequence);
|
||||
s.field("success", dap::boolean(false));
|
||||
s.field("command", command);
|
||||
s.field("message", error.message);
|
||||
s.object([&](dap::FieldSerializer* fs) {
|
||||
return fs->field("seq", dap::integer(nextSeq++)) &&
|
||||
fs->field("type", "response") &&
|
||||
fs->field("request_seq", sequence) &&
|
||||
fs->field("success", dap::boolean(false)) &&
|
||||
fs->field("command", command) &&
|
||||
fs->field("message", error.message);
|
||||
});
|
||||
send(s.dump());
|
||||
|
||||
if (auto handler = handlers.responseSent(typeinfo)) {
|
||||
|
65
src/typeinfo_test.cpp
Normal file
65
src/typeinfo_test.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "dap/typeof.h"
|
||||
#include "dap/types.h"
|
||||
#include "json_serializer.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace dap {
|
||||
|
||||
struct BaseStruct {
|
||||
dap::integer i;
|
||||
dap::number n;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO(BaseStruct,
|
||||
"BaseStruct",
|
||||
DAP_FIELD(i, "i"),
|
||||
DAP_FIELD(n, "n"));
|
||||
|
||||
struct DerivedStruct : public BaseStruct {
|
||||
dap::string s;
|
||||
dap::boolean b;
|
||||
};
|
||||
|
||||
DAP_STRUCT_TYPEINFO_EXT(DerivedStruct,
|
||||
BaseStruct,
|
||||
"DerivedStruct",
|
||||
DAP_FIELD(s, "s"),
|
||||
DAP_FIELD(b, "b"));
|
||||
|
||||
} // namespace dap
|
||||
|
||||
TEST(TypeInfo, Derived) {
|
||||
dap::DerivedStruct in;
|
||||
in.s = "hello world";
|
||||
in.b = true;
|
||||
in.i = 42;
|
||||
in.n = 3.14;
|
||||
|
||||
dap::json::Serializer s;
|
||||
ASSERT_TRUE(s.serialize(in));
|
||||
|
||||
dap::DerivedStruct out;
|
||||
dap::json::Deserializer d(s.dump());
|
||||
ASSERT_TRUE(d.deserialize(&out)) << "Failed to deserialize\n" << s.dump();
|
||||
|
||||
ASSERT_EQ(out.s, "hello world");
|
||||
ASSERT_EQ(out.b, true);
|
||||
ASSERT_EQ(out.i, 42);
|
||||
ASSERT_EQ(out.n, 3.14);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user