Allow specifying additional classes for JSON serialization

This commit is contained in:
Martchus 2017-11-06 15:31:21 +01:00
parent d039daf938
commit a7feb57f22
5 changed files with 69 additions and 16 deletions

View File

@ -33,7 +33,7 @@ public:
~CodeFactory(); ~CodeFactory();
const std::vector<std::unique_ptr<CodeGenerator>> &generators() const; const std::vector<std::unique_ptr<CodeGenerator>> &generators() const;
template <typename GeneratorType> void addGenerator(); template <typename GeneratorType, typename... Args> void addGenerator(Args &&... args);
bool run(); bool run();
clang::CompilerInstance *compilerInstance(); clang::CompilerInstance *compilerInstance();
@ -55,9 +55,9 @@ private:
clang::CompilerInstance *m_compilerInstance; clang::CompilerInstance *m_compilerInstance;
}; };
template <typename GeneratorType> void CodeFactory::addGenerator() template <typename GeneratorType, typename... Args> void CodeFactory::addGenerator(Args &&... args)
{ {
m_generators.emplace_back(std::make_unique<GeneratorType>(*this)); m_generators.emplace_back(std::make_unique<GeneratorType>(*this, std::forward<Args>(args)...));
} }
inline const std::vector<std::unique_ptr<CodeGenerator>> &CodeFactory::generators() const inline const std::vector<std::unique_ptr<CodeGenerator>> &CodeFactory::generators() const

View File

@ -7,9 +7,18 @@
#include <iostream> #include <iostream>
using namespace std; using namespace std;
using namespace ApplicationUtilities;
namespace ReflectiveRapidJSON { 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. * \brief Prints an LLVM string reference without instantiating a std::string first.
*/ */
@ -29,8 +38,10 @@ void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
return; return;
} }
// add classes derived from any instantiation of "ReflectiveRapidJSON::JsonSerializable" // add classes derived from any instantiation of "ReflectiveRapidJSON::JsonSerializable"
if (inheritsFromInstantiationOf(record, JsonSerializable<void>::qualifiedName)) { // and also add classes explicitely specified via "--additional-classes" argument
m_relevantClasses.emplace_back(record->getQualifiedNameAsString(), record); string qualifiedName(qualifiedNameIfRelevant(record));
if (!qualifiedName.empty()) {
m_relevantClasses.emplace_back(move(qualifiedName), record);
} }
break; break;
} }
@ -106,6 +117,31 @@ void JsonSerializationCodeGenerator::generate(ostream &os) const
"} // namespace ReflectiveRapidJSON\n"; "} // 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<void>::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<const JsonSerializationCodeGenerator::RelevantClass *> JsonSerializationCodeGenerator::findRelevantBaseClasses( std::vector<const JsonSerializationCodeGenerator::RelevantClass *> JsonSerializationCodeGenerator::findRelevantBaseClasses(
const RelevantClass &relevantClass) const const RelevantClass &relevantClass) const
{ {

View File

@ -3,6 +3,8 @@
#include "./codegenerator.h" #include "./codegenerator.h"
#include <c++utilities/application/argumentparser.h>
namespace ReflectiveRapidJSON { namespace ReflectiveRapidJSON {
/*! /*!
@ -11,30 +13,41 @@ namespace ReflectiveRapidJSON {
*/ */
class JsonSerializationCodeGenerator : public CodeGenerator { class JsonSerializationCodeGenerator : public CodeGenerator {
public: public:
JsonSerializationCodeGenerator(CodeFactory &factory); struct Options {
Options();
void addDeclaration(clang::Decl *decl) override; ApplicationUtilities::Argument additionalClassesArg;
void generate(std::ostream &os) const override; };
private: private:
struct RelevantClass { struct RelevantClass {
explicit RelevantClass(const std::string &qualifiedName, clang::CXXRecordDecl *record); explicit RelevantClass(std::string &&qualifiedName, clang::CXXRecordDecl *record);
std::string qualifiedName; std::string qualifiedName;
clang::CXXRecordDecl *record; 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<const RelevantClass *> findRelevantBaseClasses(const RelevantClass &relevantClass) const; std::vector<const RelevantClass *> findRelevantBaseClasses(const RelevantClass &relevantClass) const;
std::vector<RelevantClass> m_relevantClasses; std::vector<RelevantClass> m_relevantClasses;
const Options &m_options;
}; };
inline JsonSerializationCodeGenerator::JsonSerializationCodeGenerator(CodeFactory &factory) inline JsonSerializationCodeGenerator::JsonSerializationCodeGenerator(CodeFactory &factory, const Options &options)
: CodeGenerator(factory) : 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) : qualifiedName(qualifiedName)
, record(record) , record(record)
{ {

View File

@ -27,8 +27,9 @@ int main(int argc, char *argv[])
// setup argument parser // setup argument parser
ArgumentParser parser; ArgumentParser parser;
OperationArgument generateArg("generate", 'g', "runs the code generator");
generateArg.setImplicit(true);
ConfigValueArgument inputFileArg("input-file", 'i', "specifies the input file", { "path" }); ConfigValueArgument inputFileArg("input-file", 'i', "specifies the input file", { "path" });
inputFileArg.setRequired(true);
ConfigValueArgument outputFileArg("output-file", 'o', "specifies the output file", { "path" }); ConfigValueArgument outputFileArg("output-file", 'o', "specifies the output file", { "path" });
Argument generatorsArg("generators", 'g', "specifies the generators (by default all generators are enabled)"); Argument generatorsArg("generators", 'g', "specifies the generators (by default all generators are enabled)");
generatorsArg.setValueNames({ "json" }); generatorsArg.setValueNames({ "json" });
@ -36,9 +37,11 @@ int main(int argc, char *argv[])
generatorsArg.setRequiredValueCount(Argument::varValueCount); generatorsArg.setRequiredValueCount(Argument::varValueCount);
generatorsArg.setCombinable(true); generatorsArg.setCombinable(true);
ConfigValueArgument clangOptionsArg("clang-opt", 'c', "specifies an argument to be passed to Clang", { "option" }); ConfigValueArgument clangOptionsArg("clang-opt", 'c', "specifies an argument to be passed to Clang", { "option" });
JsonSerializationCodeGenerator::Options jsonOptions;
HelpArgument helpArg(parser); HelpArgument helpArg(parser);
NoColorArgument noColorArg; 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 // parse arguments
parser.parseArgsOrExit(argc, argv); parser.parseArgsOrExit(argc, argv);
@ -67,7 +70,7 @@ int main(int argc, char *argv[])
// find and construct generators by name // find and construct generators by name
for (const char *generatorName : generatorsArg.values(0)) { for (const char *generatorName : generatorsArg.values(0)) {
if (!strcmp(generatorName, "json")) { if (!strcmp(generatorName, "json")) {
factory.addGenerator<JsonSerializationCodeGenerator>(); factory.addGenerator<JsonSerializationCodeGenerator>(jsonOptions);
} else { } else {
cerr << Phrases::Error << "The specified generator \"" << generatorName << "\" does not exist." << Phrases::EndFlush; cerr << Phrases::Error << "The specified generator \"" << generatorName << "\" does not exist." << Phrases::EndFlush;
return -5; return -5;
@ -75,7 +78,7 @@ int main(int argc, char *argv[])
} }
} else { } else {
// add default generators // add default generators
factory.addGenerator<JsonSerializationCodeGenerator>(); factory.addGenerator<JsonSerializationCodeGenerator>(jsonOptions);
} }
// read AST elements from input files and run the code generator // read AST elements from input files and run the code generator

View File

@ -66,8 +66,9 @@ void JsonGeneratorTests::testGeneratorItself()
const vector<const char *> clangOptions{}; const vector<const char *> clangOptions{};
stringstream buffer; stringstream buffer;
JsonSerializationCodeGenerator::Options jsonOptions;
CodeFactory factory(TestApplication::appPath(), inputFiles, clangOptions, buffer); CodeFactory factory(TestApplication::appPath(), inputFiles, clangOptions, buffer);
factory.addGenerator<JsonSerializationCodeGenerator>(); factory.addGenerator<JsonSerializationCodeGenerator>(jsonOptions);
CPPUNIT_ASSERT(factory.run()); CPPUNIT_ASSERT(factory.run());
assertEqualityLinewise(m_expectedCode, toArrayOfLines(buffer.str())); assertEqualityLinewise(m_expectedCode, toArrayOfLines(buffer.str()));
} }