Skip to content

Commit d5d5e6e

Browse files
Finishing todos on the readme
1 parent a3ee939 commit d5d5e6e

File tree

1 file changed

+49
-31
lines changed

1 file changed

+49
-31
lines changed

README.md

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,13 @@
77
![](docs/archetype.png)
88

99

10-
## WARNING -- STILL UNDER CONSTRUCTION
11-
12-
### TODO:
13-
14-
<!-- - clang build -->
15-
- clean up macros, and choose better names
16-
- clean up the readme
17-
<!-- - replace logo -->
18-
- c99 compliant preprocessor macros
19-
- document msvc preprocessor flag
20-
2110
> **Concept based type erasure for C++11 without inheritance or dynamic
2211
> allocation.**
2312
2413
**Archetype** is a lightweight header only library for creating **type erased**,
2514
**concept driven interfaces** without traditional inheritance, or dynamic
2615
memory. It enables **modular**, **reusable** and **low coupling** APIs by
27-
letting you bind objects to **archetypes** which are compile time verified
16+
letting you bind objects to **archetype** _views_ which are compile time verified
2817
interface specifications.
2918

3019
This makes **Archetype** a practical alternative to **inheritance**,
@@ -36,17 +25,20 @@ building portable, flexible libraries.
3625
- Single file, header only library
3726
- Zero dependencies
3827
- No dynamic memory allocation
39-
- Static dispatch (no virtuals)
28+
- No virtuals
4029
- C++11 Compatible
41-
- GCC, Clang, MSVC compatible
30+
- GCC 4.8.1+, Clang 3.3+, MSVC 16.5+(/Zc:preprocessor) compatible
4231
- SFINAE based concept checking
4332
- Works with existing types (no base class required)
4433
- Composable interfaces (build _views_ from parts)
4534
- Great for embedded, plugin, and systems-level code
4635

36+
4737
## Why Archetype?
38+
Archetype provides a simple, clean, and extremely flexible way to define type erased interfaces, giving you a common way to interact with polymorphic types.
39+
4840

49-
Inheritance is often overused when **composition** or **concepts** would have
41+
Inheritance is often misused when **composition** or **concepts** would have
5042
been more appropriate. However, alternatives to inheritance often mean losing
5143
the ability to use clean, type erased interfaces.
5244

@@ -56,15 +48,16 @@ Archetype fills this gap:
5648
- Like composition, it avoids rigid hierarchies
5749
- Like concepts, it expresses **interface intent** clearly
5850
- Like std::function it allows **type erased binding**
59-
- But unlike any of these **TODO**
51+
- But unlike any of them it provides type erased, static dispatch, without dynamic memory allocation, and with composable interfaces at the same time
52+
6053

6154
## The Inheritance Problem
6255

63-
Inheritance gives you base pointers and dynamic dispatch, but at a cost:
56+
Inheritance gives you base pointers and dynamic dispatch but at a cost.
6457

6558
### Example
6659

67-
Suppose you want to reuse parts of `A`, `B`, `C`.
60+
Suppose you want to reuse parts of classes `A`, `B`, and `C`.
6861

6962
```cpp
7063
class A { void a(); };
@@ -75,24 +68,26 @@ class AB : public A, public B {};
7568
class AC : public A, public C {};
7669
class BC : public B, public C {};
7770
```
71+
We can refer `AB` and `AC` with an `A` base pointer (common interface).
72+
Or `AC` and `BC` with a `C`base pointer.
7873
79-
We can refer `AB` and `AC` with an `A` base pointer. Or `AC` and `BC` with a `C`
80-
base pointer. But if we want to refer to any object that implements both `A` and
81-
`C` like `ABC` or `ACD`?
74+
But if we want to refer to any object that implements both `A` and
75+
`C` like `ABC` or `ACD`.
8276
8377
With inheritance you:
84-
8578
- Lose composability
8679
- Struggle to find a common base
8780
- Risk coupling, and rigid hierarchies
8881
- Risk diamond problems (in multiple inheritance)
8982
9083
With composition:
91-
9284
- You can't refer to composites polymorphically
9385
- There's no base interface, unless you add one manually
9486
95-
## Archetypes
87+
With other techiques like **CRTP** or **concepts** we still loose the ability to refer
88+
to different types polymorphically ie: we can't create `std::vector<common_interface>`
89+
90+
## Archetypes/Quickstart
9691
9792
**Archetype** gives you **type erased** views over objects that _implement_ a
9893
particular interface without requiring them to inherit from anything.
@@ -122,13 +117,25 @@ ABD abd;
122117
archetype_ab::view abc_view, abd_view;
123118
abc_view.bind(abc);
124119
abd_view.bind(abd);
120+
```
125121

122+
### Use the interface:
123+
```cpp
126124
abc_view.a();
127125
abd_view.b(5);
128126
```
129127

128+
### Alternativley use a pointer style view:
129+
```cpp
130+
archetype_ab::view_ptr<> abc_view_ptr;
131+
abc_view_ptr.bind(abc);
132+
abc_view_ptr->a();
133+
abc_view_ptr->b(5);
134+
```
135+
130136
You now have a **type erased**, **composable**, **low overhead** reference to
131-
any object that implements* the interface regardless of its type or hierarchy.
137+
any object that implements* the interface regardless of its type or hierarchy.
138+
In this case we created views that can view the common `AB` parts of `ABC` and `ABD`
132139
133140
## How Archetype Compares
134141
@@ -139,13 +146,11 @@ any object that implements* the interface regardless of its type or hierarchy.
139146
| **Works with existing types** | ❌ | ❌ | ✅ | ✅ |
140147
| **Composable interfaces** | ❌ | ✅ | ❌ | ✅ |
141148
| **No base class required** | ❌ | ❌ | ✅ | ✅ |
142-
| **Static dispatch** | ❌ (virtual) ||||
143149
| **Runtime polymorphism** | ✅ | ❌ | ✅ | ✅ (type-erased) |
144150
| **Compile-time safety (SFINAE)** | ❌ | ✅ | ❌ | ✅ |
145151
| **Supports mixin views** | ❌ | ✅ | ❌ | ✅ |
146152
| **Header-only** | ✅ | ✅ | ✅ | ✅ |
147153
148-
TODO - double check this table
149154
150155
## Patterns That Work Well With Archetype
151156
@@ -167,7 +172,10 @@ class DoTheThing
167172
}
168173
169174
template<typename T>
170-
void set_logger(T & t) { valid = logger.bind(t); } //TODO - check this
175+
void set_logger(T & t) {
176+
valid = loggable::check<T>::value;
177+
logger.bind(t);
178+
}
171179
};
172180
```
173181

@@ -187,6 +195,7 @@ ARCHETYPE_DEFINE(readable, ( ARCHETYPE_METHOD(int, read, char *, int)))
187195

188196
template <class W> class WriteAPI : public W {
189197
public:
198+
ARCHETYPE_CHECK(writable, W)
190199
using W::W;
191200
using W::write;
192201
void write_api(const char *buf) {
@@ -196,6 +205,7 @@ public:
196205

197206
template <class R> class ReadAPI : public R {
198207
public:
208+
ARCHETYPE_CHECK(readable, R)
199209
using R::R;
200210
using R::read;
201211
int read_api(char *buf, int size) {
@@ -264,11 +274,14 @@ int num_writes = stateful_ref.write_api("stateful writing");
264274
# What Happens On Error?
265275

266276
If a type bound to an archetype doesn’t implement all required methods, the code
267-
will fail at compile time with a clear SFINAE-based error. No runtime surprises.
268-
277+
will fail at compile time with a clear SFINAE-based error. Archetype provides
278+
compile time checking on `bind()` but also provides an `ARCHETYPE_CHECK(<archetype>, T)` macro
279+
for verifying type `T`, and a templated check<T> struct that can be leveraged for SFINAE based
280+
type checking.
269281
# Install
270282

271-
Just drop `archetype.h` into your project.
283+
Just drop `archetype.h` into your project. Note that if you are compiling with MSVC, you
284+
will need to use the `/Zc:preprocessor` compiler options to use c99 compliant preprocessing.
272285

273286
# Philosophy
274287

@@ -289,3 +302,8 @@ frameworks, or trying to untangle brittle inheritance hierarchies Archetype is
289302
for you.
290303

291304
It brings modern modularity to C++11, without the baggage.
305+
306+
307+
# How does it work:
308+
309+
Internally archetype creates manual vtables. The manual part is automated through the macro API.

0 commit comments

Comments
 (0)