7064767aadb0df6ef6e59c46c88a0a37a6f9eb56
[folly.git] / folly / docs / Poly.md
1 `folly/Poly.h`
2 -------------------------------
3
4 `Poly` is a class template that makes it relatively easy to define a
5 type-erasing polymorphic object wrapper.
6
7 ### Type-erasure
8 ***
9
10 `std::function` is one example of a type-erasing polymorphic object wrapper;
11 `folly::exception_wrapper` is another. Type-erasure is often used as an
12 alternative to dynamic polymorphism via inheritance-based virtual dispatch.
13 The distinguishing characteristic of type-erasing wrappers are:
14
15 * **Duck typing:** Types do not need to inherit from an abstract base
16     class in order to be assignable to a type-erasing wrapper; they merely
17     need to satisfy a particular interface.
18 * **Value semantics:** Type-erasing wrappers are objects that can be
19     passed around _by value_. This is in contrast to abstract base classes
20     which must be passed by reference or by pointer or else suffer from
21     _slicing_, which causes them to lose their polymorphic behaviors.
22     Reference semantics make it difficult to reason locally about code.
23 * **Automatic memory management:** When dealing with inheritance-based
24     dynamic polymorphism, it is often necessary to allocate and manage
25     objects on the heap. This leads to a proliferation of `shared_ptr`s and
26     `unique_ptr`s in APIs, complicating their point-of-use. APIs that take
27     type-erasing wrappers, on the other hand, can often store small objects
28     in-situ, with no dynamic allocation. The memory management, if any, is
29     handled for you, and leads to cleaner APIs: consumers of your API don't
30     need to pass `shared_ptr<AbstractBase>`; they can simply pass any object
31     that satisfies the interface you require. (`std::function` is a
32     particularly compelling example of this benefit. Far worse would be an
33     inheritance-based callable solution like
34     `shared_ptr<ICallable<void(int)>>`. )
35
36 ### Example: Defining a type-erasing function wrapper with `folly::Poly`
37 ***
38
39 Defining a polymorphic wrapper with `Poly` is a matter of defining two
40 things:
41
42 * An *interface*, consisting of public member functions, and
43 * A *mapping* from a concrete type to a set of member function bindings.
44
45 Below is a (heavily commented) example of a simple implementation of a
46 `std::function`-like polymorphic wrapper. Its interface has only a single
47 member function: `operator()`
48
49 ``` Cpp
50     // An interface for a callable object of a particular signature, Fun
51     // (most interfaces don't need to be templates, FWIW).
52     template <class Fun>
53     struct IFunction;
54
55     template <class R, class... As>
56     struct IFunction<R(As...)> {
57       // An interface is defined as a nested class template called
58       // Interface that takes a single template parameter, Base, from
59       // which it inherits.
60       template <class Base>
61       struct Interface : Base {
62         // The Interface has public member functions. These become the
63         // public interface of the resulting Poly instantiation.
64         // (Implementation note: Poly<IFunction<Sig>> will publicly
65         // inherit from this struct, which is what gives it the right
66         // member functions.)
67         R operator()(As... as) const {
68           // The definition of each member function in your interface will
69           // always consist of a single line dispatching to folly::poly_call<N>.
70           // The "N" corresponds to the N-th member function in the
71           // list of member function bindings, Members, defined below.
72           // The first argument will always be *this, and the rest of the
73           // arguments should simply forward (if necessary) the member
74           // function's arguments.
75           return static_cast<R>(
76               folly::poly_call<0>(*this, std::forward<As>(as)...));
77         }
78       };
79       // The "Members" alias template is a comma-separated list of bound
80       // member functions for a given concrete type "T". The
81       // "FOLLY_POLY_MEMBERS" macro accepts a comma-separated list, and the
82       // (optional) "FOLLY_POLY_MEMBER" macro lets you disambiguate overloads
83       // by explicitly specifying the function signature the target member
84       // function should have. In this case, we require "T" to have a
85       // function call operator with the signature `R(As...) const`.
86       //
87       // If you are using a C++17-compatible compiler, you can do away with
88       // the macros and write this as:
89       //
90       //   template <class T>
91       //   using Members =
92       //       folly::PolyMembers<folly::sig<R(As...) const>(&T::operator())>;
93       //
94       // And since `folly::sig` is only needed for disambiguation in case of
95       // overloads, if you are not concerned about objects with overloaded
96       // function call operators, it could be further simplified to:
97       //
98       //   template <class T>
99       //   using Members = folly::PolyMembers<&T::operator()>;
100       //
101       template <class T>
102       using Members = FOLLY_POLY_MEMBERS(
103           FOLLY_POLY_MEMBER(R(As...) const, &T::operator()));
104     };
105
106     // Now that we have defined the interface, we can pass it to Poly to
107     // create our type-erasing wrapper:
108     template <class Fun>
109     using Function = Poly<IFunction<Fun>>;
110 ```
111
112 Given the above definition of `Function`, users can now initialize instances
113 of (say) `Function<int(int, int)>` with function objects like
114 `std::plus<int>` and `std::multiplies<int>`, as below:
115
116 ``` Cpp
117     Function<int(int, int)> fun = std::plus<int>{};
118     assert(5 == fun(2, 3));
119     fun = std::multiplies<int>{};
120     assert(6 = fun(2, 3));
121 ```
122
123 ### Defining an interface with C++17
124 ***
125
126 With C++17, defining an interface to be used with `Poly` is fairly
127 straightforward. As in the `Function` example above, there is a struct with
128 a nested `Interface` class template and a nested `Members` alias template.
129 No macros are needed with C++17.
130
131 Imagine we were defining something like a Java-style iterator. If we are
132 using a C++17 compiler, our interface would look something like this:
133
134 ``` Cpp
135     template <class Value>
136     struct IJavaIterator {
137       template <class Base>
138       struct Interface : Base {
139         bool Done() const { return folly::poly_call<0>(*this); }
140         Value Current() const { return folly::poly_call<1>(*this); }
141         void Next() { folly::poly_call<2>(*this); }
142       };
143       // NOTE: This works in C++17 only:
144       template <class T>
145       using Members = folly::PolyMembers<&T::Done, &T::Current, &T::Next>;
146     };
147
148     template <class Value>
149     using JavaIterator = Poly<IJavaIterator>;
150 ```
151
152 Given the above definition, `JavaIterator<int>` can be used to hold instances
153 of any type that has `Done`, `Current`, and `Next` member functions with the
154 correct (or compatible) signatures.
155
156 The presence of overloaded member functions complicates this picture. Often,
157 property members are faked in C++ with `const` and non-`const` member
158 function overloads, like in the interface specified below:
159
160 ``` Cpp
161     struct IIntProperty {
162       template <class Base>
163       struct Interface : Base {
164         int Value() const { return folly::poly_call<0>(*this); }
165         void Value(int i) { folly::poly_call<1>(*this, i); }
166       };
167       // NOTE: This works in C++17 only:
168       template <class T>
169       using Members = folly::PolyMembers<
170         folly::sig<int() const>(&T::Value),
171         folly::sig<void(int)>(&T::Value)>;
172     };
173
174     using IntProperty = Poly<IIntProperty>;
175 ```
176
177 Now, any object that has `Value` members of compatible signatures can be
178 assigned to instances of `IntProperty` object. Note how `folly::sig` is used
179 to disambiguate the overloads of `&T::Value`.
180
181 ### Defining an interface with C++14
182 ***
183
184 In C++14, the nice syntax above doesn't work, so we have to resort to macros.
185 The two examples above would look like this:
186
187 ``` Cpp
188     template <class Value>
189     struct IJavaIterator {
190       template <class Base>
191       struct Interface : Base {
192         bool Done() const { return folly::poly_call<0>(*this); }
193         Value Current() const { return folly::poly_call<1>(*this); }
194         void Next() { folly::poly_call<2>(*this); }
195       };
196       // NOTE: This works in C++14 and C++17:
197       template <class T>
198       using Members = FOLLY_POLY_MEMBERS(&T::Done, &T::Current, &T::Next);
199     };
200
201     template <class Value>
202     using JavaIterator = Poly<IJavaIterator>;
203 ```
204
205 and
206
207 ``` Cpp
208     struct IIntProperty {
209       template <class Base>
210       struct Interface : Base {
211         int Value() const { return folly::poly_call<0>(*this); }
212         void Value(int i) { return folly::poly_call<1>(*this, i); }
213       };
214       // NOTE: This works in C++14 and C++17:
215       template <class T>
216       using Members = FOLLY_POLY_MEMBERS(
217         FOLLY_POLY_MEMBER(int() const, &T::Value),
218         FOLLY_POLY_MEMBER(void(int), &T::Value));
219     };
220
221     using IntProperty = Poly<IIntProperty>;
222 ```
223
224 ### Extending interfaces
225 ***
226
227 One typical advantage of inheritance-based solutions to runtime polymorphism
228 is that one polymorphic interface could extend another through inheritance.
229 The same can be accomplished with type-erasing polymorphic wrappers. In
230 the `Poly` library, you can use `folly::Extends` to say that one interface
231 extends another.
232
233 ``` Cpp
234     struct IFoo {
235       template <class Base>
236       struct Interface : Base {
237         void Foo() const { return folly::poly_call<0>(*this); }
238       };
239       template <class T>
240       using Members = FOLLY_POLY_MEMBERS(&T::Foo);
241     };
242
243     // The IFooBar interface extends the IFoo interface
244     struct IFooBar : Extends<IFoo> {
245       template <class Base>
246       struct Interface : Base {
247         void Bar() const { return folly::poly_call<0>(*this); }
248       };
249       template <class T>
250       using Members = FOLLY_POLY_MEMBERS(&T::Bar);
251     };
252
253     using FooBar = Poly<IFooBar>;
254 ```
255
256 Given the above defintion, instances of type `FooBar` have both `Foo()` and
257 `Bar()` member functions.
258
259 The sensible conversions exist between a wrapped derived type and a wrapped
260 base type. For instance, assuming `IDerived` extends `IBase` with `Extends`:
261
262 ``` Cpp
263     Poly<IDerived> derived = ...;
264     Poly<IBase> base = derived; // This conversion is OK.
265 ```
266
267 As you would expect, there is no conversion in the other direction, and at
268 present there is no `Poly` equivalent to `dynamic_cast`.
269
270 ### Type-erasing polymorphic reference wrappers
271 ***
272
273 Sometimes you don't need to own a copy of an object; a reference will do. For
274 that you can use `Poly` to capture a _reference_ to an object satisfying an
275 interface rather than the whole object itself. The syntax is intuitive.
276
277 ``` Cpp
278     int i = 42;
279
280     // Capture a mutable reference to an object of any IRegular type:
281     Poly<IRegular &> intRef = i;
282
283     assert(42 == folly::poly_cast<int>(intRef));
284     // Assert that we captured the address of "i":
285     assert(&i == &folly::poly_cast<int>(intRef));
286 ```
287
288 A reference-like `Poly` has a different interface than a value-like `Poly`.
289 Rather than calling member functions with the `obj.fun()` syntax, you would
290 use the `obj->fun()` syntax. This is for the sake of `const`-correctness.
291 For example, consider the code below:
292
293 ``` Cpp
294     struct IFoo {
295       template <class Base>
296       struct Interface {
297         void Foo() { folly::poly_call<0>(*this); }
298       };
299       template <class T>
300       using Members = folly::PolyMembers<&T::Foo>;
301     };
302
303     struct SomeFoo {
304       void Foo() { std::printf("SomeFoo::Foo\n"); }
305     };
306
307     SomeFoo foo;
308     Poly<IFoo &> const anyFoo = foo;
309     anyFoo->Foo(); // prints "SomeFoo::Foo"
310 ```
311
312 Notice in the above code that the `Foo` member function is non-`const`.
313 Notice also that the `anyFoo` object is `const`. However, since it has
314 captured a non-`const` reference to the `foo` object, it should still be
315 possible to dispatch to the non-`const` `Foo` member function. When
316 instantiated with a reference type, `Poly` has an overloaded `operator->`
317 member that returns a pointer to the `IFoo` interface with the correct
318 `const`-ness, which makes this work.
319
320 The same mechanism also prevents users from calling non-`const` member
321 functions on `Poly` objects that have captured `const` references, which
322 would violate `const`-correctness.
323
324 Sensible conversions exist between non-reference and reference `Poly`s. For
325 instance:
326
327 ``` Cpp
328     Poly<IRegular> value = 42;
329     Poly<IRegular &> mutable_ref = value;
330     Poly<IRegular const &> const_ref = mutable_ref;
331
332     assert(&poly_cast<int>(value) == &poly_cast<int>(mutable_ref));
333     assert(&poly_cast<int>(value) == &poly_cast<int>(const_ref));
334 ```
335
336 ### Non-member functions (C++17)
337 ***
338
339 If you wanted to write the interface `ILogicallyNegatable`, which captures
340 all types that can be negated with unary `operator!`, you could do it
341 as we've shown above, by binding `&T::operator!` in the nested `Members`
342 alias template, but that has the problem that it won't work for types that
343 have defined unary `operator!` as a free function. To handle this case,
344 the `Poly` library lets you use a free function instead of a member function
345 when creating a binding.
346
347 With C++17 you may use a lambda to create a binding, as shown in the example
348 below:
349
350 ``` Cpp
351     struct ILogicallyNegatable {
352       template <class Base>
353       struct Interface : Base {
354         bool operator!() const { return folly::poly_call<0>(*this); }
355       };
356       template <class T>
357       using Members = folly::PolyMembers<
358         +[](T const& t) -> decltype(!t) { return !t; }>;
359     };
360 ```
361
362 This requires some explanation. The unary `operator+` in front of the lambda
363 is necessary! It causes the lambda to decay to a C-style function pointer,
364 which is one of the types that `folly::PolyMembers` accepts. The `decltype` in
365 the lambda return type is also necessary. Through the magic of SFINAE, it
366 will cause `Poly<ILogicallyNegatable>` to reject any types that don't support
367 unary `operator!`.
368
369 If you are using a free function to create a binding, the first parameter is
370 implicitly the `this` parameter. It will receive the type-erased object.
371
372 ### Non-member functions (C++14)
373 ***
374
375 If you are using a C++14 compiler, the defintion of `ILogicallyNegatable`
376 above will fail because lambdas are not `constexpr`. We can get the same
377 effect by writing the lambda as a named free function, as show below:
378
379 ``` Cpp
380     struct ILogicallyNegatable {
381       template <class Base>
382       struct Interface : Base {
383         bool operator!() const { return folly::poly_call<0>(*this); }
384       };
385       template <class T>
386       static auto negate(T const& t) -> decltype(!t) { return !t; }
387       template <class T>
388       using Members = FOLLY_POLY_MEMBERS(&negate<T>);
389     };
390 ```
391
392 As with the example that uses the lambda in the preceding section, the first
393 parameter is implicitly the `this` parameter. It will receive the type-erased
394 object.
395
396 ### Multi-dispatch
397 ***
398
399 What if you want to create an `IAddable` interface for things that can be
400 added? Adding requires _two_ objects, both of which are type-erased. This
401 interface requires dispatching on both objects, doing the addition only
402 if the types are the same. For this we make use of the `PolySelf` template
403 alias to define an interface that takes more than one object of the the
404 erased type.
405
406 ``` Cpp
407     struct IAddable {
408       template <class Base>
409       struct Interface : Base {
410         friend PolySelf<Base>
411         operator+(PolySelf<Base> const& a, PolySelf<Base> const& b) const {
412           return folly::poly_call<0>(a, b);
413         }
414       };
415       template <class T>
416       using Members = folly::PolyMembers<
417         +[](T const& a, T const& b) -> decltype(a + b) { return a + b; }>;
418     };
419 ```
420
421 Given the above definition of `IAddable` we would be able to do the following:
422
423 ``` Cpp
424     Poly<IAddable> a = 2, b = 3;
425     Poly<IAddable> c = a + b;
426     assert(poly_cast<int>(c) == 5);
427 ```
428
429 If `a` and `b` stored objects of different types, a `BadPolyCast` exception
430 would be thrown.
431
432 ### Move-only types
433 ***
434
435 If you want to store move-only types, then your interface should extend the
436 `poly::IMoveOnly` interface.
437
438 ### Implementation notes
439 ***
440
441 `Poly` will store "small" objects in an internal buffer, avoiding the cost of
442 of dynamic allocations. At present, this size is not configurable; it is
443 pegged at the size of two `double`s.
444
445 `Poly` objects are always nothrow movable. If you store an object in one that
446 has a potentially throwing move constructor, the object will be stored on the
447 heap, even if it could fit in the internal storage of the `Poly` object.
448 (So be sure to give your objects nothrow move constructors!)
449
450 `Poly` implements type-erasure in a manner very similar to how the compiler
451 accomplishes virtual dispatch. Every `Poly` object contains a pointer to a
452 table of function pointers. Member function calls involve a double-
453 indirection: once through the v-pointer, and other indirect function call
454 through the function pointer.