inheritance-based callable solution like
`shared_ptr<ICallable<void(int)>>`. )
-### Example: Defining a type-erasing function wrapper with `folly::Poly`
+### Examples: Defining a type-erasing function wrapper with `folly::Poly`
***
Defining a polymorphic wrapper with `Poly` is a matter of defining two
* An *interface*, consisting of public member functions, and
* A *mapping* from a concrete type to a set of member function bindings.
-Below is a (heavily commented) example of a simple implementation of a
+Below is a simple example program that defines a `drawable` wrapper for any type
+that provides a `draw` member function. (The details will be explained later.)
+
+``` Cpp
+ // This example is an adaptation of one found in Louis Dione's dyno library.
+ #include <folly/Poly.h>
+ #include <iostream>
+
+ struct IDrawable {
+ // Define the interface of something that can be drawn:
+ template <class Base> struct Interface : Base {
+ void draw(std::ostream& out) const { folly::poly_call<0>(*this, out);}
+ };
+ // Define how concrete types can fulfill that interface (in C++17):
+ template <class T> using Members = folly::PolyMembers<&T::draw>;
+ };
+
+ // Define an object that can hold anything that can be drawn:
+ using drawable = folly::Poly<IDrawable>;
+
+ struct Square {
+ void draw(std::ostream& out) const { out << "Square\n"; }
+ };
+
+ struct Circle {
+ void draw(std::ostream& out) const { out << "Circle\n"; }
+ };
+
+ void f(drawable const& d) {
+ d.draw(std::cout);
+ }
+
+ int main() {
+ f(Square{}); // prints Square
+ f(Circle{}); // prints Circle
+ }
+```
+
+The above program prints:
+
+```
+ Square
+ Circle
+```
+
+Here is another (heavily commented) example of a simple implementation of a
`std::function`-like polymorphic wrapper. Its interface has only a single
member function: `operator()`
};
template <class Value>
- using JavaIterator = Poly<IJavaIterator>;
+ using JavaIterator = Poly<IJavaIterator<Value>>;
```
Given the above definition, `JavaIterator<int>` can be used to hold instances
};
template <class Value>
- using JavaIterator = Poly<IJavaIterator>;
+ using JavaIterator = Poly<IJavaIterator<Value>>;
```
and
One typical advantage of inheritance-based solutions to runtime polymorphism
is that one polymorphic interface could extend another through inheritance.
The same can be accomplished with type-erasing polymorphic wrappers. In
-the `Poly` library, you can use `folly::Extends` to say that one interface
+the `Poly` library, you can use `folly::PolyExtends` to say that one interface
extends another.
``` Cpp
};
// The IFooBar interface extends the IFoo interface
- struct IFooBar : Extends<IFoo> {
+ struct IFooBar : PolyExtends<IFoo> {
template <class Base>
struct Interface : Base {
void Bar() const { return folly::poly_call<0>(*this); }
using FooBar = Poly<IFooBar>;
```
-Given the above defintion, instances of type `FooBar` have both `Foo()` and
+Given the above definition, instances of type `FooBar` have both `Foo()` and
`Bar()` member functions.
The sensible conversions exist between a wrapped derived type and a wrapped
-base type. For instance, assuming `IDerived` extends `IBase` with `Extends`:
+base type. For instance, assuming `IDerived` extends `IBase` with `PolyExtends`:
``` Cpp
Poly<IDerived> derived = ...;
};
template <class T>
using Members = folly::PolyMembers<
- +[](T const& t) -> decltype(!t) { return !t; }>;
+ +[](T const& t) -> decltype(bool(!t)) { return bool(!t); }>;
};
```
### Non-member functions (C++14)
***
-If you are using a C++14 compiler, the defintion of `ILogicallyNegatable`
+If you are using a C++14 compiler, the definition of `ILogicallyNegatable`
above will fail because lambdas are not `constexpr`. We can get the same
effect by writing the lambda as a named free function, as show below:
bool operator!() const { return folly::poly_call<0>(*this); }
};
template <class T>
- static auto negate(T const& t) -> decltype(!t) { return !t; }
+ static auto negate(T const& t)
+ -> decltype(bool(!t)) { return bool(!t); }
template <class T>
using Members = FOLLY_POLY_MEMBERS(&negate<T>);
};