Browse code

WIP: Add implementation

Robert Cranston authored on 18/06/2023 16:41:24
Showing 7 changed files

... ...
@@ -16,5 +16,7 @@ add_library(${PROJECT_NAME} INTERFACE)
16 16
 ## C++
17 17
 include(common.cmake)
18 18
 common(
19
+    DISABLE_CLANG_TIDY # TODO
20
+    DISABLE_CPPCHECK # TODO
19 21
     CXX_STANDARD 14
20 22
 )
... ...
@@ -2,6 +2,10 @@
2 2
 
3 3
 **C**oncept-based **R**elaying **Any** C++14 type.
4 4
 
5
+For a usage example, see `tests/crany.cpp`.
6
+
7
+For a diagram visualizing the implementation, see `doc/crany.dot.png`.
8
+
5 9
 [`crany`]: https://git.rcrnstn.net/rcrnstn/crany
6 10
 
7 11
 ## References
8 12
new file mode 100644
... ...
@@ -0,0 +1,19 @@
1
+digraph "Crany"
2
+{
3
+    edge [dir=back,arrowtail=empty]
4
+
5
+    "ModelSelf<Model<Self>>" -> "Concept<ModelSelf<Model<Self>>>" [label="self()"]
6
+    "Concept<ModelSelf<Model<Self>>>" -> "Model" [label="static relays"]
7
+
8
+    "ConceptSelf" -> "Concept<ConceptSelf>" [label="recursive self()"]
9
+    "Concept<ConceptSelf>" -> "CloneConcept" [label="dynamic definitions"]
10
+
11
+    "CranySelf<Crany<Concept>>" -> "Concept<CranySelf<Crany<Concept>>>" [label="self()"]
12
+    "Concept<CranySelf<Crany<Concept>>>" -> "Crany<Concept>" [label="static relays"]
13
+
14
+    "CloneConcept" -> "ModelSelf<Model<Self>>" [label="clone()" constraint=false]
15
+
16
+    "Crany<Concept>" -> "CloneConcept"  [label="self_" arrowtail=diamond constraint=false]
17
+
18
+    "Model" -> "Self" [label="self_" arrowtail=diamond]
19
+}
0 20
new file mode 100644
1 21
Binary files /dev/null and b/doc/crany.dot.png differ
2 22
new file mode 100644
... ...
@@ -0,0 +1,137 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
3
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+<!-- Generated by graphviz version 2.43.0 (0)
5
+ -->
6
+<!-- Title: Crany Pages: 1 -->
7
+<svg width="787pt" height="328pt"
8
+ viewBox="0.00 0.00 787.00 328.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
9
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 324)">
10
+<title>Crany</title>
11
+<polygon fill="white" stroke="transparent" points="-4,4 -4,-324 783,-324 783,4 -4,4"/>
12
+<text text-anchor="middle" x="389.5" y="-304.8" font-family="Arial" font-size="14.00" fill="grey">Crany</text>
13
+<!-- ModelSelf&lt;Model&lt;Self&gt;&gt; -->
14
+<g id="node1" class="node">
15
+<title>ModelSelf&lt;Model&lt;Self&gt;&gt;</title>
16
+<path fill="none" stroke="black" d="M192.5,-297C192.5,-297 45.5,-297 45.5,-297 39.5,-297 33.5,-291 33.5,-285 33.5,-285 33.5,-273 33.5,-273 33.5,-267 39.5,-261 45.5,-261 45.5,-261 192.5,-261 192.5,-261 198.5,-261 204.5,-267 204.5,-273 204.5,-273 204.5,-285 204.5,-285 204.5,-291 198.5,-297 192.5,-297"/>
17
+<text text-anchor="middle" x="119" y="-275.3" font-family="Arial" font-size="14.00">ModelSelf&lt;Model&lt;Self&gt;&gt;</text>
18
+</g>
19
+<!-- Concept&lt;ModelSelf&lt;Model&lt;Self&gt;&gt;&gt; -->
20
+<g id="node2" class="node">
21
+<title>Concept&lt;ModelSelf&lt;Model&lt;Self&gt;&gt;&gt;</title>
22
+<path fill="none" stroke="black" d="M226,-210C226,-210 12,-210 12,-210 6,-210 0,-204 0,-198 0,-198 0,-186 0,-186 0,-180 6,-174 12,-174 12,-174 226,-174 226,-174 232,-174 238,-180 238,-186 238,-186 238,-198 238,-198 238,-204 232,-210 226,-210"/>
23
+<text text-anchor="middle" x="119" y="-188.3" font-family="Arial" font-size="14.00">Concept&lt;ModelSelf&lt;Model&lt;Self&gt;&gt;&gt;</text>
24
+</g>
25
+<!-- ModelSelf&lt;Model&lt;Self&gt;&gt;&#45;&gt;Concept&lt;ModelSelf&lt;Model&lt;Self&gt;&gt;&gt; -->
26
+<g id="edge1" class="edge">
27
+<title>ModelSelf&lt;Model&lt;Self&gt;&gt;&#45;&gt;Concept&lt;ModelSelf&lt;Model&lt;Self&gt;&gt;&gt;</title>
28
+<path fill="none" stroke="black" d="M119,-250.73C119,-237.42 119,-221.81 119,-210.18"/>
29
+<polygon fill="none" stroke="black" points="115.5,-250.8 119,-260.8 122.5,-250.8 115.5,-250.8"/>
30
+<text text-anchor="middle" x="137.5" y="-231.8" font-family="Times,serif" font-size="14.00">self()</text>
31
+</g>
32
+<!-- Model -->
33
+<g id="node3" class="node">
34
+<title>Model</title>
35
+<path fill="none" stroke="black" d="M134,-123C134,-123 104,-123 104,-123 98,-123 92,-117 92,-111 92,-111 92,-99 92,-99 92,-93 98,-87 104,-87 104,-87 134,-87 134,-87 140,-87 146,-93 146,-99 146,-99 146,-111 146,-111 146,-117 140,-123 134,-123"/>
36
+<text text-anchor="middle" x="119" y="-101.3" font-family="Arial" font-size="14.00">Model</text>
37
+</g>
38
+<!-- Concept&lt;ModelSelf&lt;Model&lt;Self&gt;&gt;&gt;&#45;&gt;Model -->
39
+<g id="edge2" class="edge">
40
+<title>Concept&lt;ModelSelf&lt;Model&lt;Self&gt;&gt;&gt;&#45;&gt;Model</title>
41
+<path fill="none" stroke="black" d="M119,-163.73C119,-150.42 119,-134.81 119,-123.18"/>
42
+<polygon fill="none" stroke="black" points="115.5,-163.8 119,-173.8 122.5,-163.8 115.5,-163.8"/>
43
+<text text-anchor="middle" x="163" y="-144.8" font-family="Times,serif" font-size="14.00">static relays</text>
44
+</g>
45
+<!-- Self -->
46
+<g id="node4" class="node">
47
+<title>Self</title>
48
+<path fill="none" stroke="black" d="M134,-36C134,-36 104,-36 104,-36 98,-36 92,-30 92,-24 92,-24 92,-12 92,-12 92,-6 98,0 104,0 104,0 134,0 134,0 140,0 146,-6 146,-12 146,-12 146,-24 146,-24 146,-30 140,-36 134,-36"/>
49
+<text text-anchor="middle" x="119" y="-14.3" font-family="Arial" font-size="14.00">Self</text>
50
+</g>
51
+<!-- Model&#45;&gt;Self -->
52
+<g id="edge3" class="edge">
53
+<title>Model&#45;&gt;Self</title>
54
+<path fill="none" stroke="black" d="M119,-74.74C119,-61.88 119,-47.23 119,-36.18"/>
55
+<polygon fill="black" stroke="black" points="119,-74.8 123,-80.8 119,-86.8 115,-80.8 119,-74.8"/>
56
+<text text-anchor="middle" x="135.5" y="-57.8" font-family="Times,serif" font-size="14.00">self_</text>
57
+</g>
58
+<!-- ConceptSelf -->
59
+<g id="node5" class="node">
60
+<title>ConceptSelf</title>
61
+<path fill="none" stroke="black" d="M453,-297C453,-297 387,-297 387,-297 381,-297 375,-291 375,-285 375,-285 375,-273 375,-273 375,-267 381,-261 387,-261 387,-261 453,-261 453,-261 459,-261 465,-267 465,-273 465,-273 465,-285 465,-285 465,-291 459,-297 453,-297"/>
62
+<text text-anchor="middle" x="420" y="-275.3" font-family="Arial" font-size="14.00">ConceptSelf</text>
63
+</g>
64
+<!-- Concept&lt;ConceptSelf&gt; -->
65
+<g id="node6" class="node">
66
+<title>Concept&lt;ConceptSelf&gt;</title>
67
+<path fill="none" stroke="black" d="M486.5,-210C486.5,-210 353.5,-210 353.5,-210 347.5,-210 341.5,-204 341.5,-198 341.5,-198 341.5,-186 341.5,-186 341.5,-180 347.5,-174 353.5,-174 353.5,-174 486.5,-174 486.5,-174 492.5,-174 498.5,-180 498.5,-186 498.5,-186 498.5,-198 498.5,-198 498.5,-204 492.5,-210 486.5,-210"/>
68
+<text text-anchor="middle" x="420" y="-188.3" font-family="Arial" font-size="14.00">Concept&lt;ConceptSelf&gt;</text>
69
+</g>
70
+<!-- ConceptSelf&#45;&gt;Concept&lt;ConceptSelf&gt; -->
71
+<g id="edge4" class="edge">
72
+<title>ConceptSelf&#45;&gt;Concept&lt;ConceptSelf&gt;</title>
73
+<path fill="none" stroke="black" d="M420,-250.73C420,-237.42 420,-221.81 420,-210.18"/>
74
+<polygon fill="none" stroke="black" points="416.5,-250.8 420,-260.8 423.5,-250.8 416.5,-250.8"/>
75
+<text text-anchor="middle" x="474" y="-231.8" font-family="Times,serif" font-size="14.00">recursive self()</text>
76
+</g>
77
+<!-- CloneConcept -->
78
+<g id="node7" class="node">
79
+<title>CloneConcept</title>
80
+<path fill="none" stroke="black" d="M459,-123C459,-123 381,-123 381,-123 375,-123 369,-117 369,-111 369,-111 369,-99 369,-99 369,-93 375,-87 381,-87 381,-87 459,-87 459,-87 465,-87 471,-93 471,-99 471,-99 471,-111 471,-111 471,-117 465,-123 459,-123"/>
81
+<text text-anchor="middle" x="420" y="-101.3" font-family="Arial" font-size="14.00">CloneConcept</text>
82
+</g>
83
+<!-- Concept&lt;ConceptSelf&gt;&#45;&gt;CloneConcept -->
84
+<g id="edge5" class="edge">
85
+<title>Concept&lt;ConceptSelf&gt;&#45;&gt;CloneConcept</title>
86
+<path fill="none" stroke="black" d="M420,-163.73C420,-150.42 420,-134.81 420,-123.18"/>
87
+<polygon fill="none" stroke="black" points="416.5,-163.8 420,-173.8 423.5,-163.8 416.5,-163.8"/>
88
+<text text-anchor="middle" x="490.5" y="-144.8" font-family="Times,serif" font-size="14.00">dynamic definitions</text>
89
+</g>
90
+<!-- CloneConcept&#45;&gt;ModelSelf&lt;Model&lt;Self&gt;&gt; -->
91
+<g id="edge6" class="edge">
92
+<title>CloneConcept&#45;&gt;ModelSelf&lt;Model&lt;Self&gt;&gt;</title>
93
+<path fill="none" stroke="black" d="M367.44,-127.24C340.96,-139.12 309.02,-155.3 283,-174 264.62,-187.2 265.19,-196.54 247,-210 219.28,-230.5 184.57,-248.53 158.29,-260.87"/>
94
+<polygon fill="none" stroke="black" points="369.19,-130.29 376.94,-123.06 366.37,-123.89 369.19,-130.29"/>
95
+<text text-anchor="middle" x="307.5" y="-188.3" font-family="Times,serif" font-size="14.00">clone()</text>
96
+</g>
97
+<!-- CranySelf&lt;Crany&lt;Concept&gt;&gt; -->
98
+<g id="node8" class="node">
99
+<title>CranySelf&lt;Crany&lt;Concept&gt;&gt;</title>
100
+<path fill="none" stroke="black" d="M733.5,-297C733.5,-297 562.5,-297 562.5,-297 556.5,-297 550.5,-291 550.5,-285 550.5,-285 550.5,-273 550.5,-273 550.5,-267 556.5,-261 562.5,-261 562.5,-261 733.5,-261 733.5,-261 739.5,-261 745.5,-267 745.5,-273 745.5,-273 745.5,-285 745.5,-285 745.5,-291 739.5,-297 733.5,-297"/>
101
+<text text-anchor="middle" x="648" y="-275.3" font-family="Arial" font-size="14.00">CranySelf&lt;Crany&lt;Concept&gt;&gt;</text>
102
+</g>
103
+<!-- Concept&lt;CranySelf&lt;Crany&lt;Concept&gt;&gt;&gt; -->
104
+<g id="node9" class="node">
105
+<title>Concept&lt;CranySelf&lt;Crany&lt;Concept&gt;&gt;&gt;</title>
106
+<path fill="none" stroke="black" d="M767,-210C767,-210 529,-210 529,-210 523,-210 517,-204 517,-198 517,-198 517,-186 517,-186 517,-180 523,-174 529,-174 529,-174 767,-174 767,-174 773,-174 779,-180 779,-186 779,-186 779,-198 779,-198 779,-204 773,-210 767,-210"/>
107
+<text text-anchor="middle" x="648" y="-188.3" font-family="Arial" font-size="14.00">Concept&lt;CranySelf&lt;Crany&lt;Concept&gt;&gt;&gt;</text>
108
+</g>
109
+<!-- CranySelf&lt;Crany&lt;Concept&gt;&gt;&#45;&gt;Concept&lt;CranySelf&lt;Crany&lt;Concept&gt;&gt;&gt; -->
110
+<g id="edge7" class="edge">
111
+<title>CranySelf&lt;Crany&lt;Concept&gt;&gt;&#45;&gt;Concept&lt;CranySelf&lt;Crany&lt;Concept&gt;&gt;&gt;</title>
112
+<path fill="none" stroke="black" d="M648,-250.73C648,-237.42 648,-221.81 648,-210.18"/>
113
+<polygon fill="none" stroke="black" points="644.5,-250.8 648,-260.8 651.5,-250.8 644.5,-250.8"/>
114
+<text text-anchor="middle" x="666.5" y="-231.8" font-family="Times,serif" font-size="14.00">self()</text>
115
+</g>
116
+<!-- Crany&lt;Concept&gt; -->
117
+<g id="node10" class="node">
118
+<title>Crany&lt;Concept&gt;</title>
119
+<path fill="none" stroke="black" d="M695.5,-123C695.5,-123 600.5,-123 600.5,-123 594.5,-123 588.5,-117 588.5,-111 588.5,-111 588.5,-99 588.5,-99 588.5,-93 594.5,-87 600.5,-87 600.5,-87 695.5,-87 695.5,-87 701.5,-87 707.5,-93 707.5,-99 707.5,-99 707.5,-111 707.5,-111 707.5,-117 701.5,-123 695.5,-123"/>
120
+<text text-anchor="middle" x="648" y="-101.3" font-family="Arial" font-size="14.00">Crany&lt;Concept&gt;</text>
121
+</g>
122
+<!-- Concept&lt;CranySelf&lt;Crany&lt;Concept&gt;&gt;&gt;&#45;&gt;Crany&lt;Concept&gt; -->
123
+<g id="edge8" class="edge">
124
+<title>Concept&lt;CranySelf&lt;Crany&lt;Concept&gt;&gt;&gt;&#45;&gt;Crany&lt;Concept&gt;</title>
125
+<path fill="none" stroke="black" d="M648,-163.73C648,-150.42 648,-134.81 648,-123.18"/>
126
+<polygon fill="none" stroke="black" points="644.5,-163.8 648,-173.8 651.5,-163.8 644.5,-163.8"/>
127
+<text text-anchor="middle" x="692" y="-144.8" font-family="Times,serif" font-size="14.00">static relays</text>
128
+</g>
129
+<!-- Crany&lt;Concept&gt;&#45;&gt;CloneConcept -->
130
+<g id="edge9" class="edge">
131
+<title>Crany&lt;Concept&gt;&#45;&gt;CloneConcept</title>
132
+<path fill="none" stroke="black" d="M575.88,-105C541.85,-105 502.04,-105 471.17,-105"/>
133
+<polygon fill="black" stroke="black" points="576.12,-105 582.12,-101 588.12,-105 582.12,-109 576.12,-105"/>
134
+<text text-anchor="middle" x="529.75" y="-111.8" font-family="Times,serif" font-size="14.00">self_</text>
135
+</g>
136
+</g>
137
+</svg>
0 138
new file mode 100644
... ...
@@ -0,0 +1,139 @@
1
+#ifndef CRANY_HPP_
2
+#define CRANY_HPP_
3
+
4
+
5
+#include <memory>
6
+#include <type_traits>
7
+#include <cassert>
8
+
9
+
10
+template<template<typename> class = std::is_void>
11
+class Crany;
12
+
13
+/// CranySelf
14
+template<>
15
+class Crany<>
16
+{
17
+private:
18
+    template<template<typename> class> friend class Crany;
19
+    template<typename T>
20
+    struct CranySelf
21
+    {
22
+    protected:
23
+        auto       & self()       { return *((T       *)this)->self_; }
24
+        auto const & self() const { return *((T const *)this)->self_; }
25
+        virtual ~CranySelf() = default;
26
+    };
27
+};
28
+
29
+/// Crany
30
+template<template<typename> class Concept>
31
+class Crany final : public Concept<Crany<>::CranySelf<Crany<Concept>>>
32
+{
33
+public:
34
+
35
+    /// Special member functions
36
+    template<typename Self>
37
+    Crany(Self self) : self_{new Model<Self>(std::move(self))} {}
38
+    Crany(Crany const & other) : self_(other.self_->clone()) {}
39
+    Crany & operator=(Crany const & other) { return *this = Crany(other); }
40
+    Crany(Crany && other) = default;
41
+    Crany & operator=(Crany && other) = default;
42
+
43
+private:
44
+
45
+    /// CranySelf
46
+    friend Crany<>::CranySelf<Crany<Concept>>;
47
+
48
+    /// ConceptSelf
49
+    struct ConceptSelf
50
+    {
51
+    protected:
52
+        auto       & self()       { assert(!"Crany Concept non-virtual member function called"); return *(Concept<ConceptSelf>       *)this; }
53
+        auto const & self() const { assert(!"Crany Concept non-virtual member function called"); return *(Concept<ConceptSelf> const *)this; }
54
+        virtual ~ConceptSelf() = default;
55
+    };
56
+
57
+    /// CloneConcept
58
+    struct CloneConcept : Concept<ConceptSelf>
59
+    {
60
+        virtual CloneConcept * clone() const = 0;
61
+    };
62
+
63
+    /// ModelSelf
64
+    template<typename T>
65
+    struct ModelSelf : CloneConcept
66
+    {
67
+    protected:
68
+        auto       & self()       { return ((T       *)this)->self_; }
69
+        auto const & self() const { return ((T const *)this)->self_; }
70
+    };
71
+
72
+    /// Model
73
+    template<typename Self>
74
+    struct Model final : Concept<ModelSelf<Model<Self>>>
75
+    {
76
+        Model(Self self) : self_{std::move(self)} {}
77
+        CloneConcept * clone() const { return new Model<Self>(*this); }
78
+        Self self_;
79
+    };
80
+
81
+    /// Self
82
+    std::unique_ptr<CloneConcept> self_;
83
+
84
+    /// crany_cast
85
+    template<typename T, typename Crany> friend T const * crany_cast(Crany const *  crany) noexcept;
86
+    template<typename T, typename Crany> friend T       * crany_cast(Crany       *  crany) noexcept;
87
+    template<typename T, typename Crany> friend T         crany_cast(Crany const &  crany);
88
+    template<typename T, typename Crany> friend T         crany_cast(Crany       &  crany);
89
+    template<typename T, typename Crany> friend T         crany_cast(Crany       && crany);
90
+};
91
+
92
+/// bad_crany_cast
93
+class bad_crany_cast : public std::bad_cast
94
+{
95
+};
96
+
97
+/// crany_cast
98
+template<typename T, typename Crany>
99
+T const * crany_cast(Crany const * crany) noexcept
100
+{
101
+    if (!crany)
102
+        return nullptr;
103
+    using Model = typename Crany::Model<T>;
104
+    if (auto * model = dynamic_cast<Model *>(crany->self_.get()))
105
+        return &model->self_;
106
+    return nullptr;
107
+}
108
+template<typename T, typename Crany>
109
+T * crany_cast(Crany * crany) noexcept
110
+{
111
+    return const_cast<T *>(*const_cast<T const *>(crany));
112
+}
113
+template<typename T, typename Crany>
114
+T crany_cast(Crany const & crany)
115
+{
116
+    using U = std::remove_cv_t<std::remove_reference_t<T>>;
117
+    if (auto p = crany_cast<U>(&crany))
118
+        return static_cast<T>(*p);
119
+    throw bad_crany_cast(); // TODO
120
+}
121
+template<typename T, typename Crany>
122
+T crany_cast(Crany & crany)
123
+{
124
+    using U = std::remove_cv_t<std::remove_reference_t<T>>;
125
+    if (auto p = crany_cast<U>(&crany))
126
+        return static_cast<T>(*p);
127
+    throw bad_crany_cast(); // TODO
128
+}
129
+template<typename T, typename Crany>
130
+T crany_cast(Crany && crany)
131
+{
132
+    using U = std::remove_cv_t<std::remove_reference_t<T>>;
133
+    if (auto p = crany_cast<U>(&crany))
134
+        return static_cast<T>(std::move(*p));
135
+    throw bad_crany_cast(); // TODO
136
+}
137
+
138
+
139
+#endif // CRANY_HPP_
0 140
new file mode 100644
... ...
@@ -0,0 +1,59 @@
1
+#include "crany.hpp"
2
+
3
+#include <cmath>
4
+#include <iostream>
5
+#include <vector>
6
+
7
+
8
+auto constexpr pi = std::acos(-1.0F);
9
+
10
+
11
+struct Circle
12
+{
13
+    float radius;
14
+    void  scale(float scale)       { radius *= scale;         }
15
+    float area()             const { return pi*radius*radius; }
16
+    float circumference()    const { return 2.0F*pi*radius;   }
17
+};
18
+
19
+struct Rectangle
20
+{
21
+    float side1;
22
+    float side2;
23
+    void  scale(float scale)       { side1 *= scale; side2 *= scale; }
24
+    float area()             const { return side1*side2;             }
25
+};
26
+
27
+
28
+template<typename Self>
29
+struct ShapeConcept : Self
30
+{
31
+    using Self::self;
32
+    virtual void  scale(float value)       { return self().scale(value); }
33
+    virtual float area()             const { return self().area();       }
34
+};
35
+
36
+using Shape = Crany<ShapeConcept>;
37
+using Shapes = std::vector<Shape>;
38
+
39
+
40
+int main()
41
+{
42
+    static_assert(std::is_standard_layout   <Circle>{}, "Circle not is standard layout");
43
+    static_assert(std::is_trivially_copyable<Circle>{}, "Circle not is trivially copyable");
44
+
45
+    auto shapes = Shapes{
46
+        Circle{1.0F},
47
+        Rectangle{2.0F, 3.0F},
48
+    };
49
+    shapes.push_back(shapes.front());
50
+    shapes.back().scale(2.0F);
51
+
52
+    std::cout << std::boolalpha;
53
+    for (auto const & shape : shapes)
54
+        std::cout
55
+            << shape.area()
56
+            << " "
57
+            << bool(crany_cast<Circle>(&shape))
58
+            << "\n";
59
+}