-
Notifications
You must be signed in to change notification settings - Fork 171
/
Copy pathcomparison.cpp
127 lines (112 loc) · 4.38 KB
/
comparison.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (C) 2017-2023 Jonathan Müller and cppast contributors
// SPDX-License-Identifier: MIT
/// \file
/// Generate equality comparisons.
///
/// Given an input file, it will generate comparison operators for each class that has the
/// [[generate::comparison]] attribute.
#include <algorithm>
#include <iostream>
#include <cppast/cpp_class.hpp>
#include <cppast/cpp_member_variable.hpp>
#include <cppast/visitor.hpp>
#include "example_parser.hpp"
// whether or not the token string contains the given token
bool has_token(const cppast::cpp_token_string& str, const char* token)
{
auto iter = std::find_if(str.begin(), str.end(),
[&](const cppast::cpp_token& tok) { return tok.spelling == token; });
return iter != str.end();
}
// generates equality operator for a class
void generate_op_equal(std::ostream& out, const cppast::cpp_class& c)
{
out << "inline bool operator==(const " << c.name() << "& lhs, const " << c.name()
<< "& rhs) {\n";
out << " return ";
auto first = true;
// compare bases
for (auto& base : c.bases())
{
if (cppast::has_attribute(base, "generate::transient"))
// if they are not marked not to be compared
continue;
if (first)
first = false;
else
out << " && ";
out << "static_cast<const " << base.name() << "&>(lhs) == static_cast<const " << base.name()
<< "&>(rhs)\n";
}
// compare members
for (auto& member : c)
if (member.kind() == cppast::cpp_entity_kind::member_variable_t
&& !cppast::has_attribute(member, "generate::transient"))
{
// generate comparison code for non-transient member variables
if (first)
first = false;
else
out << " && ";
out << "lhs." << member.name() << " == "
<< "rhs." << member.name() << "\n";
}
out << " ;\n";
out << "}\n\n";
}
// generate non equality operator for a class
void generate_op_non_equal(std::ostream& out, const cppast::cpp_class& c)
{
// just forwards
out << "inline bool operator!=(const " << c.name() << "& lhs, const " << c.name()
<< "& rhs) {\n";
out << " return !(lhs == rhs);\n";
out << "}\n\n";
}
// generate comparison operators for all classes in the file
void generate_comparison(const cppast::cpp_file& file)
{
cppast::visit(
file,
[](const cppast::cpp_entity& e) {
// only visit non-templated class definitions that have the attribute set
return (!cppast::is_templated(e) && e.kind() == cppast::cpp_entity_kind::class_t
&& cppast::is_definition(e) && cppast::has_attribute(e, "generate::comparison"))
// or all namespaces
|| e.kind() == cppast::cpp_entity_kind::namespace_t;
},
[](const cppast::cpp_entity& e, const cppast::visitor_info& info) {
if (e.kind() == cppast::cpp_entity_kind::class_t && !info.is_old_entity())
{
// it is a new class
auto& class_ = static_cast<const cppast::cpp_class&>(e);
auto& attribute = cppast::has_attribute(e, "generate::comparison").value();
// generate requested operators
if (attribute.arguments())
{
if (has_token(attribute.arguments().value(), "=="))
generate_op_equal(std::cout, class_);
if (has_token(attribute.arguments().value(), "!="))
generate_op_non_equal(std::cout, class_);
}
else
{
generate_op_equal(std::cout, class_);
generate_op_non_equal(std::cout, class_);
}
}
else if (e.kind() == cppast::cpp_entity_kind::namespace_t)
{
if (info.event == cppast::visitor_info::container_entity_enter)
// open namespace
std::cout << "namespace " << e.name() << " {\n\n";
else // if (info.event == cppast::visitor_info::container_entity_exit)
// close namespace
std::cout << "}\n";
}
});
}
int main(int argc, char* argv[])
{
return example_main(argc, argv, {}, &generate_comparison);
}