]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | [library Boost.TypeErasure |
2 | [quickbook 1.5] | |
3 | [authors [Watanabe, Steven]] | |
4 | [copyright 2011-2013 Steven Watanabe] | |
5 | [license | |
6 | Distributed under the Boost Software License, Version 1.0. | |
7 | (See accompanying file LICENSE_1_0.txt or copy at | |
8 | [@http://www.boost.org/LICENSE_1_0.txt]) | |
9 | ] | |
10 | [purpose Runtime polymorphism based on concepts] | |
11 | ] | |
12 | ||
13 | [def __any [classref boost::type_erasure::any any]] | |
14 | [def __any_cast [funcref boost::type_erasure::any_cast any_cast]] | |
15 | [def __tuple [classref boost::type_erasure::tuple tuple]] | |
16 | [def __rebind_any [classref boost::type_erasure::rebind_any rebind_any]] | |
17 | [def __derived [classref boost::type_erasure::derived derived]] | |
18 | [def __concept_interface [classref boost::type_erasure::concept_interface concept_interface]] | |
19 | [def __constructible [classref boost::type_erasure::constructible constructible]] | |
20 | [def __destructible [classref boost::type_erasure::destructible destructible]] | |
21 | [def __copy_constructible [classref boost::type_erasure::copy_constructible copy_constructible]] | |
22 | [def __assignable [classref boost::type_erasure::assignable assignable]] | |
23 | [def __typeid_ [classref boost::type_erasure::typeid_ typeid_]] | |
24 | [def __relaxed [classref boost::type_erasure::relaxed relaxed]] | |
25 | [def __binding [classref boost::type_erasure::binding binding]] | |
26 | [def __static_binding [classref boost::type_erasure::static_binding static_binding]] | |
27 | [def __placeholder [classref boost::type_erasure::placeholder placeholder]] | |
28 | [def __call [funcref boost::type_erasure::call call]] | |
29 | [def __deduced [classref boost::type_erasure::deduced deduced]] | |
30 | [def __as_param [classref boost::type_erasure::as_param as_param]] | |
31 | ||
32 | [def __addable [classref boost::type_erasure::addable addable]] | |
33 | [def __subtractable [classref boost::type_erasure::subtractable subtractable]] | |
34 | [def __multipliable [classref boost::type_erasure::multipliable multipliable]] | |
35 | [def __dividable [classref boost::type_erasure::dividable dividable]] | |
36 | [def __modable [classref boost::type_erasure::modable modable]] | |
37 | [def __bitandable [classref boost::type_erasure::bitandable bitandable]] | |
38 | [def __bitorable [classref boost::type_erasure::bitorable bitorable]] | |
39 | [def __bitxorable [classref boost::type_erasure::bitxorable bitxorable]] | |
40 | [def __left_shiftable [classref boost::type_erasure::left_shiftable left_shiftable]] | |
41 | [def __right_shiftable [classref boost::type_erasure::right_shiftable right_shiftable]] | |
42 | [def __add_assignable [classref boost::type_erasure::add_assignable add_assignable]] | |
43 | [def __subtract_assignable [classref boost::type_erasure::subtract_assignable subtract_assignable]] | |
44 | [def __multiply_assignable [classref boost::type_erasure::multiply_assignable multiply_assignable]] | |
45 | [def __divide_assignable [classref boost::type_erasure::divide_assignable divide_assignable]] | |
46 | [def __mod_assignable [classref boost::type_erasure::mod_assignable mod_assignable]] | |
47 | [def __bitand_assignable [classref boost::type_erasure::bitand_assignable bitand_assignable]] | |
48 | [def __bitor_assignable [classref boost::type_erasure::bitor_assignable bitor_assignable]] | |
49 | [def __bitxor_assignable [classref boost::type_erasure::bitxor_assignable bitxor_assignable]] | |
50 | [def __left_shift_assignable [classref boost::type_erasure::left_shift_assignable left_shift_assignable]] | |
51 | [def __right_shift_assignable [classref boost::type_erasure::right_shift_assignable right_shift_assignable]] | |
52 | [def __incrementable [classref boost::type_erasure::incrementable incrementable]] | |
53 | [def __decrementable [classref boost::type_erasure::decrementable decrementable]] | |
54 | [def __negatable [classref boost::type_erasure::negatable negatable]] | |
55 | [def __complementable [classref boost::type_erasure::complementable complementable]] | |
56 | [def __dereferenceable [classref boost::type_erasure::dereferenceable dereferenceable]] | |
57 | [def __callable [classref boost::type_erasure::callable callable]] | |
58 | [def __subscriptable [classref boost::type_erasure::subscriptable subscriptable]] | |
59 | [def __equality_comparable [classref boost::type_erasure::equality_comparable equality_comparable]] | |
60 | [def __less_than_comparable [classref boost::type_erasure::less_than_comparable less_than_comparable]] | |
61 | [def __ostreamable [classref boost::type_erasure::ostreamable ostreamable]] | |
62 | [def __istreamable [classref boost::type_erasure::istreamable istreamable]] | |
63 | [def __iterator [classref boost::type_erasure::iterator iterator]] | |
64 | [def __forward_iterator [classref boost::type_erasure::forward_iterator forward_iterator]] | |
65 | [def __bidirectional_iterator [classref boost::type_erasure::bidirectional_iterator bidirectional_iterator]] | |
66 | [def __random_access_iterator [classref boost::type_erasure::random_access_iterator random_access_iterator]] | |
67 | [def __same_type [classref boost::type_erasure::same_type same_type]] | |
68 | ||
69 | [def __BOOST_TYPE_ERASURE_MEMBER [macroref BOOST_TYPE_ERASURE_MEMBER]] | |
70 | [def __BOOST_TYPE_ERASURE_FREE [macroref BOOST_TYPE_ERASURE_FREE]] | |
71 | ||
72 | [section:introduction Introduction] | |
73 | ||
74 | The Boost.TypeErasure library provides runtime polymorphism | |
75 | in C++ that is more flexible than that provided by the | |
76 | core language. | |
77 | ||
78 | C++ has two distinct kinds of polymorphism, | |
79 | virtual functions and templates, each of which has | |
80 | its own advantages and disadvantages. | |
81 | ||
82 | * Virtual functions are not resolved until runtime, | |
83 | while templates are always resolved at compile | |
84 | time. If your types can vary at runtime (for | |
85 | example, if they depend on user input), then | |
86 | static polymorphism with templates doesn't help much. | |
87 | * Virtual functions can be used with separate compilation. | |
88 | The body of a template has to be available | |
89 | in every translation unit in which it is used, | |
90 | slowing down compiles and increasing rebuilds. | |
91 | * Virtual functions automatically make the requirements | |
92 | on the arguments explicit. Templates are only | |
93 | checked when they're instantiated, requiring | |
94 | extra work in testing, assertions, and documentation. | |
95 | * The compiler creates a new copy of each function | |
96 | template every time it is instantiated. This | |
97 | allows better optimization, because the compiler | |
98 | knows everything statically, but it also causes | |
99 | a significant increase of binary sizes. | |
100 | * Templates support Value semantics. Objects that | |
101 | "behave like an int" and are not shared are easier | |
102 | to reason about. To use virtual functions, on | |
103 | the other hand, you have to use (smart) pointers | |
104 | or references. | |
105 | * Template libraries can allow third-party types to | |
106 | be adapted non-intrusively for seamless interoperability. | |
107 | With virtual functions, you have to create a wrapper | |
108 | that inherits from the base class. | |
109 | * Templates can handle constraints involving | |
110 | multiple types. For example, std::for_each | |
111 | takes an iterator range and a function that | |
112 | can be called on the elements of the range. | |
113 | Virtual functions aren't really able to | |
114 | express such constraints. | |
115 | ||
116 | The Boost.TypeErasure library combines the superior | |
117 | abstraction capabilities of templates, with the | |
118 | runtime flexibility of virtual functions. | |
119 | ||
120 | Boost includes several special cases of this kind | |
121 | of polymorphism: | |
122 | ||
123 | * `boost::any` for CopyConstructible types. | |
124 | * `boost::function` for objects that can be called like functions. | |
125 | * Boost.Range provides `any_iterator`. | |
126 | ||
127 | Boost.TypeErasure generalizes this to support arbitrary | |
128 | requirements and provides a | |
129 | [link boost_typeerasure.predef predefined set of common concepts] | |
130 | ||
131 | [endsect] | |
132 | ||
133 | [section:reading How to read this documentation] | |
134 | ||
135 | To avoid excessive verbosity, all the examples | |
136 | assume that a few using directives are in place. | |
137 | ||
138 | namespace mpl = boost::mpl; | |
139 | using namespace boost::type_erasure; | |
140 | ||
141 | [endsect] | |
142 | ||
143 | [section:basic Basic Usage] | |
144 | [import ../example/basic.cpp] | |
145 | [basic] | |
146 | [endsect] | |
147 | ||
148 | [section Composing Concepts] | |
149 | [import ../example/compose.cpp] | |
150 | [compose] | |
151 | [endsect] | |
152 | ||
153 | [section:multi Functions with Multiple Arguments] | |
154 | [import ../example/multi.cpp] | |
155 | [multi] | |
156 | [endsect] | |
157 | ||
158 | [section:concept Concepts in Depth] | |
159 | ||
160 | [section:custom Defining Custom Concepts] | |
161 | [import ../example/custom.cpp] | |
162 | [custom] | |
163 | [endsect] | |
164 | ||
165 | [section:overload Overloading] | |
166 | [import ../example/overload.cpp] | |
167 | [overload] | |
168 | [endsect] | |
169 | ||
170 | [section:concept_map Concept Maps] | |
171 | [import ../example/concept_map.cpp] | |
172 | [concept_map] | |
173 | [endsect] | |
174 | ||
175 | [section:overload Associated Types] | |
176 | [import ../example/associated.cpp] | |
177 | [associated] | |
178 | [endsect] | |
179 | ||
180 | [endsect] | |
181 | ||
182 | [section:any Using Any] | |
183 | ||
184 | [section:construction Construction] | |
185 | [import ../example/construction.cpp] | |
186 | [construction] | |
187 | [endsect] | |
188 | ||
189 | [section Conversions] | |
190 | [import ../example/convert.cpp] | |
191 | [convert] | |
192 | [endsect] | |
193 | ||
194 | [section:references References] | |
195 | [import ../example/references.cpp] | |
196 | [references] | |
197 | [endsect] | |
198 | ||
199 | [section:limit Syntax Limitations] | |
200 | ||
201 | In most cases using an any has the same | |
202 | syntax as using the underlying object. | |
203 | However, there are a few cases where | |
204 | this is not possible to implement. | |
205 | An __any reference is proxy and cannot | |
206 | be used in contexts where a real | |
207 | reference is required. In particular, | |
208 | __forward_iterator does not create | |
209 | a conforming ForwardIterator (unless | |
210 | the value_type is fixed.) Another | |
211 | difference is that all operations | |
212 | which do not take at least one __any | |
213 | argument have to be passed the type | |
214 | information explicitly. Static member | |
215 | functions and constructors can fall in | |
216 | this category. All this means that generic | |
217 | algorithms might not work when applied to | |
218 | __any arguments. | |
219 | ||
220 | [endsect] | |
221 | ||
222 | [endsect] | |
223 | ||
224 | [section:examples Examples] | |
225 | ||
226 | [section:print_sequence A polymorphic range formatter] | |
227 | [import ../example/print_sequence.cpp] | |
228 | [print_sequence] | |
229 | [endsect] | |
230 | ||
231 | [section:printf A type-safe printf] | |
232 | [import ../example/printf.cpp] | |
233 | [printf] | |
234 | [endsect] | |
235 | ||
236 | [section:multifunction Boost.Function with multiple signatures] | |
237 | [import ../example/multifunction.cpp] | |
238 | [multifunction] | |
239 | [endsect] | |
240 | ||
241 | [endsect] | |
242 | ||
243 | [section:conceptdef Concept Definitions] | |
244 | ||
245 | A Concept defines a set of constraints on the types that | |
246 | are stored in an __any. | |
247 | ||
248 | There are three kinds of concepts. | |
249 | ||
250 | # The library defines a number of [link boost_typeerasure.predef predefined concepts]. | |
251 | Most of these are equivalent to user-defined concepts, but a few | |
252 | require special handling. | |
253 | # Users can define their own primitive concepts as described below. | |
254 | The macros __BOOST_TYPE_ERASURE_MEMBER and __BOOST_TYPE_ERASURE_FREE | |
255 | define concepts of this form. | |
256 | # Any MPL Forward Sequence whose elements are | |
257 | concepts is also a concept. This allows concepts | |
258 | to be composed easily. | |
259 | ||
260 | Each primitive concept defines a single function. | |
261 | A primitive concept must be a specialization of a | |
262 | class template, with a static member function | |
263 | called `apply`, which will be executed when the | |
264 | function is dispatched by __call. The template | |
265 | can only take template type parameters. non-type | |
266 | template parameters and template template parameters | |
267 | are not allowed. | |
268 | ||
269 | The template parameters of the concept | |
270 | may involve placeholders. The following are | |
271 | considered. | |
272 | ||
273 | * Each template argument may be a cv and/or reference | |
274 | qualified placeholder type. | |
275 | * If a template argument is a function type, its | |
276 | arguments and return type may be cv/reference | |
277 | qualified placeholders. | |
278 | ||
279 | Any other placeholders are ignored. | |
280 | ||
281 | A concept is instantiated by constructing an | |
282 | __any from a raw value or by constructing a __binding. | |
283 | When a concept is instantiated with a specific | |
284 | set of type bindings, each placeholder is bound | |
285 | to a cv-unqualified non-reference type. After | |
286 | replacing each placeholder in the template argument | |
287 | list with the type that it binds to, the following | |
288 | must hold. | |
289 | ||
290 | * The number of arguments of apply in the | |
291 | bound concept must be the same as the number | |
292 | of arguments in the unbound concept. | |
293 | * The arguments and return type of apply in the | |
294 | bound concept can be derived from the corresponding | |
295 | arguments and the return type in the unbound concept | |
296 | as follows: If the argument in the unbound concept is a | |
297 | placeholder with optional cv and reference | |
298 | qualifiers, then the argument in the bound | |
299 | concept can be found by replacing the placeholder. | |
300 | Otherwise, the argument in the unbound concept | |
301 | must be the same as the argument in the bound concept. | |
302 | ||
303 | // Correct. | |
304 | template<class T = _self> | |
305 | struct foo1 { | |
306 | static void apply(const T& t) { t.foo(); } | |
307 | }; | |
308 | ||
309 | // Wrong. The signature of apply is different from the | |
310 | // primary template | |
311 | template<> | |
312 | struct foo1<int> { | |
313 | static void apply(int i); | |
314 | }; | |
315 | ||
316 | // Wrong. A concept must be a template | |
317 | struct foo2 { | |
318 | static void apply(const _self&); | |
319 | }; | |
320 | ||
321 | // Wrong. apply must be static | |
322 | template<class T = _self> | |
323 | struct foo3 { | |
324 | void apply(const T&); | |
325 | }; | |
326 | ||
327 | // Wrong. apply cannot be overloaded | |
328 | template<class T = _self> | |
329 | struct foo3 { | |
330 | static void apply(T&); | |
331 | static void apply(const T&); | |
332 | }; | |
333 | ||
334 | // Wrong. Only top level placeholders are detected | |
335 | template<class T> | |
336 | struct foo4; | |
337 | template<class T> | |
338 | struct foo4<boost::mpl::vector<T> > { | |
339 | static void apply(const T&); | |
340 | }; | |
341 | ||
342 | // Wrong. Template template parameters are not allowed. | |
343 | template<template<class> class T> | |
344 | struct foo5 | |
345 | { | |
346 | static void apply(T<int>&); | |
347 | }; | |
348 | ||
349 | [endsect] | |
350 | ||
351 | [section:predef Predefined Concepts] | |
352 | ||
353 | In the following tables, `T` and `U` are the types that the operation | |
354 | applies to, `R` is the result type. `T` always defaults | |
355 | to `_self` to match the default behavior of any. These | |
356 | concepts assume normal semantics. Thus, comparison | |
357 | operators always return bool, and references will be | |
358 | added to the arguments and results as appropriate. | |
359 | ||
360 | Except as otherwise noted, primitive concepts defined by | |
361 | the library can be specialized to provide concept maps. | |
362 | __copy_constructible, and the iterator concepts cannot | |
363 | be specialized because they are composites. __constructible, | |
364 | __destructible, __typeid_, and __same_type cannot be | |
365 | specialized because they require special handling in | |
366 | the library. | |
367 | ||
368 | [table:special Special Members | |
369 | [[concept][notes]] | |
370 | [[__constructible`<Sig>`][-]] | |
371 | [[__copy_constructible`<T>`][-]] | |
372 | [[__destructible`<T>`][-]] | |
373 | [[__assignable`<T, U = T>`][-]] | |
374 | [[__typeid_`<T>`][-]] | |
375 | ] | |
376 | [table:unary Unary Operators | |
377 | [[operator][concept][notes]] | |
378 | [[`operator++`][__incrementable`<T>`][There is no separate post-increment]] | |
379 | [[`operator--`][__decrementable`<T>`][There is no separate post-decrement]] | |
380 | [[`operator*`][__dereferenceable`<R, T>`][`R` should usually be a reference]] | |
381 | [[`operator~`][__complementable`<T, R = T>`][-]] | |
382 | [[`operator-`][__negatable`<T, R = T>`][-]] | |
383 | ] | |
384 | ||
385 | [table:binary Binary Operators | |
386 | [[operator][concept][notes]] | |
387 | [[`operator+`][__addable`<T, U = T, R = T>`][-]] | |
388 | [[`operator-`][__subtractable`<T, U = T, R = T>`][-]] | |
389 | [[`operator*`][__multipliable`<T, U = T, R = T>`][-]] | |
390 | [[`operator/`][__dividable`<T, U = T, R = T>`][-]] | |
391 | [[`operator%`][__modable`<T, U = T, R = T>`][-]] | |
392 | [[`operator&`][__bitandable`<T, U = T, R = T>`][-]] | |
393 | [[`operator|`][__bitorable`<T, U = T, R = T>`][-]] | |
394 | [[`operator^`][__bitxorable`<T, U = T, R = T>`][-]] | |
395 | [[`operator<<`][__left_shiftable`<T, U = T, R = T>`][-]] | |
396 | [[`operator>>`][__right_shiftable`<T, U = T, R = T>`][-]] | |
397 | [[`operator==` and `!=`][__equality_comparable`<T, U = T>`][`!=` is implemented in terms of `==`]] | |
398 | [[`operator<`, `>`, `<=`, and `>=`][__less_than_comparable`<T, U = T>`][All are implemented in terms of `<`]] | |
399 | [[`operator+=`][__add_assignable`<T, U = T>`][-]] | |
400 | [[`operator-=`][__subtract_assignable`<T, U = T>`][-]] | |
401 | [[`operator*=`][__multiply_assignable`<T, U = T>`][-]] | |
402 | [[`operator/=`][__divide_assignable`<T, U = T>`][-]] | |
403 | [[`operator%=`][__mod_assignable`<T, U = T>`][-]] | |
404 | [[`operator&=`][__bitand_assignable`<T, U = T>`][-]] | |
405 | [[`operator|=`][__bitor_assignable`<T, U = T>`][-]] | |
406 | [[`operator^=`][__bitxor_assignable`<T, U = T>`][-]] | |
407 | [[`operator<<=`][__left_shift_assignable`<T, U = T>`][-]] | |
408 | [[`operator>>=`][__right_shift_assignable`<T, U = T>`][-]] | |
409 | [[`operator<<`][__ostreamable`<Os = std::ostream, T = _self>`][-]] | |
410 | [[`operator>>`][__istreamable`<Is = std::istream, T = _self>`][-]] | |
411 | ] | |
412 | ||
413 | [table:misc Miscellaneous Operators | |
414 | [[operator][concept][notes]] | |
415 | [[`operator()`][__callable`<Sig, T>`][`Sig` should be a function type. T may be const qualified.]] | |
416 | [[`operator[]`][__subscriptable`<R, T, N = std::ptrdiff_t>`][`R` should usually be a reference. `T` can be optionally const qualified.]] | |
417 | ] | |
418 | ||
419 | [table:iterator Iterator Concepts | |
420 | [[concept][notes]] | |
421 | [[__iterator`<Traversal, T, Reference, Difference>`][Use __same_type to control the iterator's value type.]] | |
422 | [[__forward_iterator`<T, Reference, Difference>`][-]] | |
423 | [[__bidirectional_iterator`<T, Reference, Difference>`][-]] | |
424 | [[__random_access_iterator`<T, Reference, Difference>`][-]] | |
425 | ] | |
426 | ||
427 | [table:special Special Concepts | |
428 | [[concept][notes]] | |
429 | [[__same_type`<T>`][Indicates that two types are the same.]] | |
430 | ] | |
431 | ||
432 | [endsect] | |
433 | ||
434 | [xinclude reference.xml] | |
435 | ||
436 | [section:rationale Rationale] | |
437 | ||
438 | [section Why do I have to specify the presence of a destructor explicitly?] | |
439 | When using references the destructor isn't needed. | |
440 | By not assuming it implicitly, we allow capturing | |
441 | types with private or protected destructors by reference. | |
442 | For the sake of consistency, it must be specified | |
443 | when capturing by value as well. | |
444 | [endsect] | |
445 | ||
446 | [section Why non-member functions?] | |
447 | The members of __any can be customized. By using | |
448 | free functions, we guarantee that we don't interfere | |
449 | with anything that a user might want. | |
450 | [endsect] | |
451 | ||
452 | [section:placeholder Why are the placeholders called `_a`, `_b` and not `_1` `_2`] | |
453 | ||
454 | An earlier version of the library used the names `_1`, `_2`, etc. | |
455 | instead of `_a`, `_b`, etc. This caused a certain amount | |
456 | of confusion because the numbered placeholders are | |
457 | already used with a somewhat different meaning by several | |
458 | other libraries including Boost/Std Bind, Boost.Phoenix, | |
459 | and Boost.MPL. I eventually decided that since the | |
460 | placeholders represented named parameters instead of positional parameters, | |
461 | letters were more appropriate than numbers. | |
462 | ||
463 | [endsect] | |
464 | ||
465 | [section:ref Why not use `boost::ref` for references?] | |
466 | ||
467 | Boost.Function allows you to use `boost::ref` to store | |
468 | a reference to a function object. However, in the | |
469 | general case treating references and values in the | |
470 | same way causes inconsistent behavior that is difficult | |
471 | to reason about. If Boost.TypeErasure handled references | |
472 | like this, then, when you copy an __any, you would have | |
473 | no idea whether the new object is a real copy or | |
474 | just a new reference to the same underlying object. | |
475 | Boost.Function can get away with it, because it doesn't | |
476 | expose any mutating operations on the stored function object. | |
477 | ||
478 | Another method that has been proposed is only to | |
479 | keep a reference the first time. | |
480 | ||
481 | int i = 2; | |
482 | any x = ref(i); | |
483 | any y = x; // makes a copy | |
484 | ||
485 | Unfortunately, this doesn't handle all use cases, | |
486 | as there is no reliable way to return such a reference | |
487 | from a function. In addition it adds overhead whether | |
488 | it's needed or not, as we would have to add a flag | |
489 | to any to keep track of whether or not it is storing | |
490 | a reference. (The alternate method of storing this | |
491 | in the "`clone`" method in the vtable is impossibly complex | |
492 | to implement given the decoupled vtables that | |
493 | Boost.TypeErasure uses and it still adds overhead.). | |
494 | ||
495 | [endsect] | |
496 | ||
497 | [endsect] | |
498 | ||
499 | [section:future Future Work] | |
500 | ||
501 | These are just some ideas. There is absolutely no | |
502 | guarantee that any of them will ever be implemented. | |
503 | ||
504 | * Use SBO. | |
505 | * Allow more control over vtable layout. | |
506 | * Attempt to reuse sub-tables in conversions. | |
507 | * Allow "dynamic_cast". This requires creating | |
508 | a global registry of concept mappings. | |
509 | * Optimize the compile-time cost. | |
510 | ||
511 | [endsect] | |
512 | ||
513 | ||
514 | [section:acknowledgements Acknowledgements] | |
515 | ||
516 | The name `any` and an early ancestor of my placeholder | |
517 | system were taken from Alexander Nasonov's DynamicAny library. | |
518 | ||
519 | Thanks to review manager, Lorenzo Caminiti | |
520 | and all who participated in the formal review: | |
521 | ||
522 | * Christophe Henry | |
523 | * Paul Bristow | |
524 | * Karsten Ahnert | |
525 | * Pete Bartlett | |
526 | * Sebastian Redl | |
527 | * Hossein Haeri | |
528 | * Trigve Siver | |
529 | * Julien Nitard | |
530 | * Eric Niebler | |
531 | * Fabio Fracassi | |
532 | * Joel de Guzman | |
533 | * Alec Chapman | |
534 | * Larry Evans | |
535 | * Vincente J. Botet Escriba | |
536 | * Marcus Werle | |
537 | * Andrey Semashev | |
538 | * Dave Abrahams | |
539 | * Thomas Jordan | |
540 | ||
541 | [endsect] | |
542 | ||
543 | [section:related Related Work] | |
544 | ||
545 | There are a number of similar libraries in existence. I'm aware | |
546 | of at least three. | |
547 | ||
548 | * [@http://www.coderage.com/interfaces/ Boost.Interfaces] by Jonathan Turkanis | |
549 | * [@http://stlab.adobe.com/group__poly__related.html Adobe Poly] | |
550 | * [@http://cpp-experiment.sourceforge.net/boost/libs/dynamic_any/doc/ Boost.dynamic_any] by Alexander Nasonov | |
551 | ||
552 | [endsect] |