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:
Ben Clayton
2020-06-02 16:40:44 +01:00
parent c9187480d1
commit cc93ba9747
8 changed files with 254 additions and 121 deletions

View File

@@ -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) {
Serializer s(&(*json)[name]);
auto res = cb(&s);
if (s.removed) {
json->erase(name);
}
return res;
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() {

View File

@@ -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) {

View File

@@ -20,8 +20,6 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <nlohmann/json.hpp>
namespace dap {
struct JSONInnerTestObject {

View File

@@ -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) {
return requestTypeInfo->serialize(s, request);
});
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,13 +328,15 @@ 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) {
return typeinfo->serialize(s, data);
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());
@@ -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
View 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);
}