Reduce code duplication between serialization generators
This commit is contained in:
parent
e93be04e35
commit
c170993392
|
@ -8,6 +8,7 @@ set(LINK_TESTS_AGAINST_APP_TARGET ON)
|
||||||
# add project files
|
# add project files
|
||||||
set(HEADER_FILES
|
set(HEADER_FILES
|
||||||
codegenerator.h
|
codegenerator.h
|
||||||
|
serializationcodegenerator.h
|
||||||
jsonserializationcodegenerator.h
|
jsonserializationcodegenerator.h
|
||||||
binaryserializationcodegenerator.h
|
binaryserializationcodegenerator.h
|
||||||
codefactory.h
|
codefactory.h
|
||||||
|
@ -18,6 +19,7 @@ set(HEADER_FILES
|
||||||
)
|
)
|
||||||
set(SRC_FILES
|
set(SRC_FILES
|
||||||
codegenerator.cpp
|
codegenerator.cpp
|
||||||
|
serializationcodegenerator.cpp
|
||||||
jsonserializationcodegenerator.cpp
|
jsonserializationcodegenerator.cpp
|
||||||
binaryserializationcodegenerator.cpp
|
binaryserializationcodegenerator.cpp
|
||||||
codefactory.cpp
|
codefactory.cpp
|
||||||
|
|
|
@ -26,50 +26,12 @@ BinarySerializationCodeGenerator::Options::Options()
|
||||||
visibilityArg.setPreDefinedCompletionValues("LIB_EXPORT");
|
visibilityArg.setPreDefinedCompletionValues("LIB_EXPORT");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
BinarySerializationCodeGenerator::BinarySerializationCodeGenerator(CodeFactory &factory, const Options &options)
|
||||||
* \brief Adds all class declarations (to the internal member variable m_records).
|
: SerializationCodeGenerator(factory)
|
||||||
* \remarks "AdaptedBinarySerializable" specializations are directly filtered and added to m_adaptionRecords (instead of m_records).
|
, m_options(options)
|
||||||
*/
|
|
||||||
void BinarySerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
|
||||||
{
|
{
|
||||||
switch (decl->getKind()) {
|
m_qualifiedNameOfRecords = BinarySerializable<void>::qualifiedName;
|
||||||
case clang::Decl::Kind::CXXRecord:
|
m_qualifiedNameOfAdaptionRecords = AdaptedBinarySerializable<void>::qualifiedName;
|
||||||
case clang::Decl::Kind::ClassTemplateSpecialization: {
|
|
||||||
auto *const record = static_cast<clang::CXXRecordDecl *>(decl);
|
|
||||||
// skip forward declarations
|
|
||||||
if (!record->hasDefinition()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for template specializations to adapt a 3rd party class/struct
|
|
||||||
if (decl->getKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
|
|
||||||
auto *const templateSpecializationRecord = static_cast<clang::ClassTemplateSpecializationDecl *>(decl);
|
|
||||||
// check whether the name of the template specialization matches
|
|
||||||
if (templateSpecializationRecord->getQualifiedNameAsString() == AdaptedBinarySerializable<void>::qualifiedName) {
|
|
||||||
// get the template argument of the template specialization (exactly one argument expected)
|
|
||||||
const auto &templateArgs = templateSpecializationRecord->getTemplateArgs();
|
|
||||||
if (templateArgs.size() != 1 || templateArgs.get(0).getKind() != clang::TemplateArgument::Type) {
|
|
||||||
return; // FIXME: use Clang diagnostics to print warning
|
|
||||||
}
|
|
||||||
// get the type the template argument refers to (that's the type of the 3rd party class/struct to adapt)
|
|
||||||
auto *const templateRecord = templateArgs.get(0).getAsType()->getAsCXXRecordDecl();
|
|
||||||
if (!templateRecord) {
|
|
||||||
return; // FIXME: use Clang diagnostics to print warning
|
|
||||||
}
|
|
||||||
// save the relevant information for the code generation
|
|
||||||
m_adaptionRecords.emplace_back(templateRecord->getQualifiedNameAsString(), templateSpecializationRecord);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add any other records
|
|
||||||
m_records.emplace_back(record);
|
|
||||||
} break;
|
|
||||||
case clang::Decl::Kind::Enum:
|
|
||||||
// TODO: add enums
|
|
||||||
break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -77,26 +39,14 @@ void BinarySerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
||||||
*/
|
*/
|
||||||
string BinarySerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const
|
string BinarySerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const
|
||||||
{
|
{
|
||||||
// consider all classes for which a specialization of the "AdaptedBinarySerializable" struct is available
|
|
||||||
const string qualifiedName(record->getQualifiedNameAsString());
|
const string qualifiedName(record->getQualifiedNameAsString());
|
||||||
for (const auto &adaptionRecord : m_adaptionRecords) {
|
switch (isQualifiedNameIfRelevant(record, qualifiedName)) {
|
||||||
// skip all adaption records which are only included
|
case IsRelevant::Yes:
|
||||||
if (isOnlyIncluded(adaptionRecord.record)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (adaptionRecord.qualifiedName == qualifiedName) {
|
|
||||||
return qualifiedName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip all classes which are only included
|
|
||||||
if (isOnlyIncluded(record)) {
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
// consider all classes inheriting from an instantiation of "BinarySerializable" relevant
|
|
||||||
if (inheritsFromInstantiationOf(record, BinarySerializable<void>::qualifiedName)) {
|
|
||||||
return qualifiedName;
|
return qualifiedName;
|
||||||
|
case IsRelevant::No:
|
||||||
|
return string();
|
||||||
|
default:
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
// consider all classes specified via "--additional-classes" argument relevant
|
// consider all classes specified via "--additional-classes" argument relevant
|
||||||
|
@ -112,37 +62,6 @@ string BinarySerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecor
|
||||||
return string();
|
return string();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Searches the records added via addDeclaration() and returns the relevant ones.
|
|
||||||
* \sa Whether a record is relevant is determined using the qualifiedNameIfRelevant() method.
|
|
||||||
*/
|
|
||||||
std::vector<BinarySerializationCodeGenerator::RelevantClass> BinarySerializationCodeGenerator::findRelevantClasses() const
|
|
||||||
{
|
|
||||||
std::vector<RelevantClass> relevantClasses;
|
|
||||||
for (clang::CXXRecordDecl *record : m_records) {
|
|
||||||
string qualifiedName(qualifiedNameIfRelevant(record));
|
|
||||||
if (!qualifiedName.empty()) {
|
|
||||||
relevantClasses.emplace_back(move(qualifiedName), record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return relevantClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Returns the relevant base classes of the specified \a relevantClass. All base classes in \a relevantBases are considered relevant.
|
|
||||||
*/
|
|
||||||
std::vector<const BinarySerializationCodeGenerator::RelevantClass *> BinarySerializationCodeGenerator::findRelevantBaseClasses(
|
|
||||||
const RelevantClass &relevantClass, const std::vector<RelevantClass> &relevantBases)
|
|
||||||
{
|
|
||||||
vector<const RelevantClass *> relevantBaseClasses;
|
|
||||||
for (const RelevantClass &otherClass : relevantBases) {
|
|
||||||
if (relevantClass.record != otherClass.record && relevantClass.record->isDerivedFrom(otherClass.record)) {
|
|
||||||
relevantBaseClasses.push_back(&otherClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return relevantBaseClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Generates pull() and push() helper functions in the ReflectiveRapidJSON::BinaryReflector namespace for the relevant classes.
|
* \brief Generates pull() and push() helper functions in the ReflectiveRapidJSON::BinaryReflector namespace for the relevant classes.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef REFLECTIVE_RAPIDJSON_CODE_BINARY_SERIALIZATION_GENERATOR_H
|
#ifndef REFLECTIVE_RAPIDJSON_CODE_BINARY_SERIALIZATION_GENERATOR_H
|
||||||
#define REFLECTIVE_RAPIDJSON_CODE_BINARY_SERIALIZATION_GENERATOR_H
|
#define REFLECTIVE_RAPIDJSON_CODE_BINARY_SERIALIZATION_GENERATOR_H
|
||||||
|
|
||||||
#include "./codegenerator.h"
|
#include "./serializationcodegenerator.h"
|
||||||
|
|
||||||
#include <c++utilities/application/argumentparser.h>
|
#include <c++utilities/application/argumentparser.h>
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ namespace ReflectiveRapidJSON {
|
||||||
* \brief The BinarySerializationCodeGenerator class generates code for JSON (de)serialization
|
* \brief The BinarySerializationCodeGenerator class generates code for JSON (de)serialization
|
||||||
* of objects inheriting from an instantiation of JsonSerializable.
|
* of objects inheriting from an instantiation of JsonSerializable.
|
||||||
*/
|
*/
|
||||||
class BinarySerializationCodeGenerator : public CodeGenerator {
|
class BinarySerializationCodeGenerator : public SerializationCodeGenerator {
|
||||||
public:
|
public:
|
||||||
struct Options {
|
struct Options {
|
||||||
Options();
|
Options();
|
||||||
|
@ -22,49 +22,22 @@ public:
|
||||||
ApplicationUtilities::ConfigValueArgument visibilityArg;
|
ApplicationUtilities::ConfigValueArgument visibilityArg;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
struct RelevantClass {
|
|
||||||
explicit RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record);
|
|
||||||
|
|
||||||
std::string qualifiedName;
|
|
||||||
clang::CXXRecordDecl *record;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
BinarySerializationCodeGenerator(CodeFactory &factory, const Options &options);
|
BinarySerializationCodeGenerator(CodeFactory &factory, const Options &options);
|
||||||
|
|
||||||
void addDeclaration(clang::Decl *decl) override;
|
|
||||||
void generate(std::ostream &os) const override;
|
void generate(std::ostream &os) const override;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const;
|
std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const override;
|
||||||
std::vector<RelevantClass> findRelevantClasses() const;
|
|
||||||
static std::vector<const RelevantClass *> findRelevantBaseClasses(
|
|
||||||
const RelevantClass &relevantClass, const std::vector<RelevantClass> &relevantBases);
|
|
||||||
|
|
||||||
std::vector<clang::CXXRecordDecl *> m_records;
|
|
||||||
std::vector<RelevantClass> m_adaptionRecords;
|
|
||||||
const Options &m_options;
|
const Options &m_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline BinarySerializationCodeGenerator::BinarySerializationCodeGenerator(CodeFactory &factory, const Options &options)
|
|
||||||
: CodeGenerator(factory)
|
|
||||||
, m_options(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void BinarySerializationCodeGenerator::Options::appendTo(ApplicationUtilities::Argument *arg)
|
inline void BinarySerializationCodeGenerator::Options::appendTo(ApplicationUtilities::Argument *arg)
|
||||||
{
|
{
|
||||||
arg->addSubArgument(&additionalClassesArg);
|
arg->addSubArgument(&additionalClassesArg);
|
||||||
arg->addSubArgument(&visibilityArg);
|
arg->addSubArgument(&visibilityArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline BinarySerializationCodeGenerator::RelevantClass::RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record)
|
|
||||||
: qualifiedName(qualifiedName)
|
|
||||||
, record(record)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ReflectiveRapidJSON
|
} // namespace ReflectiveRapidJSON
|
||||||
|
|
||||||
#endif // REFLECTIVE_RAPIDJSON_CODE_BINARY_SERIALIZATION_GENERATOR_H
|
#endif // REFLECTIVE_RAPIDJSON_CODE_BINARY_SERIALIZATION_GENERATOR_H
|
||||||
|
|
|
@ -10,14 +10,6 @@ using namespace std;
|
||||||
|
|
||||||
namespace ReflectiveRapidJSON {
|
namespace ReflectiveRapidJSON {
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Prints an LLVM string reference without instantiating a std::string first.
|
|
||||||
*/
|
|
||||||
ostream &operator<<(ostream &os, llvm::StringRef str)
|
|
||||||
{
|
|
||||||
return os.write(str.data(), static_cast<streamsize>(str.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeGenerator::~CodeGenerator()
|
CodeGenerator::~CodeGenerator()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <llvm/ADT/StringRef.h>
|
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
class Decl;
|
class Decl;
|
||||||
class CXXRecordDecl;
|
class CXXRecordDecl;
|
||||||
|
@ -17,8 +15,6 @@ namespace ReflectiveRapidJSON {
|
||||||
|
|
||||||
class CodeFactory;
|
class CodeFactory;
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, llvm::StringRef str);
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief The CodeGenerator class is the base for generators used by the CodeFactory class.
|
* \brief The CodeGenerator class is the base for generators used by the CodeFactory class.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -26,50 +26,12 @@ JsonSerializationCodeGenerator::Options::Options()
|
||||||
visibilityArg.setPreDefinedCompletionValues("LIB_EXPORT");
|
visibilityArg.setPreDefinedCompletionValues("LIB_EXPORT");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
JsonSerializationCodeGenerator::JsonSerializationCodeGenerator(CodeFactory &factory, const Options &options)
|
||||||
* \brief Adds all class declarations (to the internal member variable m_records).
|
: SerializationCodeGenerator(factory)
|
||||||
* \remarks "AdaptedJsonSerializable" specializations are directly filtered and added to m_adaptionRecords (instead of m_records).
|
, m_options(options)
|
||||||
*/
|
|
||||||
void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
|
||||||
{
|
{
|
||||||
switch (decl->getKind()) {
|
m_qualifiedNameOfRecords = JsonSerializable<void>::qualifiedName;
|
||||||
case clang::Decl::Kind::CXXRecord:
|
m_qualifiedNameOfAdaptionRecords = AdaptedJsonSerializable<void>::qualifiedName;
|
||||||
case clang::Decl::Kind::ClassTemplateSpecialization: {
|
|
||||||
auto *const record = static_cast<clang::CXXRecordDecl *>(decl);
|
|
||||||
// skip forward declarations
|
|
||||||
if (!record->hasDefinition()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for template specializations to adapt a 3rd party class/struct
|
|
||||||
if (decl->getKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
|
|
||||||
auto *const templateSpecializationRecord = static_cast<clang::ClassTemplateSpecializationDecl *>(decl);
|
|
||||||
// check whether the name of the template specialization matches
|
|
||||||
if (templateSpecializationRecord->getQualifiedNameAsString() == AdaptedJsonSerializable<void>::qualifiedName) {
|
|
||||||
// get the template argument of the template specialization (exactly one argument expected)
|
|
||||||
const auto &templateArgs = templateSpecializationRecord->getTemplateArgs();
|
|
||||||
if (templateArgs.size() != 1 || templateArgs.get(0).getKind() != clang::TemplateArgument::Type) {
|
|
||||||
return; // FIXME: use Clang diagnostics to print warning
|
|
||||||
}
|
|
||||||
// get the type the template argument refers to (that's the type of the 3rd party class/struct to adapt)
|
|
||||||
auto *const templateRecord = templateArgs.get(0).getAsType()->getAsCXXRecordDecl();
|
|
||||||
if (!templateRecord) {
|
|
||||||
return; // FIXME: use Clang diagnostics to print warning
|
|
||||||
}
|
|
||||||
// save the relevant information for the code generation
|
|
||||||
m_adaptionRecords.emplace_back(templateRecord->getQualifiedNameAsString(), templateSpecializationRecord);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add any other records
|
|
||||||
m_records.emplace_back(record);
|
|
||||||
} break;
|
|
||||||
case clang::Decl::Kind::Enum:
|
|
||||||
// TODO: add enums
|
|
||||||
break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -77,26 +39,14 @@ void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
||||||
*/
|
*/
|
||||||
string JsonSerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const
|
string JsonSerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const
|
||||||
{
|
{
|
||||||
// consider all classes for which a specialization of the "AdaptedJsonSerializable" struct is available
|
|
||||||
const string qualifiedName(record->getQualifiedNameAsString());
|
const string qualifiedName(record->getQualifiedNameAsString());
|
||||||
for (const auto &adaptionRecord : m_adaptionRecords) {
|
switch (isQualifiedNameIfRelevant(record, qualifiedName)) {
|
||||||
// skip all adaption records which are only included
|
case IsRelevant::Yes:
|
||||||
if (isOnlyIncluded(adaptionRecord.record)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (adaptionRecord.qualifiedName == qualifiedName) {
|
|
||||||
return qualifiedName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip all classes which are only included
|
|
||||||
if (isOnlyIncluded(record)) {
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
// consider all classes inheriting from an instantiation of "JsonSerializable" relevant
|
|
||||||
if (inheritsFromInstantiationOf(record, JsonSerializable<void>::qualifiedName)) {
|
|
||||||
return qualifiedName;
|
return qualifiedName;
|
||||||
|
case IsRelevant::No:
|
||||||
|
return string();
|
||||||
|
default:
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
// consider all classes specified via "--additional-classes" argument relevant
|
// consider all classes specified via "--additional-classes" argument relevant
|
||||||
|
@ -112,37 +62,6 @@ string JsonSerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecordD
|
||||||
return string();
|
return string();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Searches the records added via addDeclaration() and returns the relevant ones.
|
|
||||||
* \sa Whether a record is relevant is determined using the qualifiedNameIfRelevant() method.
|
|
||||||
*/
|
|
||||||
std::vector<JsonSerializationCodeGenerator::RelevantClass> JsonSerializationCodeGenerator::findRelevantClasses() const
|
|
||||||
{
|
|
||||||
std::vector<RelevantClass> relevantClasses;
|
|
||||||
for (clang::CXXRecordDecl *record : m_records) {
|
|
||||||
string qualifiedName(qualifiedNameIfRelevant(record));
|
|
||||||
if (!qualifiedName.empty()) {
|
|
||||||
relevantClasses.emplace_back(move(qualifiedName), record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return relevantClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Returns the relevant base classes of the specified \a relevantClass. All base classes in \a relevantBases are considered relevant.
|
|
||||||
*/
|
|
||||||
std::vector<const JsonSerializationCodeGenerator::RelevantClass *> JsonSerializationCodeGenerator::findRelevantBaseClasses(
|
|
||||||
const RelevantClass &relevantClass, const std::vector<RelevantClass> &relevantBases)
|
|
||||||
{
|
|
||||||
vector<const RelevantClass *> relevantBaseClasses;
|
|
||||||
for (const RelevantClass &otherClass : relevantBases) {
|
|
||||||
if (relevantClass.record != otherClass.record && relevantClass.record->isDerivedFrom(otherClass.record)) {
|
|
||||||
relevantBaseClasses.push_back(&otherClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return relevantBaseClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Generates pull() and push() helper functions in the ReflectiveRapidJSON::JsonReflector namespace for the relevant classes.
|
* \brief Generates pull() and push() helper functions in the ReflectiveRapidJSON::JsonReflector namespace for the relevant classes.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
#ifndef REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
||||||
#define REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
#define REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
||||||
|
|
||||||
#include "./codegenerator.h"
|
#include "./serializationcodegenerator.h"
|
||||||
|
|
||||||
#include <c++utilities/application/argumentparser.h>
|
#include <c++utilities/application/argumentparser.h>
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ namespace ReflectiveRapidJSON {
|
||||||
* \brief The JsonSerializationCodeGenerator class generates code for JSON (de)serialization
|
* \brief The JsonSerializationCodeGenerator class generates code for JSON (de)serialization
|
||||||
* of objects inheriting from an instantiation of JsonSerializable.
|
* of objects inheriting from an instantiation of JsonSerializable.
|
||||||
*/
|
*/
|
||||||
class JsonSerializationCodeGenerator : public CodeGenerator {
|
class JsonSerializationCodeGenerator : public SerializationCodeGenerator {
|
||||||
public:
|
public:
|
||||||
struct Options {
|
struct Options {
|
||||||
Options();
|
Options();
|
||||||
|
@ -22,49 +22,22 @@ public:
|
||||||
ApplicationUtilities::ConfigValueArgument visibilityArg;
|
ApplicationUtilities::ConfigValueArgument visibilityArg;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
|
||||||
struct RelevantClass {
|
|
||||||
explicit RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record);
|
|
||||||
|
|
||||||
std::string qualifiedName;
|
|
||||||
clang::CXXRecordDecl *record;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
JsonSerializationCodeGenerator(CodeFactory &factory, const Options &options);
|
JsonSerializationCodeGenerator(CodeFactory &factory, const Options &options);
|
||||||
|
|
||||||
void addDeclaration(clang::Decl *decl) override;
|
|
||||||
void generate(std::ostream &os) const override;
|
void generate(std::ostream &os) const override;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const;
|
std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const override;
|
||||||
std::vector<RelevantClass> findRelevantClasses() const;
|
|
||||||
static std::vector<const RelevantClass *> findRelevantBaseClasses(
|
|
||||||
const RelevantClass &relevantClass, const std::vector<RelevantClass> &relevantBases);
|
|
||||||
|
|
||||||
std::vector<clang::CXXRecordDecl *> m_records;
|
|
||||||
std::vector<RelevantClass> m_adaptionRecords;
|
|
||||||
const Options &m_options;
|
const Options &m_options;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline JsonSerializationCodeGenerator::JsonSerializationCodeGenerator(CodeFactory &factory, const Options &options)
|
|
||||||
: CodeGenerator(factory)
|
|
||||||
, m_options(options)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void JsonSerializationCodeGenerator::Options::appendTo(ApplicationUtilities::Argument *arg)
|
inline void JsonSerializationCodeGenerator::Options::appendTo(ApplicationUtilities::Argument *arg)
|
||||||
{
|
{
|
||||||
arg->addSubArgument(&additionalClassesArg);
|
arg->addSubArgument(&additionalClassesArg);
|
||||||
arg->addSubArgument(&visibilityArg);
|
arg->addSubArgument(&visibilityArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline JsonSerializationCodeGenerator::RelevantClass::RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record)
|
|
||||||
: qualifiedName(qualifiedName)
|
|
||||||
, record(record)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ReflectiveRapidJSON
|
} // namespace ReflectiveRapidJSON
|
||||||
|
|
||||||
#endif // REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
#endif // REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include "./serializationcodegenerator.h"
|
||||||
|
|
||||||
|
#include <c++utilities/application/global.h>
|
||||||
|
|
||||||
|
#include <clang/AST/DeclCXX.h>
|
||||||
|
#include <clang/AST/DeclFriend.h>
|
||||||
|
#include <clang/AST/DeclTemplate.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace ReflectiveRapidJSON {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Prints an LLVM string reference without instantiating a std::string first.
|
||||||
|
*/
|
||||||
|
ostream &operator<<(ostream &os, llvm::StringRef str)
|
||||||
|
{
|
||||||
|
return os.write(str.data(), static_cast<streamsize>(str.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Adds all class declarations (to the internal member variable m_records).
|
||||||
|
* \remarks "AdaptedXXXSerializable" specializations are directly filtered and added to m_adaptionRecords (instead of m_records).
|
||||||
|
*/
|
||||||
|
void SerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
||||||
|
{
|
||||||
|
switch (decl->getKind()) {
|
||||||
|
case clang::Decl::Kind::CXXRecord:
|
||||||
|
case clang::Decl::Kind::ClassTemplateSpecialization: {
|
||||||
|
auto *const record = static_cast<clang::CXXRecordDecl *>(decl);
|
||||||
|
// skip forward declarations
|
||||||
|
if (!record->hasDefinition()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for template specializations to adapt a 3rd party class/struct
|
||||||
|
if (m_qualifiedNameOfAdaptionRecords && decl->getKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
|
||||||
|
auto *const templateSpecializationRecord = static_cast<clang::ClassTemplateSpecializationDecl *>(decl);
|
||||||
|
// check whether the name of the template specialization matches
|
||||||
|
if (templateSpecializationRecord->getQualifiedNameAsString() == m_qualifiedNameOfAdaptionRecords) {
|
||||||
|
// get the template argument of the template specialization (exactly one argument expected)
|
||||||
|
const auto &templateArgs = templateSpecializationRecord->getTemplateArgs();
|
||||||
|
if (templateArgs.size() != 1 || templateArgs.get(0).getKind() != clang::TemplateArgument::Type) {
|
||||||
|
return; // FIXME: use Clang diagnostics to print warning
|
||||||
|
}
|
||||||
|
// get the type the template argument refers to (that's the type of the 3rd party class/struct to adapt)
|
||||||
|
auto *const templateRecord = templateArgs.get(0).getAsType()->getAsCXXRecordDecl();
|
||||||
|
if (!templateRecord) {
|
||||||
|
return; // FIXME: use Clang diagnostics to print warning
|
||||||
|
}
|
||||||
|
// save the relevant information for the code generation
|
||||||
|
m_adaptionRecords.emplace_back(templateRecord->getQualifiedNameAsString(), templateSpecializationRecord);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add any other records
|
||||||
|
m_records.emplace_back(record);
|
||||||
|
} break;
|
||||||
|
case clang::Decl::Kind::Enum:
|
||||||
|
// TODO: add enums
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializationCodeGenerator::IsRelevant SerializationCodeGenerator::isQualifiedNameIfRelevant(clang::CXXRecordDecl *record, const std::string &qualifiedName) const
|
||||||
|
{
|
||||||
|
// consider all classes for which a specialization of the "AdaptedJsonSerializable" struct is available
|
||||||
|
for (const auto &adaptionRecord : m_adaptionRecords) {
|
||||||
|
// skip all adaption records which are only included
|
||||||
|
if (isOnlyIncluded(adaptionRecord.record)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (adaptionRecord.qualifiedName == qualifiedName) {
|
||||||
|
return IsRelevant::Yes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip all classes which are only included
|
||||||
|
if (isOnlyIncluded(record)) {
|
||||||
|
return IsRelevant::No;
|
||||||
|
}
|
||||||
|
|
||||||
|
// consider all classes inheriting from an instantiation of "JsonSerializable" relevant
|
||||||
|
if (inheritsFromInstantiationOf(record, m_qualifiedNameOfRecords)) {
|
||||||
|
return IsRelevant::Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsRelevant::Maybe;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SerializationCodeGenerator::RelevantClass> SerializationCodeGenerator::findRelevantClasses() const
|
||||||
|
{
|
||||||
|
std::vector<RelevantClass> relevantClasses;
|
||||||
|
for (clang::CXXRecordDecl *record : m_records) {
|
||||||
|
string qualifiedName(qualifiedNameIfRelevant(record));
|
||||||
|
if (!qualifiedName.empty()) {
|
||||||
|
relevantClasses.emplace_back(move(qualifiedName), record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return relevantClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const SerializationCodeGenerator::RelevantClass *> SerializationCodeGenerator::findRelevantBaseClasses(const SerializationCodeGenerator::RelevantClass &relevantClass, const std::vector<SerializationCodeGenerator::RelevantClass> &relevantBases)
|
||||||
|
{
|
||||||
|
vector<const RelevantClass *> relevantBaseClasses;
|
||||||
|
for (const RelevantClass &otherClass : relevantBases) {
|
||||||
|
if (relevantClass.record != otherClass.record && relevantClass.record->isDerivedFrom(otherClass.record)) {
|
||||||
|
relevantBaseClasses.push_back(&otherClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return relevantBaseClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ReflectiveRapidJSON
|
|
@ -0,0 +1,63 @@
|
||||||
|
#ifndef REFLECTIVE_RAPIDJSON_SERIALIZATION_CODE_GENERATOR_H
|
||||||
|
#define REFLECTIVE_RAPIDJSON_SERIALIZATION_CODE_GENERATOR_H
|
||||||
|
|
||||||
|
#include "./codegenerator.h"
|
||||||
|
|
||||||
|
#include <llvm/ADT/StringRef.h>
|
||||||
|
|
||||||
|
namespace ReflectiveRapidJSON {
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, llvm::StringRef str);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The SerializationCodeGenerator class is the common base for (de)serialization
|
||||||
|
* related code generation.
|
||||||
|
*/
|
||||||
|
class SerializationCodeGenerator : public CodeGenerator {
|
||||||
|
public:
|
||||||
|
struct RelevantClass {
|
||||||
|
explicit RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record);
|
||||||
|
|
||||||
|
std::string qualifiedName;
|
||||||
|
clang::CXXRecordDecl *record;
|
||||||
|
};
|
||||||
|
|
||||||
|
SerializationCodeGenerator(CodeFactory &factory);
|
||||||
|
|
||||||
|
void addDeclaration(clang::Decl *decl) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum class IsRelevant {
|
||||||
|
Yes, No, Maybe
|
||||||
|
};
|
||||||
|
IsRelevant isQualifiedNameIfRelevant(clang::CXXRecordDecl *record, const std::string &qualifiedName) const;
|
||||||
|
virtual std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const = 0;
|
||||||
|
std::vector<RelevantClass> findRelevantClasses() const;
|
||||||
|
static std::vector<const RelevantClass *> findRelevantBaseClasses(
|
||||||
|
const RelevantClass &relevantClass, const std::vector<RelevantClass> &relevantBases);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const char *m_qualifiedNameOfRecords;
|
||||||
|
const char *m_qualifiedNameOfAdaptionRecords;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<clang::CXXRecordDecl *> m_records;
|
||||||
|
std::vector<RelevantClass> m_adaptionRecords;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline SerializationCodeGenerator::RelevantClass::RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record)
|
||||||
|
: qualifiedName(qualifiedName)
|
||||||
|
, record(record)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline SerializationCodeGenerator::SerializationCodeGenerator(CodeFactory &factory)
|
||||||
|
: CodeGenerator(factory)
|
||||||
|
, m_qualifiedNameOfRecords(nullptr)
|
||||||
|
, m_qualifiedNameOfAdaptionRecords(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ReflectiveRapidJSON
|
||||||
|
|
||||||
|
#endif // REFLECTIVE_RAPIDJSON_SERIALIZATION_CODE_GENERATOR_H
|
Loading…
Reference in New Issue