From a7feb57f22a8fd7f11623eebcc19127c7f5ab1ef Mon Sep 17 00:00:00 2001 From: Martchus Date: Mon, 6 Nov 2017 15:31:21 +0100 Subject: [PATCH] Allow specifying additional classes for JSON serialization --- generator/codefactory.h | 6 +-- generator/jsonserializationcodegenerator.cpp | 40 +++++++++++++++++++- generator/jsonserializationcodegenerator.h | 25 +++++++++--- generator/main.cpp | 11 ++++-- generator/tests/jsongenerator.cpp | 3 +- 5 files changed, 69 insertions(+), 16 deletions(-) diff --git a/generator/codefactory.h b/generator/codefactory.h index 619ae19..9b202c0 100644 --- a/generator/codefactory.h +++ b/generator/codefactory.h @@ -33,7 +33,7 @@ public: ~CodeFactory(); const std::vector> &generators() const; - template void addGenerator(); + template void addGenerator(Args &&... args); bool run(); clang::CompilerInstance *compilerInstance(); @@ -55,9 +55,9 @@ private: clang::CompilerInstance *m_compilerInstance; }; -template void CodeFactory::addGenerator() +template void CodeFactory::addGenerator(Args &&... args) { - m_generators.emplace_back(std::make_unique(*this)); + m_generators.emplace_back(std::make_unique(*this, std::forward(args)...)); } inline const std::vector> &CodeFactory::generators() const diff --git a/generator/jsonserializationcodegenerator.cpp b/generator/jsonserializationcodegenerator.cpp index 8ed1f18..5dc5871 100644 --- a/generator/jsonserializationcodegenerator.cpp +++ b/generator/jsonserializationcodegenerator.cpp @@ -7,9 +7,18 @@ #include using namespace std; +using namespace ApplicationUtilities; namespace ReflectiveRapidJSON { +JsonSerializationCodeGenerator::Options::Options() + : additionalClassesArg("json-classes", '\0', "specifies additional classes to consider for JSON serialization") +{ + additionalClassesArg.setCombinable(true); + additionalClassesArg.setValueNames({ "class-name" }); + additionalClassesArg.setRequiredValueCount(Argument::varValueCount); +} + /*! * \brief Prints an LLVM string reference without instantiating a std::string first. */ @@ -29,8 +38,10 @@ void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl) return; } // add classes derived from any instantiation of "ReflectiveRapidJSON::JsonSerializable" - if (inheritsFromInstantiationOf(record, JsonSerializable::qualifiedName)) { - m_relevantClasses.emplace_back(record->getQualifiedNameAsString(), record); + // and also add classes explicitely specified via "--additional-classes" argument + string qualifiedName(qualifiedNameIfRelevant(record)); + if (!qualifiedName.empty()) { + m_relevantClasses.emplace_back(move(qualifiedName), record); } break; } @@ -106,6 +117,31 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const "} // namespace ReflectiveRapidJSON\n"; } +/*! + * \brief Returns the qualified name of the specified \a record if it is considered relevant. + */ +string JsonSerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const +{ + // consider all classes inheriting from an instantiation of "JsonSerializable" relevant + if (inheritsFromInstantiationOf(record, JsonSerializable::qualifiedName)) { + return record->getQualifiedNameAsString(); + } + + // consider all classes specified via "--additional-classes" argument relevant + if (!m_options.additionalClassesArg.isPresent()) { + return string(); + } + + const string qualifiedName(record->getQualifiedNameAsString()); + for (const char *className : m_options.additionalClassesArg.values()) { + if (className == qualifiedName) { + return qualifiedName; + } + } + + return string(); +} + std::vector JsonSerializationCodeGenerator::findRelevantBaseClasses( const RelevantClass &relevantClass) const { diff --git a/generator/jsonserializationcodegenerator.h b/generator/jsonserializationcodegenerator.h index 9a55560..89303a7 100644 --- a/generator/jsonserializationcodegenerator.h +++ b/generator/jsonserializationcodegenerator.h @@ -3,6 +3,8 @@ #include "./codegenerator.h" +#include + namespace ReflectiveRapidJSON { /*! @@ -11,30 +13,41 @@ namespace ReflectiveRapidJSON { */ class JsonSerializationCodeGenerator : public CodeGenerator { public: - JsonSerializationCodeGenerator(CodeFactory &factory); + struct Options { + Options(); - void addDeclaration(clang::Decl *decl) override; - void generate(std::ostream &os) const override; + ApplicationUtilities::Argument additionalClassesArg; + }; private: struct RelevantClass { - explicit RelevantClass(const std::string &qualifiedName, clang::CXXRecordDecl *record); + 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; + std::string qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const; + +private: std::vector findRelevantBaseClasses(const RelevantClass &relevantClass) const; std::vector m_relevantClasses; + const Options &m_options; }; -inline JsonSerializationCodeGenerator::JsonSerializationCodeGenerator(CodeFactory &factory) +inline JsonSerializationCodeGenerator::JsonSerializationCodeGenerator(CodeFactory &factory, const Options &options) : CodeGenerator(factory) + , m_options(options) { } -inline JsonSerializationCodeGenerator::RelevantClass::RelevantClass(const std::string &qualifiedName, clang::CXXRecordDecl *record) +inline JsonSerializationCodeGenerator::RelevantClass::RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record) : qualifiedName(qualifiedName) , record(record) { diff --git a/generator/main.cpp b/generator/main.cpp index 4a5f743..3e27259 100644 --- a/generator/main.cpp +++ b/generator/main.cpp @@ -27,8 +27,9 @@ int main(int argc, char *argv[]) // setup argument parser ArgumentParser parser; + OperationArgument generateArg("generate", 'g', "runs the code generator"); + generateArg.setImplicit(true); ConfigValueArgument inputFileArg("input-file", 'i', "specifies the input file", { "path" }); - inputFileArg.setRequired(true); ConfigValueArgument outputFileArg("output-file", 'o', "specifies the output file", { "path" }); Argument generatorsArg("generators", 'g', "specifies the generators (by default all generators are enabled)"); generatorsArg.setValueNames({ "json" }); @@ -36,9 +37,11 @@ int main(int argc, char *argv[]) generatorsArg.setRequiredValueCount(Argument::varValueCount); generatorsArg.setCombinable(true); ConfigValueArgument clangOptionsArg("clang-opt", 'c', "specifies an argument to be passed to Clang", { "option" }); + JsonSerializationCodeGenerator::Options jsonOptions; HelpArgument helpArg(parser); NoColorArgument noColorArg; - parser.setMainArguments({ &inputFileArg, &outputFileArg, &generatorsArg, &clangOptionsArg, &noColorArg, &helpArg }); + generateArg.setSubArguments({ &inputFileArg, &outputFileArg, &generatorsArg, &clangOptionsArg, &jsonOptions.additionalClassesArg }); + parser.setMainArguments({ &generateArg, &noColorArg, &helpArg }); // parse arguments parser.parseArgsOrExit(argc, argv); @@ -67,7 +70,7 @@ int main(int argc, char *argv[]) // find and construct generators by name for (const char *generatorName : generatorsArg.values(0)) { if (!strcmp(generatorName, "json")) { - factory.addGenerator(); + factory.addGenerator(jsonOptions); } else { cerr << Phrases::Error << "The specified generator \"" << generatorName << "\" does not exist." << Phrases::EndFlush; return -5; @@ -75,7 +78,7 @@ int main(int argc, char *argv[]) } } else { // add default generators - factory.addGenerator(); + factory.addGenerator(jsonOptions); } // read AST elements from input files and run the code generator diff --git a/generator/tests/jsongenerator.cpp b/generator/tests/jsongenerator.cpp index 04f90eb..6c27848 100644 --- a/generator/tests/jsongenerator.cpp +++ b/generator/tests/jsongenerator.cpp @@ -66,8 +66,9 @@ void JsonGeneratorTests::testGeneratorItself() const vector clangOptions{}; stringstream buffer; + JsonSerializationCodeGenerator::Options jsonOptions; CodeFactory factory(TestApplication::appPath(), inputFiles, clangOptions, buffer); - factory.addGenerator(); + factory.addGenerator(jsonOptions); CPPUNIT_ASSERT(factory.run()); assertEqualityLinewise(m_expectedCode, toArrayOfLines(buffer.str())); }