Refactor JsonSerializationCodeGenerator
* JSONSerializationCodeGenerator -> JsonSerializationCodeGenerator * Move to separate file
This commit is contained in:
parent
c94c896f6c
commit
a768408493
|
@ -8,6 +8,7 @@ set(LINK_TESTS_AGAINST_APP_TARGET ON)
|
||||||
# add project files
|
# add project files
|
||||||
set(HEADER_FILES
|
set(HEADER_FILES
|
||||||
codegenerator.h
|
codegenerator.h
|
||||||
|
jsonserializationcodegenerator.h
|
||||||
codefactory.h
|
codefactory.h
|
||||||
frontendaction.h
|
frontendaction.h
|
||||||
consumer.h
|
consumer.h
|
||||||
|
@ -16,6 +17,7 @@ set(HEADER_FILES
|
||||||
)
|
)
|
||||||
set(SRC_FILES
|
set(SRC_FILES
|
||||||
codegenerator.cpp
|
codegenerator.cpp
|
||||||
|
jsonserializationcodegenerator.cpp
|
||||||
codefactory.cpp
|
codefactory.cpp
|
||||||
frontendaction.cpp
|
frontendaction.cpp
|
||||||
consumer.cpp
|
consumer.cpp
|
||||||
|
|
|
@ -1,27 +1,13 @@
|
||||||
#include "./codegenerator.h"
|
#include "./codegenerator.h"
|
||||||
|
|
||||||
#include "../lib/json/serializable.h"
|
|
||||||
|
|
||||||
#include <c++utilities/application/global.h>
|
#include <c++utilities/application/global.h>
|
||||||
|
|
||||||
#include <clang/AST/DeclCXX.h>
|
#include <clang/AST/DeclCXX.h>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace ReflectiveRapidJSON {
|
namespace ReflectiveRapidJSON {
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Prints an LLVM string reference without instantiating a std::string first.
|
|
||||||
*/
|
|
||||||
ostream &operator<<(ostream &os, llvm::StringRef str)
|
|
||||||
{
|
|
||||||
os.write(str.data(), static_cast<streamsize>(str.size()));
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeGenerator::~CodeGenerator()
|
CodeGenerator::~CodeGenerator()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -49,103 +35,4 @@ bool CodeGenerator::inheritsFromInstantiationOf(clang::CXXRecordDecl *const reco
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSONSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
|
||||||
{
|
|
||||||
switch (decl->getKind()) {
|
|
||||||
case clang::Decl::Kind::CXXRecord: {
|
|
||||||
auto *const record = static_cast<clang::CXXRecordDecl *>(decl);
|
|
||||||
// skip forward declarations
|
|
||||||
if (!record->hasDefinition()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// add classes derived from any instantiation of "ReflectiveRapidJSON::JsonSerializable"
|
|
||||||
if (inheritsFromInstantiationOf(record, JsonSerializable<void>::qualifiedName)) {
|
|
||||||
m_relevantClasses.emplace_back(record->getQualifiedNameAsString(), record);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case clang::Decl::Kind::Enum:
|
|
||||||
// TODO: add enums
|
|
||||||
break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void JSONSerializationCodeGenerator::generate(ostream &os) const
|
|
||||||
{
|
|
||||||
if (m_relevantClasses.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// put everything into namespace ReflectiveRapidJSON::Reflector
|
|
||||||
os << "namespace ReflectiveRapidJSON {\n"
|
|
||||||
"namespace Reflector {\n\n";
|
|
||||||
|
|
||||||
// add push and pull functions for each class, for an example of the resulting
|
|
||||||
// output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
|
|
||||||
for (const RelevantClass &relevantClass : m_relevantClasses) {
|
|
||||||
// write comment
|
|
||||||
os << "// define code for (de)serializing " << relevantClass.qualifiedName << " objects\n";
|
|
||||||
|
|
||||||
// find relevant base classes
|
|
||||||
const vector<const RelevantClass *> relevantBases = findRelevantBaseClasses(relevantClass);
|
|
||||||
|
|
||||||
// print push method
|
|
||||||
os << "template <> inline void push<::" << relevantClass.qualifiedName << ">(const ::" << relevantClass.qualifiedName
|
|
||||||
<< " &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, ::RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)\n{\n"
|
|
||||||
" // push base classes\n";
|
|
||||||
for (const RelevantClass *baseClass : relevantBases) {
|
|
||||||
os << " push(static_cast<const ::" << baseClass->qualifiedName << " &>(reflectable), value, allocator);\n";
|
|
||||||
}
|
|
||||||
os << " // push members\n";
|
|
||||||
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
|
||||||
os << " push(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, allocator);\n";
|
|
||||||
}
|
|
||||||
os << "}\n";
|
|
||||||
|
|
||||||
// print pull method
|
|
||||||
os << "template <> inline void pull<::" << relevantClass.qualifiedName << ">(::" << relevantClass.qualifiedName
|
|
||||||
<< " &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value, "
|
|
||||||
"JsonDeserializationErrors "
|
|
||||||
"*errors)\n{\n"
|
|
||||||
" // pull base classes\n";
|
|
||||||
for (const RelevantClass *baseClass : relevantBases) {
|
|
||||||
os << " pull(static_cast<::" << baseClass->qualifiedName << " &>(reflectable), value, errors);\n";
|
|
||||||
}
|
|
||||||
os << " // set error context for current record\n"
|
|
||||||
" const char *previousRecord;\n"
|
|
||||||
" if (errors) {\n"
|
|
||||||
" previousRecord = errors->currentRecord;\n"
|
|
||||||
" errors->currentRecord = \""
|
|
||||||
<< relevantClass.qualifiedName
|
|
||||||
<< "\";\n"
|
|
||||||
" }\n"
|
|
||||||
" // pull members\n";
|
|
||||||
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
|
||||||
os << " pull(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, errors);\n";
|
|
||||||
}
|
|
||||||
os << " // restore error context for previous record\n"
|
|
||||||
" if (errors) {\n"
|
|
||||||
" errors->currentRecord = previousRecord;\n"
|
|
||||||
" }\n";
|
|
||||||
os << "}\n\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// close namespace ReflectiveRapidJSON::Reflector
|
|
||||||
os << "} // namespace Reflector\n"
|
|
||||||
"} // namespace ReflectiveRapidJSON\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<const JSONSerializationCodeGenerator::RelevantClass *> JSONSerializationCodeGenerator::findRelevantBaseClasses(
|
|
||||||
const RelevantClass &relevantClass) const
|
|
||||||
{
|
|
||||||
vector<const RelevantClass *> relevantBaseClasses;
|
|
||||||
for (const RelevantClass &otherClass : m_relevantClasses) {
|
|
||||||
if (relevantClass.record != otherClass.record && relevantClass.record->isDerivedFrom(otherClass.record)) {
|
|
||||||
relevantBaseClasses.push_back(&otherClass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return relevantBaseClasses;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ReflectiveRapidJSON
|
} // namespace ReflectiveRapidJSON
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
class Decl;
|
class Decl;
|
||||||
class NamedDecl;
|
|
||||||
class CXXRecordDecl;
|
class CXXRecordDecl;
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
||||||
|
@ -46,41 +45,6 @@ inline CodeFactory &CodeGenerator::factory() const
|
||||||
return m_factory;
|
return m_factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief The JSONSerializationCodeGenerator class generates code for JSON (de)serialization
|
|
||||||
* of objects inheriting from an instantiation of JsonSerializable.
|
|
||||||
*/
|
|
||||||
class JSONSerializationCodeGenerator : public CodeGenerator {
|
|
||||||
public:
|
|
||||||
JSONSerializationCodeGenerator(CodeFactory &factory);
|
|
||||||
|
|
||||||
void addDeclaration(clang::Decl *decl) override;
|
|
||||||
void generate(std::ostream &os) const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct RelevantClass {
|
|
||||||
explicit RelevantClass(const std::string &qualifiedName, clang::CXXRecordDecl *record);
|
|
||||||
|
|
||||||
std::string qualifiedName;
|
|
||||||
clang::CXXRecordDecl *record;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<const RelevantClass *> findRelevantBaseClasses(const RelevantClass &relevantClass) const;
|
|
||||||
|
|
||||||
std::vector<RelevantClass> m_relevantClasses;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline JSONSerializationCodeGenerator::JSONSerializationCodeGenerator(CodeFactory &factory)
|
|
||||||
: CodeGenerator(factory)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline JSONSerializationCodeGenerator::RelevantClass::RelevantClass(const std::string &qualifiedName, clang::CXXRecordDecl *record)
|
|
||||||
: qualifiedName(qualifiedName)
|
|
||||||
, record(record)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ReflectiveRapidJSON
|
} // namespace ReflectiveRapidJSON
|
||||||
|
|
||||||
#endif // REFLECTIVE_RAPIDJSON_CODE_GENERATOR_H
|
#endif // REFLECTIVE_RAPIDJSON_CODE_GENERATOR_H
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
#include "./jsonserializationcodegenerator.h"
|
||||||
|
|
||||||
|
#include "../lib/json/serializable.h"
|
||||||
|
|
||||||
|
#include <clang/AST/DeclCXX.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace ReflectiveRapidJSON {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Prints an LLVM string reference without instantiating a std::string first.
|
||||||
|
*/
|
||||||
|
ostream &operator<<(ostream &os, llvm::StringRef str)
|
||||||
|
{
|
||||||
|
os.write(str.data(), static_cast<streamsize>(str.size()));
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
||||||
|
{
|
||||||
|
switch (decl->getKind()) {
|
||||||
|
case clang::Decl::Kind::CXXRecord: {
|
||||||
|
auto *const record = static_cast<clang::CXXRecordDecl *>(decl);
|
||||||
|
// skip forward declarations
|
||||||
|
if (!record->hasDefinition()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// add classes derived from any instantiation of "ReflectiveRapidJSON::JsonSerializable"
|
||||||
|
if (inheritsFromInstantiationOf(record, JsonSerializable<void>::qualifiedName)) {
|
||||||
|
m_relevantClasses.emplace_back(record->getQualifiedNameAsString(), record);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case clang::Decl::Kind::Enum:
|
||||||
|
// TODO: add enums
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonSerializationCodeGenerator::generate(ostream &os) const
|
||||||
|
{
|
||||||
|
if (m_relevantClasses.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put everything into namespace ReflectiveRapidJSON::Reflector
|
||||||
|
os << "namespace ReflectiveRapidJSON {\n"
|
||||||
|
"namespace Reflector {\n\n";
|
||||||
|
|
||||||
|
// add push and pull functions for each class, for an example of the resulting
|
||||||
|
// output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
|
||||||
|
for (const RelevantClass &relevantClass : m_relevantClasses) {
|
||||||
|
// write comment
|
||||||
|
os << "// define code for (de)serializing " << relevantClass.qualifiedName << " objects\n";
|
||||||
|
|
||||||
|
// find relevant base classes
|
||||||
|
const vector<const RelevantClass *> relevantBases = findRelevantBaseClasses(relevantClass);
|
||||||
|
|
||||||
|
// print push method
|
||||||
|
os << "template <> inline void push<::" << relevantClass.qualifiedName << ">(const ::" << relevantClass.qualifiedName
|
||||||
|
<< " &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, ::RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)\n{\n"
|
||||||
|
" // push base classes\n";
|
||||||
|
for (const RelevantClass *baseClass : relevantBases) {
|
||||||
|
os << " push(static_cast<const ::" << baseClass->qualifiedName << " &>(reflectable), value, allocator);\n";
|
||||||
|
}
|
||||||
|
os << " // push members\n";
|
||||||
|
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
||||||
|
os << " push(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, allocator);\n";
|
||||||
|
}
|
||||||
|
os << "}\n";
|
||||||
|
|
||||||
|
// print pull method
|
||||||
|
os << "template <> inline void pull<::" << relevantClass.qualifiedName << ">(::" << relevantClass.qualifiedName
|
||||||
|
<< " &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value, "
|
||||||
|
"JsonDeserializationErrors "
|
||||||
|
"*errors)\n{\n"
|
||||||
|
" // pull base classes\n";
|
||||||
|
for (const RelevantClass *baseClass : relevantBases) {
|
||||||
|
os << " pull(static_cast<::" << baseClass->qualifiedName << " &>(reflectable), value, errors);\n";
|
||||||
|
}
|
||||||
|
os << " // set error context for current record\n"
|
||||||
|
" const char *previousRecord;\n"
|
||||||
|
" if (errors) {\n"
|
||||||
|
" previousRecord = errors->currentRecord;\n"
|
||||||
|
" errors->currentRecord = \""
|
||||||
|
<< relevantClass.qualifiedName
|
||||||
|
<< "\";\n"
|
||||||
|
" }\n"
|
||||||
|
" // pull members\n";
|
||||||
|
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
||||||
|
os << " pull(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, errors);\n";
|
||||||
|
}
|
||||||
|
os << " // restore error context for previous record\n"
|
||||||
|
" if (errors) {\n"
|
||||||
|
" errors->currentRecord = previousRecord;\n"
|
||||||
|
" }\n";
|
||||||
|
os << "}\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// close namespace ReflectiveRapidJSON::Reflector
|
||||||
|
os << "} // namespace Reflector\n"
|
||||||
|
"} // namespace ReflectiveRapidJSON\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<const JsonSerializationCodeGenerator::RelevantClass *> JsonSerializationCodeGenerator::findRelevantBaseClasses(
|
||||||
|
const RelevantClass &relevantClass) const
|
||||||
|
{
|
||||||
|
vector<const RelevantClass *> relevantBaseClasses;
|
||||||
|
for (const RelevantClass &otherClass : m_relevantClasses) {
|
||||||
|
if (relevantClass.record != otherClass.record && relevantClass.record->isDerivedFrom(otherClass.record)) {
|
||||||
|
relevantBaseClasses.push_back(&otherClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return relevantBaseClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ReflectiveRapidJSON
|
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
||||||
|
#define REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
||||||
|
|
||||||
|
#include "./codegenerator.h"
|
||||||
|
|
||||||
|
namespace ReflectiveRapidJSON {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief The JSONSerializationCodeGenerator class generates code for JSON (de)serialization
|
||||||
|
* of objects inheriting from an instantiation of JsonSerializable.
|
||||||
|
*/
|
||||||
|
class JsonSerializationCodeGenerator : public CodeGenerator {
|
||||||
|
public:
|
||||||
|
JsonSerializationCodeGenerator(CodeFactory &factory);
|
||||||
|
|
||||||
|
void addDeclaration(clang::Decl *decl) override;
|
||||||
|
void generate(std::ostream &os) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct RelevantClass {
|
||||||
|
explicit RelevantClass(const std::string &qualifiedName, clang::CXXRecordDecl *record);
|
||||||
|
|
||||||
|
std::string qualifiedName;
|
||||||
|
clang::CXXRecordDecl *record;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<const RelevantClass *> findRelevantBaseClasses(const RelevantClass &relevantClass) const;
|
||||||
|
|
||||||
|
std::vector<RelevantClass> m_relevantClasses;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline JsonSerializationCodeGenerator::JsonSerializationCodeGenerator(CodeFactory &factory)
|
||||||
|
: CodeGenerator(factory)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline JsonSerializationCodeGenerator::RelevantClass::RelevantClass(const std::string &qualifiedName, clang::CXXRecordDecl *record)
|
||||||
|
: qualifiedName(qualifiedName)
|
||||||
|
, record(record)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ReflectiveRapidJSON
|
||||||
|
|
||||||
|
#endif // REFLECTIVE_RAPIDJSON_CODE_JSON_SERIALIZATION_GENERATOR_H
|
|
@ -1,4 +1,5 @@
|
||||||
#include "./codefactory.h"
|
#include "./codefactory.h"
|
||||||
|
#include "./jsonserializationcodegenerator.h"
|
||||||
|
|
||||||
#include "resources/config.h"
|
#include "resources/config.h"
|
||||||
|
|
||||||
|
@ -66,7 +67,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>();
|
||||||
} 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;
|
||||||
|
@ -74,7 +75,7 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// add default generators
|
// add default generators
|
||||||
factory.addGenerator<JSONSerializationCodeGenerator>();
|
factory.addGenerator<JsonSerializationCodeGenerator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// read AST elements from input files and run the code generator
|
// read AST elements from input files and run the code generator
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "./structs.h"
|
#include "./structs.h"
|
||||||
|
|
||||||
#include "../codefactory.h"
|
#include "../codefactory.h"
|
||||||
|
#include "../jsonserializationcodegenerator.h"
|
||||||
|
|
||||||
#include "resources/config.h"
|
#include "resources/config.h"
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ void OverallTests::testGeneratorItself()
|
||||||
|
|
||||||
stringstream buffer;
|
stringstream buffer;
|
||||||
CodeFactory factory(TestApplication::appPath(), inputFiles, clangOptions, buffer);
|
CodeFactory factory(TestApplication::appPath(), inputFiles, clangOptions, buffer);
|
||||||
factory.addGenerator<JSONSerializationCodeGenerator>();
|
factory.addGenerator<JsonSerializationCodeGenerator>();
|
||||||
CPPUNIT_ASSERT(factory.run());
|
CPPUNIT_ASSERT(factory.run());
|
||||||
assertEqualityLinewise(m_expectedCode, toArrayOfLines(buffer.str()));
|
assertEqualityLinewise(m_expectedCode, toArrayOfLines(buffer.str()));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue