diff --git a/generator/CMakeLists.txt b/generator/CMakeLists.txt index 6d46955..0bcefd7 100644 --- a/generator/CMakeLists.txt +++ b/generator/CMakeLists.txt @@ -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 diff --git a/generator/binaryserializationcodegenerator.cpp b/generator/binaryserializationcodegenerator.cpp index 199a5e7..b90cd36 100644 --- a/generator/binaryserializationcodegenerator.cpp +++ b/generator/binaryserializationcodegenerator.cpp @@ -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(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(decl); - // check whether the name of the template specialization matches - if (templateSpecializationRecord->getQualifiedNameAsString() == AdaptedBinarySerializable::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::qualifiedName; + m_qualifiedNameOfAdaptionRecords = AdaptedBinarySerializable::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::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::findRelevantClasses() const -{ - std::vector 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 BinarySerializationCodeGenerator::findRelevantBaseClasses( - const RelevantClass &relevantClass, const std::vector &relevantBases) -{ - vector 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. */ diff --git a/generator/binaryserializationcodegenerator.h b/generator/binaryserializationcodegenerator.h index c31204e..d5b1156 100644 --- a/generator/binaryserializationcodegenerator.h +++ b/generator/binaryserializationcodegenerator.h @@ -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 @@ -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 findRelevantClasses() const; - static std::vector findRelevantBaseClasses( - const RelevantClass &relevantClass, const std::vector &relevantBases); +protected: + std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const override; - std::vector m_records; - std::vector 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 diff --git a/generator/codegenerator.cpp b/generator/codegenerator.cpp index 379c2f7..b532f5d 100644 --- a/generator/codegenerator.cpp +++ b/generator/codegenerator.cpp @@ -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(str.size())); -} - CodeGenerator::~CodeGenerator() { } diff --git a/generator/codegenerator.h b/generator/codegenerator.h index 768ab69..0c5de9d 100644 --- a/generator/codegenerator.h +++ b/generator/codegenerator.h @@ -5,8 +5,6 @@ #include #include -#include - 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. */ diff --git a/generator/jsonserializationcodegenerator.cpp b/generator/jsonserializationcodegenerator.cpp index e0410d4..2de3a96 100644 --- a/generator/jsonserializationcodegenerator.cpp +++ b/generator/jsonserializationcodegenerator.cpp @@ -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(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(decl); - // check whether the name of the template specialization matches - if (templateSpecializationRecord->getQualifiedNameAsString() == AdaptedJsonSerializable::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::qualifiedName; + m_qualifiedNameOfAdaptionRecords = AdaptedJsonSerializable::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::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::findRelevantClasses() const -{ - std::vector 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 JsonSerializationCodeGenerator::findRelevantBaseClasses( - const RelevantClass &relevantClass, const std::vector &relevantBases) -{ - vector 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. */ diff --git a/generator/jsonserializationcodegenerator.h b/generator/jsonserializationcodegenerator.h index 747a700..18d9aa9 100644 --- a/generator/jsonserializationcodegenerator.h +++ b/generator/jsonserializationcodegenerator.h @@ -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 @@ -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 findRelevantClasses() const; - static std::vector findRelevantBaseClasses( - const RelevantClass &relevantClass, const std::vector &relevantBases); +protected: + std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const override; - std::vector m_records; - std::vector 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 diff --git a/generator/serializationcodegenerator.cpp b/generator/serializationcodegenerator.cpp new file mode 100644 index 0000000..9598cbf --- /dev/null +++ b/generator/serializationcodegenerator.cpp @@ -0,0 +1,116 @@ +#include "./serializationcodegenerator.h" + +#include + +#include +#include +#include + +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(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(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(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::findRelevantClasses() const +{ + std::vector relevantClasses; + for (clang::CXXRecordDecl *record : m_records) { + string qualifiedName(qualifiedNameIfRelevant(record)); + if (!qualifiedName.empty()) { + relevantClasses.emplace_back(move(qualifiedName), record); + } + } + return relevantClasses; +} + +std::vector SerializationCodeGenerator::findRelevantBaseClasses(const SerializationCodeGenerator::RelevantClass &relevantClass, const std::vector &relevantBases) +{ + vector relevantBaseClasses; + for (const RelevantClass &otherClass : relevantBases) { + if (relevantClass.record != otherClass.record && relevantClass.record->isDerivedFrom(otherClass.record)) { + relevantBaseClasses.push_back(&otherClass); + } + } + return relevantBaseClasses; +} + +} // namespace ReflectiveRapidJSON diff --git a/generator/serializationcodegenerator.h b/generator/serializationcodegenerator.h new file mode 100644 index 0000000..040ad6e --- /dev/null +++ b/generator/serializationcodegenerator.h @@ -0,0 +1,63 @@ +#ifndef REFLECTIVE_RAPIDJSON_SERIALIZATION_CODE_GENERATOR_H +#define REFLECTIVE_RAPIDJSON_SERIALIZATION_CODE_GENERATOR_H + +#include "./codegenerator.h" + +#include + +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 findRelevantClasses() const; + static std::vector findRelevantBaseClasses( + const RelevantClass &relevantClass, const std::vector &relevantBases); + +protected: + const char *m_qualifiedNameOfRecords; + const char *m_qualifiedNameOfAdaptionRecords; + +private: + std::vector m_records; + std::vector 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