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();
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();
clang::CompilerInstance *compilerInstance();
@ -55,9 +55,9 @@ private:
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

View File

@ -7,9 +7,18 @@
#include <iostream>
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<void>::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<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(
const RelevantClass &relevantClass) const
{

View File

@ -3,6 +3,8 @@
#include "./codegenerator.h"
#include <c++utilities/application/argumentparser.h>
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<const RelevantClass *> findRelevantBaseClasses(const RelevantClass &relevantClass) const;
std::vector<RelevantClass> 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)
{

View File

@ -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<JsonSerializationCodeGenerator>();
factory.addGenerator<JsonSerializationCodeGenerator>(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<JsonSerializationCodeGenerator>();
factory.addGenerator<JsonSerializationCodeGenerator>(jsonOptions);
}
// 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{};
stringstream buffer;
JsonSerializationCodeGenerator::Options jsonOptions;
CodeFactory factory(TestApplication::appPath(), inputFiles, clangOptions, buffer);
factory.addGenerator<JsonSerializationCodeGenerator>();
factory.addGenerator<JsonSerializationCodeGenerator>(jsonOptions);
CPPUNIT_ASSERT(factory.run());
assertEqualityLinewise(m_expectedCode, toArrayOfLines(buffer.str()));
}