UgMisc 0.3
Miscellaneous C++ header library
Loading...
Searching...
No Matches
member.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: © 2026 Larry Chips <larry@larrychips.net>
3 * SPDX-Licence-Identifier: MIT
4 */
10#include "samples_output.hpp"
12
13#include <ugmisc/member.hpp>
14
15#include <cassert>
16#include <functional>
17
18
19struct Things;
20
21
22#ifdef UGMISC_HAVE_CONCEPTS
23template<typename OStream>
24inline auto& operator<<(OStream& out, Things* things)
25requires std::same_as<OStream, std::ostream> || std::same_as<OStream, std::wostream>
26{
27 return out << "<Things>";
28}
29#else
30template<typename OStream>
31inline auto& thingsOutput(OStream& out, Things* things) {
32 return out << "<Things>";
33}
34
35inline std::ostream& operator<<(std::ostream& out, Things* things) {
36 return thingsOutput(out, things);
37}
38
39inline std::wostream& operator<<(std::wostream& out, Things* things) {
40 return thingsOutput(out, things);
41}
42#endif
43
44
45/*
46 * These declarations can appear at namespace scope or in a class or a class
47 * template. They can't appear inside a function unfortunately, due to having
48 * member class templates.
49 *
50 * Resulting declarations are:
51 *
52 * struct Womble; // Finds members called "womble".
53 * struct Use; // Finds members called "use".
54 * struct Name; // Finds members called "name".
55 */
56UGMISC_DECL_MEMBER_ACCESS(Womble, womble);
59
60
61/*
62 * Use various womble types through its WombleTraits in the same way allocators
63 * are used through std::allocator_traits.
64 */
65template<typename T>
66struct WombleTraits {
67private:
68 static inline const char* defaultWomble(T&);
69 static inline const char* defaultUse(T&, Things*);
70 static inline const char* defaultName(T&);
71
72public:
73 // Womble is a verb (to womble, he wombles, wombling free).
74 static auto womble(T& w) {
75 // Call w.womble() if possible, or defaultWomble(w) otherwise.
77 [&]() {
78 return defaultWomble(w);
79 }
80 )(w)();
81 }
82
83 /*
84 * Make good use of the things that the womble finds.
85 *
86 * We could do this in a very similar way to womble() but this way shows it
87 * doesn't all have to be crammed into one horrible expression.
88 */
89 static auto use(T& w, Things* found) {
90 // Call w.use(found) if possible,
91 // or defaultUse(w, found) if possible,
92 // or defaultUse(w) otherwise.
93 auto getDefault = [&](auto...args) {
94 return defaultUse(w, args...);
95 };
96 auto caller = ugmisc::member_caller<Use>::fallback(getDefault)(w);
97 return caller(found);
98 }
99
100 static auto name(T& w) {
101 using namespace ugmisc;
102
103 // Try w.name() then w.name then defaultName().
104 auto getDefaultName = [&]() { return defaultName(w); };
105 auto getNameValue = [&]() {
106 return member_value<Name>{}(w)(getDefaultName);
107 };
108 auto getNameFunction = member_caller<Name>::fallback(getNameValue)(w);
109 return getNameFunction();
110 }
111};
112
113
114template<typename T>
115const char* WombleTraits<T>::defaultWomble(T&) {
116 SampleOutput out;
117 out << "WombleTraits::defaultWomble(" << getTypeName<T>() << "&)";
118 return "WombleTraits::defaultWomble()";
119}
120
121template<typename T>
122const char* WombleTraits<T>::defaultUse(T&, Things*) {
123 SampleOutput out;
124 out << "WombleTraits::defaultUse(" << getTypeName<T>() << "&, Things*)";
125 return "WombleTraits::defaultUse()";
126}
127
128template<typename T>
129const char* WombleTraits<T>::defaultName(T&) {
130 SampleOutput out;
131 out << "WombleTraits::defaultName(" << getTypeName<T>() << "&)";
132 return "WombleTraits::defaultName()";
133}
134
135
136
137struct CompleteWomble {
138 static constexpr const char *sTypeName = "CompleteWomble";
139
140 const char * use(Things* found) {
141 SampleOutput out;
142 out << "CompleteWomble::use(" << found << ")";
143 return "CompleteWomble::use()";
144 }
145
146 const char *name() {
147 SampleOutput out;
148 out << "CompleteWomble::name()";
149 return "CompleteWomble";
150 }
151
152 const char *womble() {
153 SampleOutput out;
154 out << "CompleteWomble::womble()";
155 return "CompleteWomble::womble()";
156 }
157};
158
159
160struct EmptyWomble {
161 static constexpr const char *sTypeName = "EmptyWomble";
162};
163
164
165int main(int, char**) {
166 SampleOutput out;
167 auto always = out.always();
168 auto expect = out.expect();
169
170 Things* things;
171
172 CompleteWomble complete;
173 EmptyWomble empty;
174
175 using CompleteTraits = WombleTraits<CompleteWomble>;
176 using EmptyTraits = WombleTraits<EmptyWomble>;
177
178 always << "CompleteTraits::name(complete)\n ";
179 CompleteTraits::name(complete);
180 out << "\n";
181 expect << "CompleteWomble::name()"
182 "\n";
183
184 always << "CompleteTraits::womble(complete)\n ";
185 CompleteTraits::womble(complete);
186 out << "\n";
187 expect << "CompleteWomble::womble()"
188 "\n";
189
190 always << "CompleteTraits::use(complete, things)\n ";
191 CompleteTraits::use(complete, things);
192 out << "\n";
193 expect << "CompleteWomble::use(" << things << ")"
194 "\n";
195
196 always << "EmptyTraits::name(empty)\n ";
197 EmptyTraits::name(empty);
198 out << "\n";
199 expect << "WombleTraits::defaultName(" << getTypeName<EmptyWomble>() << "&)"
200 "\n";
201
202 always << "EmptyTraits::womble(empty)\n ";
203 EmptyTraits::womble(empty);
204 out << "\n";
205 expect << "WombleTraits::defaultWomble(" << getTypeName<EmptyWomble>() << "&)"
206 "\n";
207
208 always << "EmptyTraits::use(empty, things)\n ";
209 EmptyTraits::use(empty, things);
210 out << "\n";
211 expect << "WombleTraits::defaultUse(" << getTypeName<EmptyWomble>() << "&, Things*)"
212 "\n";
213
214
215 return 0;
216}
Testing for and using named static and non static members of types.
#define UGMISC_DECL_MEMBER_ACCESS(TNAME, NAME)
Definition member.hpp:148
static constexpr default_function< F > fallback(F &&f)
Definition member.hpp:933