Reduce code duplication between serialization generators

This commit is contained in:
Martchus 2018-06-23 18:04:30 +02:00
parent e93be04e35
commit c170993392
9 changed files with 211 additions and 258 deletions

View File

@ -8,6 +8,7 @@ set(LINK_TESTS_AGAINST_APP_TARGET ON)
# add project files
set(HEADER_FILES
codegenerator.h
serializationcodegenerator.h
jsonserializationcodegenerator.h
binaryserializationcodegenerator.h
codefactory.h
@ -18,6 +19,7 @@ set(HEADER_FILES
)
set(SRC_FILES
codegenerator.cpp
serializationcodegenerator.cpp
jsonserializationcodegenerator.cpp
binaryserializationcodegenerator.cpp
codefactory.cpp

View File

@ -26,50 +26,12 @@ BinarySerializationCodeGenerator::Options::Options()
visibilityArg.setPreDefinedCompletionValues("LIB_EXPORT");
}
/*!
* \brief Adds all class declarations (to the internal member variable m_records).
* \remarks "AdaptedBinarySerializable" specializations are directly filtered and added to m_adaptionRecords (instead of m_records).
*/
void BinarySerializationCodeGenerator::addDeclaration(clang::Decl *decl)
BinarySerializationCodeGenerator::BinarySerializationCodeGenerator(CodeFactory &factory, const Options &options)
: SerializationCodeGenerator(factory)
, m_options(options)
{
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 (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:;
}
m_qualifiedNameOfRecords = BinarySerializable<void>::qualifiedName;
m_qualifiedNameOfAdaptionRecords = AdaptedBinarySerializable<void>::qualifiedName;
}
/*!
@ -77,26 +39,14 @@ void BinarySerializationCodeGenerator::addDeclaration(clang::Decl *decl)
*/
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());
for (const auto &adaptionRecord : m_adaptionRecords) {
// skip all adaption records which are only included
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)) {
switch (isQualifiedNameIfRelevant(record, qualifiedName)) {
case IsRelevant::Yes:
return qualifiedName;
case IsRelevant::No:
return string();
default:
;
}
// consider all classes specified via "--additional-classes" argument relevant
@ -112,37 +62,6 @@ string BinarySerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecor
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.
*/

View File

@ -1,7 +1,7 @@
#ifndef 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>
@ -11,7 +11,7 @@ namespace ReflectiveRapidJSON {
* \brief The BinarySerializationCodeGenerator class generates code for JSON (de)serialization
* of objects inheriting from an instantiation of JsonSerializable.
*/
class BinarySerializationCodeGenerator : public CodeGenerator {
class BinarySerializationCodeGenerator : public SerializationCodeGenerator {
public:
struct Options {
Options();
@ -22,49 +22,22 @@ public:
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);
void addDeclaration(clang::Decl *decl) override;
void generate(std::ostream &os) const override;
private:
std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const;
std::vector<RelevantClass> findRelevantClasses() const;
static std::vector<const RelevantClass *> findRelevantBaseClasses(
const RelevantClass &relevantClass, const std::vector<RelevantClass> &relevantBases);
protected:
std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const override;
std::vector<clang::CXXRecordDecl *> m_records;
std::vector<RelevantClass> m_adaptionRecords;
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)
{
arg->addSubArgument(&additionalClassesArg);
arg->addSubArgument(&visibilityArg);
}
inline BinarySerializationCodeGenerator::RelevantClass::RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record)
: qualifiedName(qualifiedName)
, record(record)
{
}
} // namespace ReflectiveRapidJSON
#endif // REFLECTIVE_RAPIDJSON_CODE_BINARY_SERIALIZATION_GENERATOR_H

View File

@ -10,14 +10,6 @@ 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()));
}
CodeGenerator::~CodeGenerator()
{
}

View File

@ -5,8 +5,6 @@
#include <string>
#include <vector>
#include <llvm/ADT/StringRef.h>
namespace clang {
class Decl;
class CXXRecordDecl;
@ -17,8 +15,6 @@ namespace ReflectiveRapidJSON {
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.
*/

View File

@ -26,50 +26,12 @@ JsonSerializationCodeGenerator::Options::Options()
visibilityArg.setPreDefinedCompletionValues("LIB_EXPORT");
}
/*!
* \brief Adds all class declarations (to the internal member variable m_records).
* \remarks "AdaptedJsonSerializable" specializations are directly filtered and added to m_adaptionRecords (instead of m_records).
*/
void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
JsonSerializationCodeGenerator::JsonSerializationCodeGenerator(CodeFactory &factory, const Options &options)
: SerializationCodeGenerator(factory)
, m_options(options)
{
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 (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:;
}
m_qualifiedNameOfRecords = JsonSerializable<void>::qualifiedName;
m_qualifiedNameOfAdaptionRecords = AdaptedJsonSerializable<void>::qualifiedName;
}
/*!
@ -77,26 +39,14 @@ void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
*/
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());
for (const auto &adaptionRecord : m_adaptionRecords) {
// skip all adaption records which are only included
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)) {
switch (isQualifiedNameIfRelevant(record, qualifiedName)) {
case IsRelevant::Yes:
return qualifiedName;
case IsRelevant::No:
return string();
default:
;
}
// consider all classes specified via "--additional-classes" argument relevant
@ -112,37 +62,6 @@ string JsonSerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecordD
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.
*/

View File

@ -1,7 +1,7 @@
#ifndef 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>
@ -11,7 +11,7 @@ namespace ReflectiveRapidJSON {
* \brief The JsonSerializationCodeGenerator class generates code for JSON (de)serialization
* of objects inheriting from an instantiation of JsonSerializable.
*/
class JsonSerializationCodeGenerator : public CodeGenerator {
class JsonSerializationCodeGenerator : public SerializationCodeGenerator {
public:
struct Options {
Options();
@ -22,49 +22,22 @@ public:
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);
void addDeclaration(clang::Decl *decl) override;
void generate(std::ostream &os) const override;
private:
std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const;
std::vector<RelevantClass> findRelevantClasses() const;
static std::vector<const RelevantClass *> findRelevantBaseClasses(
const RelevantClass &relevantClass, const std::vector<RelevantClass> &relevantBases);
protected:
std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const override;
std::vector<clang::CXXRecordDecl *> m_records;
std::vector<RelevantClass> m_adaptionRecords;
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)
{
arg->addSubArgument(&additionalClassesArg);
arg->addSubArgument(&visibilityArg);
}
inline JsonSerializationCodeGenerator::RelevantClass::RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record)
: qualifiedName(qualifiedName)
, record(record)
{
}
} // namespace ReflectiveRapidJSON
#endif // REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H

View File

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

View File

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