Skip to content

Commit 428ecb1

Browse files
committed
wip
1 parent 5b28fb7 commit 428ecb1

2 files changed

Lines changed: 44 additions & 23 deletions

File tree

README.md

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,29 @@ turn on optimizations (`-O2 -DNDEBUG`) and to select the Boost library.
1717

1818
## OpenMethod vs Boost.TypeErasure
1919

20-
At first sight, OpenMethod and TypeErasure exist in the same space: runtime
21-
polymorphic systems that offer a solution to the Expression Problem.
20+
OpenMethod and TypeErasure exist in the same space: runtime polymorphic systems
21+
that offer a solution to the Expression Problem.
2222

23-
Thanks to BOOST_TYPE_ERASURE_FREE, one can add operations to existing classes,
24-
without the need to modify them. And of course one can add new classes to
25-
existing operations, by providing the required functions on them. However, one
26-
quickly hits a wall, and this is related to the fact that in B.TE polymorphism
27-
is imbued to a "magic" handle, `any`, whereas in OpenMethod, polymorphism is
28-
attached to the object itself.
23+
Thanks to BOOST_TYPE_ERASURE_FREE, we can add operations to existing classes,
24+
without the need to modify them. And of course we can add new classes to
25+
existing operations, by providing the required functions on them.
2926

30-
To make this more concrete, let's look at an example. Consider a matrix library,
31-
which contains several matrix subtypes (let's limit ourselves to "ordinary" and
32-
"symmetric") and operations (let's just focus on transposition).
27+
However, we quickly hits a wall, and this is related to the fact that in B.TE
28+
polymorphism is imbued to a "magic" handle, `any`, whereas in OpenMethod,
29+
polymorphism is attached to the object itself.
30+
31+
Let's look at an example. Consider a matrix library, which contains several
32+
matrix subtypes (let's limit ourselves to "ordinary" and "symmetric") and
33+
operations (let's just focus on transposition).
3334

3435
Here is (what I believe is) a plausible design:
3536

3637
```c++
3738
#include <boost/mpl/vector.hpp>
3839
#include <boost/type_erasure/any.hpp>
3940
#include <boost/type_erasure/free.hpp>
41+
#include <boost/type_erasure/typeid_of.hpp>
42+
#include <boost/core/demangle.hpp>
4043
#include <iostream>
4144

4245
namespace mpl = boost::mpl;
@@ -45,51 +48,62 @@ using namespace boost::type_erasure;
4548
BOOST_TYPE_ERASURE_FREE(transpose);
4649

4750
struct ordinary_matrix_impl {
48-
ordinary_matrix_impl() {
49-
std::cout << "ordinary_matrix_impl\n";
50-
}
51+
virtual ~ordinary_matrix_impl() { /* ... */ }
52+
std::size_t rows, cols;
53+
std::vector<double> elements; // rows x cols doubles
5154
};
5255

5356
struct symmetric_matrix_impl {
54-
symmetric_matrix_impl() {
55-
std::cout << "symmetric_matrix_impl\n";
56-
}
57+
virtual ~symmetric_matrix_impl() { /* ... */ }
58+
std::size_t rows; // cols == rows
59+
std::vector<double> elements; // rows doubles
5760
};
5861

5962
struct matrix {
60-
any<mpl::vector<copy_constructible<>, has_transpose<matrix(_self&)>>> impl;
63+
any<mpl::vector<copy_constructible<>, has_transpose<matrix(_self&)>, typeid_<>>> impl;
6164
};
6265

6366
auto transpose(matrix m) {
6467
return transpose(m.impl);
6568
}
6669

6770
auto transpose(ordinary_matrix_impl& m) {
68-
std::cout << "transpose ordinary matrix\n";
6971
return matrix{ordinary_matrix_impl()};
7072
}
7173

7274
auto transpose(symmetric_matrix_impl& m) {
73-
std::cout << "transpose symmetric matrix\n";
7475
return matrix{m};
7576
}
7677

7778
auto main() -> int {
7879
{
7980
matrix m{ordinary_matrix_impl()};
8081
auto t = transpose(m);
82+
std::cout << boost::core::demangle(typeid_of(t.impl).name()) << "\n";
83+
// ordinary_matrix_impl
8184
}
8285

8386
{
8487
matrix m{symmetric_matrix_impl()};
8588
auto t = transpose(m);
89+
std::cout << boost::core::demangle(typeid_of(t.impl).name()) << "\n";
90+
// symmetric_matrix_impl
8691
}
8792

8893
return 0;
8994
}
9095
```
9196

92-
([Compiler Explorer](https://godbolt.org/z/hWoM6jWdd))
97+
([Compiler Explorer](https://godbolt.org/z/613Eo5zGP)
98+
99+
We were able to add new behavior to existing classes (the "impl" classes)
100+
without needing to modify them. This is promising.
101+
102+
Here is the equivalent using open-methods:
103+
104+
```c++
105+
```
106+
107+
93108

94-
Now let's suppose that an application needs to serialize matrices to JSON. The
95-
operation can be added to the matrix library in the same way as `transpose`:
109+
We can also add serialization to JSON:

doc/introduction.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ the Expression Problem:
99
to add new operations on the existing types, and new types to the existing
1010
operations, without modifying existing code?
1111

12+
Open-methods also offer a solution to the banana-gorilla-jungle problem:
13+
14+
> The problem with object-oriented languages is they’ve got all this implicit
15+
environment that they carry around with them. You wanted a banana but what you
16+
got was a gorilla holding the banana and the entire jungle. — Joe Armstrong,
17+
creator of Erlang progamming language
18+
1219
As a bonus, open-methods can take more than one argument into account when
1320
selecting the appropriate function to call - aka multiple dispatch. For that
1421
reason, open-methods are often called multi-methods, but that term is

0 commit comments

Comments
 (0)