Skip to content
This repository was archived by the owner on Mar 4, 2026. It is now read-only.

Commit 304d4db

Browse files
committed
new take on friendship
1 parent 411c8ca commit 304d4db

23 files changed

+675
-143
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ if (${YOMM2_ENABLE_TESTS})
6969
add_test (adventure examples/adventure)
7070
add_test (next examples/next)
7171
add_test (asteroids examples/asteroids)
72-
add_test (friendship examples/friendship)
72+
add_test (containers examples/containers/containers)
7373
endif()
7474

7575
if (NOT DEFINED(YOMM2_ENABLE_BENCHMARKS))

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,7 @@ The library comes with a series of examples:
232232

233233
* [Adventure: a 3-method example](examples/adventure.cpp)
234234

235+
* [friendship: an example with namespaces, method containers and friend
236+
declarations](examples/friendship)
237+
235238
I presented the library at CppCon 2018. Here are [the video recording](https://www.youtube.com/watch?v=xkxo0lah51s) and [the slides](https://jll63.github.io/yomm2/slides/).

REFERENCE.md

Lines changed: 116 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ with `YOMM2_CLASS`. This means all classes that are marked with `virtual_`, and
7676
all their subclasses. Each class registration must list the base classes that
7777
may be involved in method calls.
7878

79-
`register_class` is an alias for `YOMM2_CLASS` created by header
79+
`register_class` is an alias for `YOMM2_CLASS` provided by header
8080
`yorel/yomm2/cute.hpp`.
8181

8282
#### Examples:
@@ -135,20 +135,20 @@ a virtual Animal argument in a method declaration.
135135
```
136136
YOMM2_DECLARE(return_type, method, (type...));
137137
138-
return_type rv = method(unmarked_type... arg);
138+
return_type rv = method(unspecified_type... arg);
139139
140140
```
141141

142142
Declare a method.
143143

144144
Create an inline function `method` that returns a `return type` and takes an
145-
argument list of `unmarked_type...`, which consist of `type...` without the
145+
argument list of `unspecified_type...`, which consist of `type...` without the
146146
`virtual_` marker. At least one `type` (but not necessarily all) must be marked
147147
with `virtual_`.
148148

149149
When `method` is called, the dynamic types of the arguments marked with
150150
`virtual_` are examined, and the most specific definition compatible with
151-
`unmarked_type...` is called. If no compatible definition exists, or if
151+
`unspecified_type...` is called. If no compatible definition exists, or if
152152
several compatible definitions exist but none of them is more specific than
153153
all the others, the call is illegal and an error handler is executed. By
154154
default it writes a diagnostic on `std::cerr ` and terminates the program via
@@ -161,7 +161,7 @@ NOTE:
161161
* The parameters in `type...` consist of _just_ a type, e.g. `int` is correct
162162
but `int i` is not.
163163

164-
`declare_method` is an alias for `YOMM2_DECLARE` created by header
164+
`declare_method` is an alias for `YOMM2_DECLARE` provided by header
165165
`yorel/yomm2/cute.hpp`.
166166

167167
## Examples:
@@ -175,22 +175,31 @@ YOMM2_DECLARE(bool, approve, (virtual_<Role&>, virtual_<Expense&>), double);
175175

176176
#### Synopsis:
177177
```
178-
YOMM2_DEFINE(return_type, name, (unmarked_type... argument)) {
178+
YOMM2_DEFINE(return_type, name, (unspecified_type... argument)) {
179+
....
180+
}
181+
182+
YOMM2_DEFINE(container, return_type, name, (unspecified_type... argument)) {
179183
....
180184
}
181185
```
182186

183187
Add an implementation to a method.
184188

185-
Locate a method that can be called with the specified `unmarked_type...` list
189+
Locate a method that can be called with the specified `unspecified_type...` list
186190
and add the definition to the method's list of definitions. The method must
187191
exist and must be unique. `return_type` must be covariant with the method's
188192
return type. `return_type` may be `auto`.
189193

190194
NOTE that the types of the arguments are _not_ marked with `virtual_`.
191195

192-
`define_method` is an aliases for `YOMM2_DEFINE` and `YOMM2_END`
193-
created by header `yorel/yomm2/cute.hpp`.
196+
If `container` is specified, the method definition is placed inside the said
197+
container, which must have been declared with `YOMM2_DECLARE_METHOD_CONTAINER`
198+
or `method_container`. See the documentation of
199+
`YOMM2_DECLARE_METHOD_CONTAINER` for more information on method containers.
200+
201+
`define_method` is an alias for `YOMM2_DEFINE`, provided by header
202+
`yorel/yomm2/cute.hpp`.
194203

195204
## Examples:
196205
```
@@ -222,11 +231,108 @@ YOMM2_DEFINE(std::string, meet, (Cat& cat, Dog& dog)) {
222231
}
223232
```
224233

234+
## macros YOMM2_METHOD_INLINE, define_method_inline
235+
236+
#### Synopsis:
237+
```
238+
YOMM2_DEFINE_INLINE(container, return_type, name, (unspecified_type... argument)) {
239+
....
240+
}
241+
```
242+
243+
Add an implementation to a method, inside a container, and make it inline.
244+
245+
Like the `YOMM2_DEFINE(container, ...)` macro, define a method inside the
246+
specified container, which must have been declared with
247+
`YOMM2_DECLARE_METHOD_CONTAINER` or `method_container`. The definition has the
248+
`inline` storage class, and thus can be placed in a header file and is a
249+
potential candidate for inlining.
250+
251+
See the documentation of `YOMM2_DECLARE_METHOD_CONTAINER` for more information
252+
on method containers.
253+
254+
`define_method_inline` is an alias for `YOMM2_DEFINE_INLINE`, provided by
255+
header `yorel/yomm2/cute.hpp`.
256+
257+
## macros YOMM2_DECLARE_METHOD_CONTAINER, method_container
258+
259+
#### Synopsis:
260+
261+
```
262+
YOMM2_DECLARE_METHOD_CONTAINER(container)
263+
YOMM2_DECLARE_METHOD_CONTAINER(container, return_type, name, (unspecified_type... argument))
264+
```
265+
266+
Declare a method container, and optionally a method definition inside that
267+
container.
268+
269+
Method containers are collections of method definitions that can be addressed
270+
by their name, return type and signature. This makes it possible for a class to
271+
grant friendship to all the methods inside a container, or to a single method
272+
with a specific name, return type and signature - see `YOMM2_FRIEND`. It also
273+
makes it possible to retrieve a specific method, in order to call it or create
274+
a pointer to it - see `YOMM2_DEFINITION`. See [containers](examples/containers)
275+
for an example.
276+
277+
This macro only creates declarations, and thus can be placed in a header
278+
file. The four argument form makes it possible to access a method definition
279+
across translation units.
280+
281+
Method containers are implemented as templates, and method definitions scoped
282+
inside containers are implemented as template specializations. Thus methods can
283+
only be added to a container defined in the same namespace, or a namespace
284+
nested inside the namespace where the container has been declared.
285+
286+
`method_container` is an alias for `YOMM2_DECLARE_METHOD_CONTAINER`, provided
287+
by header `yorel/yomm2/cute.hpp`.
288+
289+
## macros YOMM2_FRIEND, friend_method
290+
291+
#### Synopsis:
292+
293+
```
294+
YOMM2_FRIEND(container)
295+
YOMM2_FRIEND(container, return_type, (unspecified_type... argument))
296+
```
297+
298+
Grant friendship to all the methods inside a container friend of a class, or to
299+
a specific method. See [containers](examples/containers) for an example.
300+
301+
`friend_method_container` is an alias for `YOMM2_FRIEND`, provided by header
302+
`yorel/yomm2/cute.hpp`.
303+
304+
## macros YOMM2_DEFINITION, method_definition
305+
306+
#### Synopsis:
307+
308+
```
309+
YOMM2_DEFINITION(container, return_type, (unspecified_type... argument))
310+
```
311+
312+
Retrieve a method definition with a given return type and signature from a
313+
container.
314+
315+
The resulting method can be used as a normal function reference. It can be
316+
called, or its address can be taken. In particular, this makes it possible for
317+
a method definition to call a base method as part of its implementation, in the
318+
same manner as an ordinary virtual function can call a specific base function
319+
by prefixing its name with a base class name.
320+
321+
Note that the preferred way of calling the overriden method is via `next`. In
322+
normal circumstances, a method definition cannot assume which "super" or "base"
323+
function is the best choice, since the set of methods pertaining to the same
324+
declaration is open.
325+
326+
See [containers](examples/containers) for an example.
327+
328+
`method_definition` is an alias for `YOMM2_DEFINITION`, provided by header
329+
`yorel/yomm2/cute.hpp`.
330+
225331
## function next
226332

227333
#### Synopsis:
228334
```
229-
YOMM2_DEFINE(return_type, name, (type... arg)) {
335+
YOMM2_DEFINE(return_type, name, (unspecified_type... arg)) {
230336
....
231337
next(arg...);
232338
...

dev/run-target

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
COMPILER=$1
22
BUILD=$2
33
TARGET=$3
4-
cd build/$COMPILER/$BUILD && make $TARGET && find . -name $TARGET -exec $YOMM2_RUN_TARGET_PREFIX {} \;
4+
cd build/$COMPILER/$BUILD && make $TARGET && find . -name $TARGET -type f -exec $YOMM2_RUN_TARGET_PREFIX {} \;

examples/CMakeLists.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ add_executable(asteroids asteroids.cpp)
2727
target_link_libraries (asteroids yomm2)
2828
add_test (asteroids asteroids)
2929

30-
add_executable(friendship friendship.cpp)
31-
target_link_libraries (friendship yomm2)
32-
add_test (friendship friendship)
30+
add_subdirectory (containers)
31+
add_test (containers containers)
3332

3433
if(NOT MSVC)
3534
set(CMAKE_SKIP_BUILD_RPATH TRUE)

examples/containers/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2018-2020 Jean-Louis Leroy
2+
# Distributed under the Boost Software License, Version 1.0.
3+
# See accompanying file LICENSE_1_0.txt
4+
# or copy at http://www.boost.org/LICENSE_1_0.txt)
5+
6+
add_executable(
7+
containers
8+
main.cpp
9+
shape_painter.cpp concrete_shape_painters.cpp
10+
line_painter.cpp arc_painter.cpp segment_painter.cpp
11+
painter.cpp)
12+
target_link_libraries (containers yomm2)

examples/containers/README.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Method Containers, Namespaces, Inline and Friendship
2+
3+
This example shows how to use method containers to grant friendship to method
4+
definitions, and access method defintions across translation units and
5+
namespaces.
6+
7+
Header [`geometries.hpp`](geometries.hpp) defines a class hierarchy in
8+
namespace `geometries`, representing 1D and 2D geometrical entities. Its
9+
structure is as follows:
10+
11+
```
12+
header geometries.hpp
13+
namespace geometries
14+
class Geometry
15+
class Line
16+
class Arc
17+
class Segment
18+
class Shape
19+
class Square
20+
class Circle
21+
```
22+
23+
Header [`painter.hpp`](painter.hpp) defines a mechanism for geometry
24+
objects. It consists of a `Painter` class, a `paintObject` open method, two
25+
container declarations inside two nested namespaces, respectively for
26+
1-dimensional and 2-dimensional entities.
27+
28+
Its structure is as follows:
29+
30+
```
31+
painter.hpp
32+
namespace painter
33+
namespace paint1d
34+
method container painters
35+
namespace paint2d
36+
method container painters
37+
class Painter
38+
friend declaration for all methods defined in paint1d::painters
39+
friend declaration for the method defined in paint2d::painters taking a Shape
40+
declaration of open method paintObject
41+
```
42+
43+
Note that we are using *two* distinct containers, both called `painters`, but
44+
one is in namespace `paint1d` and the other in namespace `paint2d`.
45+
46+
[`line_painter.hpp`](line_painter.hpp) defines an *inline* implementation of
47+
`paintObject` for `Line`:
48+
49+
```
50+
header line_painter.hpp
51+
namespace painter
52+
namespace paint1d
53+
define paintObject for `Line`, making it inline
54+
```
55+
56+
[`segment_painter.hpp`](segment_painter.hpp) implements `paintObject` for
57+
`Segment`, calling the base method for `Line` in the process:
58+
59+
```
60+
translation unit segment_painter.cpp
61+
namespace painter
62+
namespace paint1d
63+
define paintObject for `Segment`
64+
call inline `paintObject` for `Line` from container `paint1d::painters`
65+
```
66+
67+
[`arc_painter.cpp`](arc_painter.cpp) does the same for `Arc`.
68+
69+
Note that all three method definitions (for `Line`, `Segment` and `Arc`) can
70+
access the private parts of `Painter`, because they are defined inside a
71+
container that was granted friendship as a whole.
72+
73+
Also note that the `next` mechanism is not used, instead the method definition for
74+
`Line` is always used.
75+
76+
[`shape_painter.hpp`](shape_painter.hpp) declares (but does not define) that
77+
container `paint2d::painters` contains a `paintObject` method for `Shape`:
78+
79+
```
80+
header shape_painter.hpp
81+
namespace painter
82+
namespace paint2d
83+
declare method container `painters` and inside it, `paintObject(Painter, Shape)`
84+
```
85+
86+
[`shape_painter.cpp`](shape_painter.cpp) defines the said method:
87+
88+
```
89+
translation unit shape_painter.cpp
90+
namespace painter
91+
namespace paint2d
92+
define `paintObject(Painter, Shape)` inside method container `painters`
93+
```
94+
95+
Note that this method definition is a friend of `Painter`, and thus can access
96+
its private parts.
97+
98+
Finally, [`concrete_shape_painters.cpp`](concrete_shape_painters.cpp) defines
99+
`paintObject` for `Square` and `Circle`:
100+
101+
```
102+
translation unit shape_painter.cpp
103+
namespace painter
104+
namespace paint2d
105+
define `paintObject(Painter, Square)` inside method container `painters`
106+
define `paintObject(Painter, Circle)` inside method container `painters`
107+
```
108+
109+
Both call base method `paintObject(Painter, Shape)` declared in
110+
[`shape_painter.hpp`](shape_painter.hpp) and defined in
111+
[`shape_painter.cpp`](shape_painter.cpp).
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) 2020 Jean-Louis Leroy
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// See accompanying file LICENSE_1_0.txt
4+
// or copy at http://www.boost.org/LICENSE_1_0.txt)
5+
6+
// This exmaple is based on sample code provided by Github user matpen in
7+
// https://github.com/jll63/yomm2/issues/7
8+
9+
#include <iostream>
10+
11+
#include <yorel/yomm2/cute.hpp>
12+
13+
#include "geometries.hpp"
14+
#include "line_painter.hpp"
15+
16+
register_class(geometries::Arc, geometries::Line);
17+
18+
namespace painter {
19+
namespace paint1d {
20+
21+
define_method(
22+
painters,
23+
void, paintObject, (Painter& painter, const geometries::Arc& arc))
24+
{
25+
++painter.counter;
26+
method_definition(painters, void, (Painter&, const geometries::Line&))(painter, arc);
27+
std::cout << " " << "painting arc\n";
28+
}
29+
30+
}
31+
}

0 commit comments

Comments
 (0)