Reflection for RapidJSON 0.0.17
Reflection for serializing/deserializing with RapidJSON
Loading...
Searching...
No Matches
serializationcodegenerator.cpp
Go to the documentation of this file.
2
3#include <c++utilities/application/global.h>
4
5#include <clang/AST/DeclCXX.h>
6#include <clang/AST/DeclFriend.h>
7#include <clang/AST/DeclTemplate.h>
8#include <clang/AST/PrettyPrinter.h>
9#include <clang/AST/QualTypeNames.h>
10
11#include <iostream>
12
13using namespace std;
14
15namespace ReflectiveRapidJSON {
16
20ostream &operator<<(ostream &os, llvm::StringRef str)
21{
22 return os.write(str.data(), static_cast<streamsize>(str.size()));
23}
24
30{
31 switch (decl->getKind()) {
32 case clang::Decl::Kind::CXXRecord:
33 case clang::Decl::Kind::ClassTemplateSpecialization: {
34 auto *const record = static_cast<clang::CXXRecordDecl *>(decl);
35 // skip forward declarations
36 if (!record->hasDefinition()) {
37 return;
38 }
39
40 // check for template specializations to adapt a 3rd party class/struct
41 if (m_qualifiedNameOfAdaptionRecords && decl->getKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
42 auto *const templateSpecializationRecord = static_cast<clang::ClassTemplateSpecializationDecl *>(decl);
43 // check whether the name of the template specialization matches
44 if (templateSpecializationRecord->getQualifiedNameAsString() == m_qualifiedNameOfAdaptionRecords) {
45 // get the template argument of the template specialization (exactly one argument expected)
46 const auto &templateArgs = templateSpecializationRecord->getTemplateArgs();
47 if (templateArgs.size() != 1 || templateArgs.get(0).getKind() != clang::TemplateArgument::Type) {
48 return; // FIXME: use Clang diagnostics to print warning
49 }
50 // get the type the template argument refers to (that's the type of the 3rd party class/struct to adapt)
51 auto *const templateRecord = templateArgs.get(0).getAsType()->getAsCXXRecordDecl();
52 if (!templateRecord) {
53 return; // FIXME: use Clang diagnostics to print warning
54 }
55 // save the relevant information for the code generation
56 m_adaptionRecords.emplace_back(templateRecord->getQualifiedNameAsString(), templateSpecializationRecord);
57 return;
58 }
59 }
60
61 // add any other records
62 m_records.emplace_back(record);
63 } break;
64 case clang::Decl::Kind::Enum:
65 // TODO: add enums
66 break;
67 default:;
68 }
69}
70
72{
73 // skip all classes which are only forward-declared
74 if (!possiblyRelevantClass.record->isCompleteDefinition()) {
75 possiblyRelevantClass.isRelevant = IsRelevant::No;
76 return;
77 }
78
79 // consider all classes for which a specialization of the "AdaptedJsonSerializable" struct is available
80 for (const auto &adaptionRecord : m_adaptionRecords) {
81 // skip all adaption records which are only included
82 if (isOnlyIncluded(adaptionRecord.record)) {
83 continue;
84 }
85 if (adaptionRecord.qualifiedName == possiblyRelevantClass.qualifiedName) {
86 possiblyRelevantClass.isRelevant = IsRelevant::Yes;
87 return;
88 }
89 }
90
91 // skip all classes which are only included
92 if (isOnlyIncluded(possiblyRelevantClass.record)) {
93 possiblyRelevantClass.isRelevant = IsRelevant::No;
94 return;
95 }
96
97 // consider all classes inheriting from an instantiation of "JsonSerializable" relevant
98 if (const auto *const relevantBase = inheritsFromInstantiationOf(possiblyRelevantClass.record, m_qualifiedNameOfRecords)) {
99 auto policy = clang::PrintingPolicy(possiblyRelevantClass.record->getASTContext().getLangOpts());
100 policy.FullyQualifiedName = true;
101 policy.SuppressScope = false;
102 policy.SuppressUnwrittenScope = false;
103 policy.SplitTemplateClosers = false;
104 possiblyRelevantClass.relevantBase
105 = clang::TypeName::getFullyQualifiedName(relevantBase->getType(), possiblyRelevantClass.record->getASTContext(), policy, true);
106 possiblyRelevantClass.isRelevant = IsRelevant::Yes;
107 return;
108 }
109}
110
111std::vector<SerializationCodeGenerator::RelevantClass> SerializationCodeGenerator::findRelevantClasses() const
112{
113 std::vector<RelevantClass> relevantClasses;
114 for (clang::CXXRecordDecl *const record : m_records) {
115 auto &relevantClass = relevantClasses.emplace_back(record->getQualifiedNameAsString(), record);
116 computeRelevantClass(relevantClass);
117 if (relevantClass.isRelevant != IsRelevant::Yes) {
118 relevantClasses.pop_back();
119 }
120 }
121 return relevantClasses;
122}
123
124std::vector<const SerializationCodeGenerator::RelevantClass *> SerializationCodeGenerator::findRelevantBaseClasses(
125 const SerializationCodeGenerator::RelevantClass &relevantClass, const std::vector<SerializationCodeGenerator::RelevantClass> &relevantBases)
126{
127 vector<const RelevantClass *> relevantBaseClasses;
128 for (const RelevantClass &otherClass : relevantBases) {
129 if (relevantClass.record != otherClass.record && relevantClass.record->isDerivedFrom(otherClass.record)) {
130 relevantBaseClasses.push_back(&otherClass);
131 }
132 }
133 return relevantBaseClasses;
134}
135
136} // namespace ReflectiveRapidJSON
static clang::CXXBaseSpecifier * inheritsFromInstantiationOf(clang::CXXRecordDecl *record, const char *templateClass)
Returns whether the specified record inherits from an instantiation of the specified templateClass.
bool isOnlyIncluded(const clang::Decl *declaration) const
Returns whether the specified declaration is only included and not part of the actual file.
void addDeclaration(clang::Decl *decl) override
Adds all class declarations (to the internal member variable m_records).
static std::vector< const RelevantClass * > findRelevantBaseClasses(const RelevantClass &relevantClass, const std::vector< RelevantClass > &relevantBases)
virtual void computeRelevantClass(RelevantClass &possiblyRelevantClass) const
std::ostream & operator<<(std::ostream &os, llvm::StringRef str)
Prints an LLVM string reference without instantiating a std::string first.