Reflection for RapidJSON 0.0.17
Reflection for serializing/deserializing with RapidJSON
Loading...
Searching...
No Matches
jsonserializationcodegenerator.cpp
Go to the documentation of this file.
2
3#include "../lib/json/serializable.h"
4
5#include <clang/AST/DeclCXX.h>
6#include <clang/AST/DeclFriend.h>
7#include <clang/AST/DeclTemplate.h>
8
9#include <iostream>
10
11using namespace std;
12using namespace CppUtilities;
13
14namespace ReflectiveRapidJSON {
15
21 : additionalClassesArg("json-classes", '\0', "specifies additional classes to consider for JSON (de)serialization", { "class-name" })
22 , visibilityArg("json-visibility", '\0', "specifies the \"visibility attribute\" for generated functions", { "attribute" })
23{
24 additionalClassesArg.setRequiredValueCount(Argument::varValueCount);
25 additionalClassesArg.setValueCompletionBehavior(ValueCompletionBehavior::None);
26 visibilityArg.setPreDefinedCompletionValues("CPP_UTILITIES_GENERIC_LIB_EXPORT");
27}
28
31 , m_options(options)
32{
33 m_qualifiedNameOfRecords = JsonSerializable<void>::qualifiedName;
34 m_qualifiedNameOfAdaptionRecords = AdaptedJsonSerializable<void>::qualifiedName;
35}
36
41{
43 if (possiblyRelevantClass.isRelevant != IsRelevant::Maybe) {
44 return;
45 }
46
47 // consider all classes specified via "--additional-classes" argument relevant
48 if (!m_options.additionalClassesArg.isPresent()) {
49 return;
50 }
51 for (const char *const className : m_options.additionalClassesArg.values()) {
52 if (className == possiblyRelevantClass.qualifiedName) {
53 possiblyRelevantClass.isRelevant = IsRelevant::Yes;
54 return;
55 }
56 }
57}
58
63{
64 // initialize source manager to make use of isOnlyIncluded() for skipping records which are only included
66
67 // find relevant classes
68 const auto relevantClasses = findRelevantClasses();
69 if (relevantClasses.empty()) {
70 return; // nothing to generate
71 }
72
73 // put everything into namespace ReflectiveRapidJSON::JsonReflector
74 os << "namespace ReflectiveRapidJSON {\n"
75 "namespace JsonReflector {\n\n";
76
77 // determine visibility attribute
78 const char *visibility = m_options.visibilityArg.firstValue();
79 if (!visibility) {
80 visibility = "";
81 }
82
83 // add push and pull functions for each class, for an example of the resulting
84 // output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
85 for (const RelevantClass &relevantClass : relevantClasses) {
86 // determine whether private members should be pushed/pulled as well: check whether friend declarations for push/pull present
87 // note: the friend declarations we are looking for are expanded from the REFLECTIVE_RAPIDJSON_ENABLE_PRIVATE_MEMBERS macro
88 bool pushPrivateMembers = false, pullPrivateMembers = false;
89 for (const clang::FriendDecl *const friendDecl : relevantClass.record->friends()) {
90 // get the actual declaration which must be a function
91 const clang::NamedDecl *const actualFriendDecl = friendDecl->getFriendDecl();
92 if (!actualFriendDecl || actualFriendDecl->getKind() != clang::Decl::Kind::Function) {
93 continue;
94 }
95 // check whether the friend function matches the push/pull helper function
96 const string friendName(actualFriendDecl->getQualifiedNameAsString());
97 if (friendName == "ReflectiveRapidJSON::JsonReflector::push") {
98 pushPrivateMembers = true;
99 }
100 if (friendName == "ReflectiveRapidJSON::JsonReflector::pull") {
101 pullPrivateMembers = true;
102 }
103 if (pushPrivateMembers && pullPrivateMembers) {
104 break;
105 }
106 }
107
108 // find relevant base classes
109 const vector<const RelevantClass *> relevantBases = findRelevantBaseClasses(relevantClass, relevantClasses);
110
111 // print comment
112 os << "// define code for (de)serializing " << relevantClass.qualifiedName << " objects\n";
113
114 // print push method
115 os << "template <> " << visibility << " void push<::" << relevantClass.qualifiedName << ">(const ::" << relevantClass.qualifiedName
116 << " &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, ::RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)\n{\n"
117 " // push base classes\n";
118 for (const RelevantClass *baseClass : relevantBases) {
119 os << " push(static_cast<const ::" << baseClass->qualifiedName << " &>(reflectable), value, allocator);\n";
120 }
121 os << " // push members\n";
122 auto pushWritten = false;
123 for (const clang::FieldDecl *field : relevantClass.record->fields()) {
124 if (pushPrivateMembers || field->getAccess() == clang::AS_public) {
125 os << " push(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, allocator);\n";
126 pushWritten = true;
127 }
128 }
129 if (relevantBases.empty() && !pushWritten) {
130 os << " (void)reflectable;\n (void)value;\n (void)allocator;\n";
131 }
132 os << "}\n";
133
134 // skip printing the pull method for classes without default constructor because deserializing those is currently not supported
135 if (!relevantClass.record->hasDefaultConstructor()) {
136 continue;
137 }
138
139 // print pull method
140 os << "template <> " << visibility << " void pull<::" << relevantClass.qualifiedName << ">(::" << relevantClass.qualifiedName
141 << " &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value, "
142 "JsonDeserializationErrors "
143 "*errors)\n{\n"
144 " // pull base classes\n";
145 for (const RelevantClass *baseClass : relevantBases) {
146 os << " pull(static_cast<::" << baseClass->qualifiedName << " &>(reflectable), value, errors);\n";
147 }
148 os << " // set error context for current record\n"
149 " const char *previousRecord = nullptr;\n"
150 " if (errors) {\n"
151 " previousRecord = errors->currentRecord;\n"
152 " errors->currentRecord = \""
153 << relevantClass.qualifiedName
154 << "\";\n"
155 " }\n"
156 " // pull members\n";
157 auto pullWritten = false;
158 for (const clang::FieldDecl *field : relevantClass.record->fields()) {
159 // skip const members
160 if (field->getType().isConstant(field->getASTContext())) {
161 continue;
162 }
163 if (pullPrivateMembers || field->getAccess() == clang::AS_public) {
164 os << " pull(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, errors);\n";
165 pullWritten = true;
166 }
167 }
168 if (relevantBases.empty() && !pullWritten) {
169 os << " (void)reflectable;\n (void)value;\n";
170 }
171 os << " // restore error context for previous record\n"
172 " if (errors) {\n"
173 " errors->currentRecord = previousRecord;\n"
174 " }\n";
175 os << "}\n\n";
176 }
177
178 // close namespace ReflectiveRapidJSON::JsonReflector
179 os << "} // namespace JsonReflector\n"
180 "} // namespace ReflectiveRapidJSON\n";
181}
182
183} // namespace ReflectiveRapidJSON
The CodeFactory class produces additional (reflection) code for a specified list of C++ source files.
Definition codefactory.h:27
void lazyInitializeSourceManager() const
Lazy initializes the source manager.
JsonSerializationCodeGenerator(CodeFactory &factory, const Options &options)
void computeRelevantClass(RelevantClass &possiblyRelevantClass) const override
Checks whether possiblyRelevantClass is actually relevant.
void generate(std::ostream &os) const override
Generates pull() and push() helper functions in the ReflectiveRapidJSON::JsonReflector namespace for ...
static std::vector< const RelevantClass * > findRelevantBaseClasses(const RelevantClass &relevantClass, const std::vector< RelevantClass > &relevantBases)
virtual void computeRelevantClass(RelevantClass &possiblyRelevantClass) const
Options()
Initializes the CLI arguments which are specific to the JsonSerializationCodeGenerator.