From: Eric Niebler Date: Mon, 30 Oct 2017 16:26:13 +0000 (-0700) Subject: Folly.Poly: a library for creating type-erasing polymorphic wrappers X-Git-Tag: v2017.11.06.00~37 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=18882128de06bfc10b7137877f70f63d957db313;p=folly.git Folly.Poly: a library for creating type-erasing polymorphic wrappers Summary: `Poly` is a class template that makes it relatively easy to define a type-erasing polymorphic object wrapper. == Type-erasure `std::function` is one example of a type-erasing polymorphic object wrapper; `folly::exception_wrapper` is another. Type-erasure is often used as an alternative to dynamic polymorphism via inheritance-based virtual dispatch. The distinguishing characteristic of type-erasing wrappers are: * **Duck typing:** Types do not need to inherit from an abstract base class in order to be assignable to a type-erasing wrapper; they merely need to satisfy a particular interface. * **Value semantics:** Type-erasing wrappers are objects that can be passed around _by value_. This is in contrast to abstract base classes which must be passed by reference or by pointer or else suffer from _slicing_, which causes them to lose their polymorphic behaviors. Reference semantics make it difficult to reason locally about code. * **Automatic memory management:** When dealing with inheritance-based dynamic polymorphism, it is often necessary to allocate and manage objects on the heap. This leads to a proliferation of `shared_ptr`s and `unique_ptr`s in APIs, complicating their point-of-use. APIs that take type-erasing wrappers, on the other hand, can often store small objects in-situ, with no dynamic allocation. The memory management, if any, is handled for you, and leads to cleaner APIs: consumers of your API don't need to pass `shared_ptr`; they can simply pass any object that satisfies the interface you require. (`std::function` is a particularly compelling example of this benefit. Far worse would be an inheritance-based callable solution like `shared_ptr>`. ) == Example: Defining a type-erasing function wrapper with `folly::Poly` Defining a polymorphic wrapper with `Poly` is a matter of defining two things: * 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 `std::function`-like polymorphic wrapper. Its interface has only a single member function: `operator()` lang=c++ // An interface for a callable object of a particular signature, Fun // (most interfaces don't need to be templates, FWIW). template struct IFunction; template struct IFunction { // An interface is defined as a nested class template called // Interface that takes a single template parameter, Base, from // which it inherits. template struct Interface : Base { // The Interface has public member functions. These become the // public interface of the resulting Poly instantiation. // (Implementation note: Poly> will publicly // inherit from this struct, which is what gives it the right // member functions.) R operator()(As... as) const { // The definition of each member function in your interface will // always consist of a single line dispatching to folly::call. // The "N" corresponds to the N-th member function in the // list of member function bindings, Members, defined below. // The first argument will always be *this, and the rest of the // arguments should simply forward (if necessary) the member // function's arguments. return static_cast( folly::poly_call<0>(*this, std::forward(as)...)); } }; // The "Members" alias template is a comma-separated list of bound // member functions for a given concrete type "T". The // "FOLLY_POLY_MEMBERS" macro accepts a comma-separated list, and the // (optional) "FOLLY_POLY_MEMBER" macro lets you disambiguate overloads // by explicitly specifying the function signature the target member // function should have. In this case, we require "T" to have a // function call operator with the signature `R(As...) const`. // // If you are using a C++17-compatible compiler, you can do away with // the macros and write this as: // // template // using Members = // folly::PolyMembers(&T::operator())>; // // And since `folly::sig` is only needed for disambiguation in case of // overloads, if you are not concerned about objects with overloaded // function call operators, it could be further simplified to: // // template using Members = folly::PolyMembers<&T::operator()>; // template using Members = FOLLY_POLY_MEMBERS( FOLLY_POLY_MEMBER(R(As...) const, &T::operator())); }; // Now that we have defined the interface, we can pass it to Poly to // create our type-erasing wrapper: template using Function = Poly>; Given the above definition of `Function`, users can now initialize instances of (say) `Function` with function objects like `std::plus` and `std::multiplies`, as below: lang=c++ Function fun = std::plus{}; assert(5 == fun(2, 3)); fun = std::multiplies{}; assert(6 = fun(2, 3)); == Defining an interface with C++17 With C++17, defining an interface to be used with `Poly` is fairly straightforward. As in the `Function` example above, there is a struct with a nested `Interface` class template and a nested `Members` alias template. No macros are needed with C++17. Imagine we were defining something like a Java-style iterator. If we are using a C++17 compiler, our interface would look something like this: lang=c++ template struct IJavaIterator { template struct Interface : Base { bool Done() const { return folly::poly_call<0>(*this); } Value Current() const { return folly::poly_call<1>(*this); } void Next() { folly::poly_call<2>(*this); } }; // NOTE: This works in C++17 only: template using Members = folly::PolyMembers<&T::Done, &T::Current, &T::Next>; }; template using JavaIterator = Poly; Given the above definition, `JavaIterator` can be used to hold instances of any type that has `Done`, `Current`, and `Next` member functions with the correct (or compatible) signatures. The presence of overloaded member functions complicates this picture. Often, property members are faked in C++ with `const` and non-`const` member function overloads, like in the interface specified below: lang=c++ struct IIntProperty { template struct Interface : Base { int Value() const { return folly::poly_call<0>(*this); } void Value(int i) { folly::poly_call<1>(*this, i); } }; // NOTE: This works in C++17 only: template using Members = folly::PolyMembers< folly::sig(&T::Value), folly::sig(&T::Value)>; }; using IntProperty = Poly; Now, any object that has `Value` members of compatible signatures can be assigned to instances of `IntProperty` object. Note how `folly::sig` is used to disambiguate the overloads of `&T::Value`. == Defining an interface with C++14 In C++14, the nice syntax above doesn't work, so we have to resort to macros. The two examples above would look like this: lang=c++ template struct IJavaIterator { template struct Interface : Base { bool Done() const { return folly::poly_call<0>(*this); } Value Current() const { return folly::poly_call<1>(*this); } void Next() { folly::poly_call<2>(*this); } }; // NOTE: This works in C++14 and C++17: template using Members = FOLLY_POLY_MEMBERS(&T::Done, &T::Current, &T::Next); }; template using JavaIterator = Poly; and lang=c++ struct IIntProperty { template struct Interface : Base { int Value() const { return folly::poly_call<0>(*this); } void Value(int i) { return folly::poly_call<1>(*this, i); } }; // NOTE: This works in C++14 and C++17: template using Members = FOLLY_POLY_MEMBERS( FOLLY_POLY_MEMBER(int() const, &T::Value), FOLLY_POLY_MEMBER(void(int), &T::Value)); }; using IntProperty = Poly; == Extending interfaces 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::PolyExtends` to say that one interface extends another. lang=c++ struct IFoo { template struct Interface : Base { void Foo() const { return folly::poly_call<0>(*this); } }; template using Members = FOLLY_POLY_MEMBERS(&T::Foo); }; // The IFooBar interface extends the IFoo interface struct IFooBar : PolyExtends { template struct Interface : Base { void Bar() const { return folly::poly_call<0>(*this); } }; template using Members = FOLLY_POLY_MEMBERS(&T::Bar); }; using FooBar = Poly; 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`: lang=c++ Poly derived = ...; Poly base = derived; // This conversion is OK. As you would expect, there is no conversion in the other direction, and at present there is no `Poly` equivalent to `dynamic_cast`. == Type-erasing polymorphic reference wrappers Sometimes you don't need to own a copy of an object; a reference will do. For that you can use `Poly` to capture a //reference// to an object satisfying an interface rather than the whole object itself. The syntax is intuitive. lang=c++ int i = 42; // Capture a mutable reference to an object of any IRegular type: Poly intRef = i; assert(42 == folly::poly_cast(intRef)); // Assert that we captured the address of "i": assert(&i == &folly::poly_cast(intRef)); A reference-like `Poly` has a different interface than a value-like `Poly`. Rather than calling member functions with the `obj.fun()` syntax, you would use the `obj->fun()` syntax. This is for the sake of `const`-correctness. For example, consider the code below: lang=c++ struct IFoo { template struct Interface { void Foo() { folly::poly_call<0>(*this); } }; template using Members = folly::PolyMembers<&T::Foo>; }; struct SomeFoo { void Foo() { std::printf("SomeFoo::Foo\n"); } }; SomeFoo foo; Poly const anyFoo = foo; anyFoo->Foo(); // prints "SomeFoo::Foo" Notice in the above code that the `Foo` member function is non-`const`. Notice also that the `anyFoo` object is `const`. However, since it has captured a non-`const` reference to the `foo` object, it should still be possible to dispatch to the non-`const` `Foo` member function. When instantiated with a reference type, `Poly` has an overloaded `operator->` member that returns a pointer to the `IFoo` interface with the correct `const`-ness, which makes this work. The same mechanism also prevents users from calling non-`const` member functions on `Poly` objects that have captured `const` references, which would violate `const`-correctness. Sensible conversions exist between non-reference and reference `Poly`s. For instance: lang=c++ Poly value = 42; Poly mutable_ref = value; Poly const_ref = mutable_ref; assert(&poly_cast(value) == &poly_cast(mutable_ref)); assert(&poly_cast(value) == &poly_cast(const_ref)); == Non-member functions (C++17) If you wanted to write the interface `ILogicallyNegatable`, which captures all types that can be negated with unary `operator!`, you could do it as we've shown above, by binding `&T::operator!` in the nested `Members` alias template, but that has the problem that it won't work for types that have defined unary `operator!` as a free function. To handle this case, the `Poly` library lets you use a free function instead of a member function when creating a binding. With C++17 you may use a lambda to create a binding, as shown in the example below: lang=c++ struct ILogicallyNegatable { template struct Interface : Base { bool operator!() const { return folly::poly_call<0>(*this); } }; template using Members = folly::PolyMembers< +[](T const& t) -> decltype(!t) { return !t; }>; }; This requires some explanation. The unary `operator+` in front of the lambda is necessary! It causes the lambda to decay to a C-style function pointer, which is one of the types that `folly::PolyMembers` accepts. The `decltype` in the lambda return type is also necessary. Through the magic of SFINAE, it will cause `Poly` to reject any types that don't support unary `operator!`. If you are using a free function to create a binding, the first parameter is implicitly the `this` parameter. It will receive the type-erased object. == Non-member functions (C++14) 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: lang=c++ struct ILogicallyNegatable { template struct Interface : Base { bool operator!() const { return folly::poly_call<0>(*this); } }; template static auto negate(T const& t) -> decltype(!t) { return !t; } template using Members = FOLLY_POLY_MEMBERS(&negate); }; As with the example that uses the lambda in the preceding section, the first parameter is implicitly the `this` parameter. It will receive the type-erased object. == Multi-dispatch What if you want to create an `IAddable` interface for things that can be added? Adding requires //two// objects, both of which are type-erased. This interface requires dispatching on both objects, doing the addition only if the types are the same. For this we make use of the `Self` template alias to define an interface that takes more than one object of the the erased type. lang=c++ struct IAddable { template struct Interface : Base { friend Self operator+(Self const& a, Self const& b) const { return folly::poly_call<0>(a, b); } }; template using Members = folly::PolyMembers< +[](T const& a, T const& b) -> decltype(a + b) { return a + b; }>; }; Given the above defintion of `IAddable` we would be able to do the following: lang=c++ Poly a = 2, b = 3; Poly c = a + b; assert(poly_cast(c) == 5); If `a` and `b` stored objects of different types, a `BadPolyCast` exception would be thrown. == Move-only types If you want to store move-only types, then your interface should extend the `IMoveOnly` interface. == Implementation notes `Poly` will store "small" objects in an internal buffer, avoiding the cost of of dynamic allocations. At present, this size is not configurable; it is pegged at the size of two `double`s. `Poly` objects are always nothrow movable. If you store an object in one that has a potentially throwing move contructor, the object will be stored on the heap, even if it could fit in the internal storage of the `Poly` object. (So be sure to give your objects nothrow move constructors!) `Poly` implements type-erasure in a manner very similar to how the compiler accomplishes virtual dispatch. Every `Poly` object contains a pointer to a table of function pointers. Member function calls involve a double- indirection: once through the v-pointer, and other indirect function call through the function pointer. Reviewed By: yfeldblum Differential Revision: D4897112 fbshipit-source-id: ff1c1156316bfbdd8f2205c4f57932c0067cacac --- diff --git a/folly/Makefile.am b/folly/Makefile.am index 337b1cd6..d535a3b6 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -77,6 +77,7 @@ nobase_follyinclude_HEADERS = \ detail/IPAddressSource.h \ detail/MemoryIdler.h \ detail/MPMCPipelineDetail.h \ + detail/PolyDetail.h \ detail/RangeCommon.h \ detail/RangeSse42.h \ detail/Sleeper.h \ @@ -84,6 +85,7 @@ nobase_follyinclude_HEADERS = \ detail/SocketFastOpen.h \ detail/StaticSingletonManager.h \ detail/ThreadLocalDetail.h \ + detail/TypeList.h \ detail/TurnSequencer.h \ detail/UncaughtExceptionCounter.h \ executors/Async.h \ @@ -341,6 +343,10 @@ nobase_follyinclude_HEADERS = \ PackedSyncPtr.h \ Padded.h \ PicoSpinLock.h \ + Poly.h \ + Poly-inl.h \ + poly/Nullable.h \ + poly/Regular.h \ Portability.h \ portability/Asm.h \ portability/Atomic.h \ @@ -565,6 +571,7 @@ libfolly_la_SOURCES = \ detail/SocketFastOpen.cpp \ MacAddress.cpp \ memory/ThreadCachedArena.cpp \ + Poly.cpp \ portability/Dirent.cpp \ portability/Fcntl.cpp \ portability/Libgen.cpp \ diff --git a/folly/Poly-inl.h b/folly/Poly-inl.h new file mode 100644 index 00000000..feb846b2 --- /dev/null +++ b/folly/Poly-inl.h @@ -0,0 +1,226 @@ +/* + * Copyright 2017-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace folly { +namespace detail { + +template +inline PolyVal::PolyVal(PolyVal&& that) noexcept { + that.vptr_->ops_(Op::eMove, &that, static_cast(this)); + vptr_ = std::exchange(that.vptr_, vtable()); +} + +template +inline PolyVal::PolyVal(PolyOrNonesuch const& that) { + that.vptr_->ops_( + Op::eCopy, const_cast(that._data_()), PolyAccess::data(*this)); + vptr_ = that.vptr_; +} + +template +inline PolyVal::~PolyVal() { + vptr_->ops_(Op::eNuke, this, nullptr); +} + +template +inline Poly& PolyVal::operator=(PolyVal that) noexcept { + vptr_->ops_(Op::eNuke, _data_(), nullptr); + that.vptr_->ops_(Op::eMove, that._data_(), _data_()); + vptr_ = std::exchange(that.vptr_, vtable()); + return static_cast&>(*this); +} + +template +template ::value, int>> +inline PolyVal::PolyVal(T&& t) { + using U = std::decay_t; + static_assert( + std::is_copy_constructible::value || !Copyable::value, + "This Poly<> requires copyability, and the source object is not " + "copyable"); + // The static and dynamic types should match; otherwise, this will slice. + assert(typeid(t) == typeid(_t>) || + !"Dynamic and static exception types don't match. Object would " + "be sliced when storing in Poly."); + if (inSitu()) { + ::new (static_cast(&_data_()->buff_)) U(static_cast(t)); + } else { + _data_()->pobj_ = new U(static_cast(t)); + } + vptr_ = vtableFor(); +} + +template +template ::value, int>> +inline PolyVal::PolyVal(Poly that) { + static_assert( + !Copyable::value || std::is_copy_constructible>::value, + "This Poly<> requires copyability, and the source object is not " + "copyable"); + auto* that_vptr = PolyAccess::vtable(that); + if (that_vptr->state_ != State::eEmpty) { + that_vptr->ops_(Op::eMove, PolyAccess::data(that), _data_()); + vptr_ = &select(*std::exchange(that_vptr, vtable>())); + } +} + +template +template ::value, int>> +inline Poly& PolyVal::operator=(T&& t) { + *this = PolyVal(static_cast(t)); + return static_cast&>(*this); +} + +template +template ::value, int>> +inline Poly& PolyVal::operator=(Poly that) { + *this = PolyVal(std::move(that)); + return static_cast&>(*this); +} + +template +inline void PolyVal::swap(Poly& that) noexcept { + switch (vptr_->state_) { + case State::eEmpty: + *this = std::move(that); + break; + case State::eOnHeap: + if (State::eOnHeap == that.vptr_->state_) { + std::swap(_data_()->pobj_, _data_()->pobj_); + std::swap(vptr_, that.vptr_); + } + FOLLY_FALLTHROUGH; + case State::eInSitu: + std::swap(*this, that); // NOTE: qualified, not ADL + } +} + +template +inline AddCvrefOf, I>& PolyRef::_polyRoot_() const noexcept { + return const_cast, I>&>( + static_cast const&>(*this)); +} + +template +constexpr RefType PolyRef::refType() noexcept { + using J = std::remove_reference_t; + return std::is_rvalue_reference::value + ? RefType::eRvalue + : std::is_const::value ? RefType::eConstLvalue : RefType::eLvalue; +} + +template +template +inline PolyRef::PolyRef(That&& that, Type) { + auto* that_vptr = PolyAccess::vtable(PolyAccess::root(that)); + detail::State const that_state = that_vptr->state_; + if (that_state == State::eEmpty) { + throw BadPolyAccess(); + } + auto* that_data = PolyAccess::data(PolyAccess::root(that)); + _data_()->pobj_ = that_state == State::eInSitu + ? const_cast(static_cast(&that_data->buff_)) + : that_data->pobj_; + this->vptr_ = &select>( + *static_cast> const*>(that_vptr->ops_( + Op::eRefr, nullptr, reinterpret_cast(refType())))); +} + +template +inline PolyRef::PolyRef(PolyRef const& that) noexcept { + _data_()->pobj_ = that._data_()->pobj_; + this->vptr_ = that.vptr_; +} + +template +inline Poly& PolyRef::operator=(PolyRef const& that) noexcept { + _data_()->pobj_ = that._data_()->pobj_; + this->vptr_ = that.vptr_; + return static_cast&>(*this); +} + +template +template ::value, int>> +inline PolyRef::PolyRef(T&& t) noexcept { + _data_()->pobj_ = + const_cast(static_cast(std::addressof(t))); + this->vptr_ = vtableFor, AddCvrefOf, I>>(); +} + +template +template < + class I2, + std::enable_if_t::value, int>> +inline PolyRef::PolyRef(Poly&& that) noexcept( + std::is_reference::value) + : PolyRef{that, Type{}} { + static_assert( + Disjunction, std::is_rvalue_reference>::value, + "Attempting to construct a Poly that is a reference to a temporary. " + "This is probably a mistake."); +} + +template +template ::value, int>> +inline Poly& PolyRef::operator=(T&& t) noexcept { + *this = PolyRef(static_cast(t)); + return static_cast&>(*this); +} + +template +template < + class I2, + std::enable_if_t::value, int>> +inline Poly& PolyRef::operator=(Poly&& that) noexcept( + std::is_reference::value) { + *this = PolyRef(std::move(that)); + return static_cast&>(*this); +} + +template +template < + class I2, + std::enable_if_t::value, int>> +inline Poly& PolyRef::operator=(Poly& that) noexcept( + std::is_reference::value) { + *this = PolyRef(that); + return static_cast&>(*this); +} + +template +template < + class I2, + std::enable_if_t::value, int>> +inline Poly& PolyRef::operator=(Poly const& that) noexcept( + std::is_reference::value) { + *this = PolyRef(that); + return static_cast&>(*this); +} + +template +inline void PolyRef::swap(Poly& that) noexcept { + std::swap(_data_()->pobj_, that._data_()->pobj_); + std::swap(this->vptr_, that.vptr_); +} + +template +inline AddCvrefOf, I>& PolyRef::get() const noexcept { + return const_cast, I>&>( + static_cast const&>(*this)); +} + +} // namespace detail +} // namespace folly diff --git a/folly/Poly.cpp b/folly/Poly.cpp new file mode 100644 index 00000000..352b1a0f --- /dev/null +++ b/folly/Poly.cpp @@ -0,0 +1,24 @@ +/* + * Copyright 2017-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace folly { +namespace detail { +[[noreturn]] void throwBadPolyAccess() { throw BadPolyAccess(); } +[[noreturn]] void throwBadPolyCast() { throw BadPolyCast(); } +} // namespace detail +} // namespace folly diff --git a/folly/Poly.h b/folly/Poly.h new file mode 100644 index 00000000..218fbd89 --- /dev/null +++ b/folly/Poly.h @@ -0,0 +1,1184 @@ +/* + * Copyright 2017-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// TODO: [x] "cast" from Poly to Poly +// TODO: [ ] copy/move from Poly/Poly to Poly +// TODO: [ ] copy-on-write? +// TODO: [ ] down- and cross-casting? (Possible?) +// TODO: [ ] shared ownership? (Dubious.) +// TODO: [ ] can games be played with making the VTable a member of a struct +// with strange alignment such that the address of the VTable can +// be used to tell whether the object is stored in-situ or not? + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if !defined(__cpp_inline_variables) +#define FOLLY_INLINE_CONSTEXPR constexpr +#else +#define FOLLY_INLINE_CONSTEXPR inline constexpr +#endif + +#include + +namespace folly { +template +struct Poly; + +/** + * Within the definition of interface `I`, `PolySelf` is an alias for + * the instance of `Poly` that is currently being instantiated. It is + * one of: `Poly`, `Poly`, `Poly`, or `Poly`; where + * `J` is either `I` or some interface that extends `I`. + * + * It can be used within interface definitions to declare members that accept + * other `Poly` objects of the same type as `*this`. + * + * The first parameter may optionally be cv- and/or reference-qualified, in + * which case, the qualification is applies to the type of the interface in the + * resulting `Poly<>` instance. The second template parameter controls whether + * or not the interface is decayed before the cv-ref qualifiers of the first + * argument are applied. For example, given the following: + * + * struct Foo { + * template + * struct Interface : Base { + * using A = PolySelf; + * using B = PolySelf; + * using C = PolySelf; + * using X = PolySelf; + * using Y = PolySelf; + * using Z = PolySelf; + * }; + * // ... + * }; + * struct Bar : PolyExtends { + * // ... + * }; + * + * Then for `Poly`, the typedefs are aliases for the following types: + * - `A` is `Poly` + * - `B` is `Poly` + * - `C` is `Poly` + * - `X` is `Poly` + * - `Y` is `Poly` + * - `Z` is `Poly` + * + * And for `Poly`, the typedefs are aliases for the following types: + * - `A` is `Poly` + * - `B` is `Poly` + * - `C` is `Poly` + * - `X` is `Poly` + * - `Y` is `Poly` + * - `Z` is `Poly` + */ +template < + class Node, + class Tfx = detail::MetaIdentity, + class Access = detail::PolyAccess> +using PolySelf = decltype(Access::template self_()); + +/** + * When used in conjunction with `PolySelf`, controls how to construct `Poly` + * types related to the one currently being instantiated. + * + * \sa PolySelf + */ +using PolyDecay = detail::MetaQuote; + +#if !defined(__cpp_template_auto) + +/** + * Use `FOLLY_POLY_MEMBERS(MEMS...)` on pre-C++17 compilers to specify a + * comma-separated list of member function bindings. + * + * For example: + * + * struct IFooBar { + * template + * struct Interface : Base { + * int foo() const { return folly::poly_call<0>(*this); } + * void bar() { folly::poly_call<1>(*this); } + * }; + * template + * using Members = FOLLY_POLY_MEMBERS(&T::foo, &T::bar); + * }; + */ +#define FOLLY_POLY_MEMBERS(...) \ + typename decltype(::folly::detail::deduceMembers( \ + __VA_ARGS__))::template Members<__VA_ARGS__> + +/** + * Use `FOLLY_POLY_MEMBER(SIG, MEM)` on pre-C++17 compilers to specify a member + * function binding that needs to be disambiguated because of overloads. `SIG` + * should the (possibly const-qualified) signature of the `MEM` member function + * pointer. + * + * For example: + * + * struct IFoo { + * template struct Interface : Base { + * int foo() const { return folly::poly_call<0>(*this); } + * }; + * template using Members = FOLLY_POLY_MEMBERS( + * // This works even if T::foo is overloaded: + * FOLLY_POLY_MEMBER(int()const, &T::foo) + * ); + * }; + */ +#define FOLLY_POLY_MEMBER(SIG, MEM) \ + ::folly::detail::MemberDef< \ + ::folly::detail::Member(MEM)), MEM>>::value + +/** + * A list of member function bindings. + */ +template +using PolyMembers = detail::TypeList; + +#else +#define FOLLY_POLY_MEMBER(SIG, MEM) ::folly::sig(MEM) +#define FOLLY_POLY_MEMBERS(...) ::folly::PolyMembers<__VA_ARGS__> + +template +struct PolyMembers {}; + +#endif + +/** + * Exception type that is thrown on invalid access of an empty `Poly` object. + */ +struct BadPolyAccess : std::exception { + BadPolyAccess() = default; + char const* what() const noexcept override { + return "BadPolyAccess"; + } +}; + +/** + * Exception type that is thrown when attempting to extract from a `Poly` a + * value of the wrong type. + */ +struct BadPolyCast : std::bad_cast { + BadPolyCast() = default; + char const* what() const noexcept override { + return "BadPolyCast"; + } +}; + +/** + * Used in the definition of a `Poly` interface to say that the current + * interface is an extension of a set of zero or more interfaces. + * + * Example: + * + * struct IFoo { + * template struct Interface : Base { + * void foo() { folly::poly_call<0>(*this); } + * }; + * template using Members = FOLLY_POLY_MEMBERS(&T::foo); + * } + * struct IBar : PolyExtends { + * template struct Interface : Base { + * void bar(int i) { folly::poly_call<0>(*this, i); } + * }; + * template using Members = FOLLY_POLY_MEMBERS(&T::bar); + * } + */ +template +struct PolyExtends : virtual I... { + using Subsumptions = detail::TypeList; + + template + struct Interface : Base { + Interface() = default; + using Base::Base; + }; + + template + using Members = PolyMembers<>; +}; + +//////////////////////////////////////////////////////////////////////////////// +/** + * Call the N-th member of the currently-being-defined interface. When the + * first parameter is an object of type `PolySelf` (as opposed to `*this`) + * you must explicitly specify which interface through which to dispatch. + * For instance: + * + * struct IAddable { + * template + * struct Interface : Base { + * friend PolySelf + * operator+(PolySelf const& a, PolySelf const& b) { + * return folly::poly_call<0, IAddable>(a, b); + * } + * }; + * template + * static auto plus_(T const& a, T const& b) -> decltype(a + b) { + * return a + b; + * } + * template + * using Members = FOLLY_POLY_MEMBERS(&plus_>); + * }; + * + * \sa PolySelf + */ +template +auto poly_call(This&& _this, As&&... as) + -> decltype(detail::PolyAccess::call( + static_cast(_this), + static_cast(as)...)) { + return detail::PolyAccess::call( + static_cast(_this), static_cast(as)...); +} + +/// \overload +template +decltype(auto) poly_call(detail::PolyNode&& _this, As&&... as) { + using This = detail::InterfaceOf>; + return detail::PolyAccess::call( + static_cast(_this), static_cast(as)...); +} + +/// \overload +template +decltype(auto) poly_call(detail::PolyNode& _this, As&&... as) { + using This = detail::InterfaceOf>; + return detail::PolyAccess::call( + static_cast(_this), static_cast(as)...); +} + +/// \overload +template +decltype(auto) poly_call(detail::PolyNode const& _this, As&&... as) { + using This = detail::InterfaceOf>; + return detail::PolyAccess::call( + static_cast(_this), static_cast(as)...); +} + +/// \overload +template < + std::size_t N, + class I, + class Poly, + typename... As, + std::enable_if_t::value, int> = 0> +auto poly_call(Poly&& _this, As&&... as) -> decltype(poly_call( + static_cast(_this).get(), + static_cast(as)...)) { + return poly_call( + static_cast(_this).get(), static_cast(as)...); +} + +/// \cond +/// \overload +template +[[noreturn]] detail::Bottom poly_call(detail::ArchetypeBase const&, As&&...) { + assume_unreachable(); +} +/// \endcond + +//////////////////////////////////////////////////////////////////////////////// +/** + * Try to cast the `Poly` object to the requested type. If the `Poly` stores an + * object of that type, return a reference to the object; otherwise, throw an + * exception. + * \tparam T The (unqualified) type to which to cast the `Poly` object. + * \tparam Poly The type of the `Poly` object. + * \param that The `Poly` object to be cast. + * \return A reference to the `T` object stored in or refered to by `that`. + * \throw BadPolyAccess if `that` is empty. + * \throw BadPolyCast if `that` does not store or refer to an object of type + * `T`. + */ +template +detail::AddCvrefOf&& poly_cast(detail::PolyRoot&& that) { + return detail::PolyAccess::cast(std::move(that)); +} + +/// \overload +template +detail::AddCvrefOf& poly_cast(detail::PolyRoot& that) { + return detail::PolyAccess::cast(that); +} + +/// \overload +template +detail::AddCvrefOf const& poly_cast(detail::PolyRoot const& that) { + return detail::PolyAccess::cast(that); +} + +/// \cond +/// \overload +template +[[noreturn]] detail::AddCvrefOf&& poly_cast(detail::ArchetypeRoot&&) { + assume_unreachable(); +} + +/// \overload +template +[[noreturn]] detail::AddCvrefOf& poly_cast(detail::ArchetypeRoot&) { + assume_unreachable(); +} + +/// \overload +template +[[noreturn]] detail::AddCvrefOf const& poly_cast( + detail::ArchetypeRoot const&) { assume_unreachable(); } +/// \endcond + +/// \overload +template < + class T, + class Poly, + std::enable_if_t::value, int> = 0> +constexpr auto poly_cast(Poly&& that) + -> decltype(poly_cast(std::declval().get())) { + return poly_cast(static_cast(that).get()); +} + +//////////////////////////////////////////////////////////////////////////////// +/** + * Returns a reference to the `std::type_info` object corresponding to the + * object currently stored in `that`. If `that` is empty, returns + * `typeid(void)`. + */ +template +std::type_info const& poly_type(detail::PolyRoot const& that) noexcept { + return detail::PolyAccess::type(that); +} + +/// \cond +/// \overload +[[noreturn]] inline std::type_info const& poly_type( + detail::ArchetypeBase const&) noexcept { + assume_unreachable(); +} +/// \endcond + +/// \overload +template ::value, int> = 0> +constexpr auto poly_type(Poly const& that) noexcept + -> decltype(poly_type(that.get())) { + return poly_type(that.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +/** + * Returns `true` if `that` is not currently storing an object; `false`, + * otherwise. + */ +template +bool poly_empty(detail::PolyRoot const& that) noexcept { + return detail::State::eEmpty == detail::PolyAccess::vtable(that)->state_; +} + +/// \overload +template +constexpr bool poly_empty(detail::PolyRoot const&) noexcept { + return false; +} + +/// \overload +template +constexpr bool poly_empty(detail::PolyRoot const&) noexcept { + return false; +} + +/// \overload +template +constexpr bool poly_empty(Poly const&) noexcept { + return false; +} + +/// \overload +template +constexpr bool poly_empty(Poly const&) noexcept { + return false; +} + +/// \cond +[[noreturn]] inline bool poly_empty(detail::ArchetypeBase const&) noexcept { + assume_unreachable(); +} +/// \endcond + +//////////////////////////////////////////////////////////////////////////////// +/** + * Given a `Poly`, return a `Poly`. Otherwise, when `I` is not a + * reference type, returns a `Poly&&` when given a `Poly&`, like + * `std::move`. + */ +template < + class I, + std::enable_if_t>::value, int> = 0> +constexpr Poly&& poly_move(detail::PolyRoot& that) noexcept { + return static_cast&&>(static_cast&>(that)); +} + +/// \overload +template < + class I, + std::enable_if_t>::value, int> = 0> +Poly poly_move(detail::PolyRoot const& that) noexcept { + return detail::PolyAccess::move(that); +} + +/// \overload +template +Poly poly_move(detail::PolyRoot const& that) noexcept { + return detail::PolyAccess::move(that); +} + +/// \cond +/// \overload +[[noreturn]] inline detail::ArchetypeBase poly_move( + detail::ArchetypeBase const&) noexcept { + assume_unreachable(); +} +/// \endcond + +/// \overload +template ::value, int> = 0> +constexpr auto poly_move(Poly& that) noexcept + -> decltype(poly_move(that.get())) { + return poly_move(that.get()); +} + +/// \cond +namespace detail { +/** + * The implementation for `Poly` for when the interface type is not + * reference-like qualified, as in `Poly`. + */ +template +struct PolyVal : PolyImpl { + private: + friend PolyAccess; + + struct NoneSuch {}; + using Copyable = std::is_copy_constructible>; + using PolyOrNonesuch = If; + + using PolyRoot::vptr_; + + PolyRoot& _polyRoot_() noexcept { + return *this; + } + PolyRoot const& _polyRoot_() const noexcept { + return *this; + } + + Data* _data_() noexcept { + return PolyAccess::data(*this); + } + Data const* _data_() const noexcept { + return PolyAccess::data(*this); + } + + public: + /** + * Default constructor. + * \post `poly_empty(*this) == true` + */ + PolyVal() = default; + /** + * Move constructor. + * \post `poly_empty(that) == true` + */ + PolyVal(PolyVal&& that) noexcept; + /** + * A copy constructor if `I` is copyable; otherwise, a useless constructor + * from a private, incomplete type. + */ + /* implicit */ PolyVal(PolyOrNonesuch const& that); + + ~PolyVal(); + + /** + * Inherit any constructors defined by any of the interfaces. + */ + using PolyImpl::PolyImpl; + + /** + * Copy assignment, destroys the object currently held (if any) and makes + * `*this` equal to `that` by stealing its guts. + */ + Poly& operator=(PolyVal that) noexcept; + + /** + * Construct a Poly from a concrete type that satisfies the I concept + */ + template ::value, int> = 0> + /* implicit */ PolyVal(T&& t); + + /** + * Construct a `Poly` from a compatible `Poly`. "Compatible" here means: the + * other interface extends this one either directly or indirectly. + */ + template ::value, int> = 0> + /* implicit */ PolyVal(Poly that); + + /** + * Assign to this `Poly` from a concrete type that satisfies the `I` + * concept. + */ + template ::value, int> = 0> + Poly& operator=(T&& t); + + /** + * Assign a compatible `Poly` to `*this`. "Compatible" here means: the + * other interface extends this one either directly or indirectly. + */ + template ::value, int> = 0> + Poly& operator=(Poly that); + + /** + * Swaps the values of two `Poly` objects. + */ + void swap(Poly& that) noexcept; +}; + +//////////////////////////////////////////////////////////////////////////////// +/** + * The implementation of `Poly` for when the interface type is + * reference-quelified, like `Poly`. + */ +template +struct PolyRef : private PolyImpl { + private: + friend PolyAccess; + + AddCvrefOf, I>& _polyRoot_() const noexcept; + + Data* _data_() noexcept { + return PolyAccess::data(*this); + } + Data const* _data_() const noexcept { + return PolyAccess::data(*this); + } + + static constexpr RefType refType() noexcept; + + protected: + template + PolyRef(That&& that, Type); + + public: + /** + * Copy constructor + * \post `&poly_cast(*this) == &poly_cast(that)`, where `T` is the + * type of the object held by `that`. + */ + PolyRef(PolyRef const& that) noexcept; + + /** + * Copy assignment + * \post `&poly_cast(*this) == &poly_cast(that)`, where `T` is the + * type of the object held by `that`. + */ + Poly& operator=(PolyRef const& that) noexcept; + + /** + * Construct a `Poly` from a concrete type that satisfies concept `I`. + * \post `!poly_empty(*this)` + */ + template ::value, int> = 0> + /* implicit */ PolyRef(T&& t) noexcept; + + /** + * Construct a `Poly` from a compatible `Poly`. + */ + template < + class I2, + std::enable_if_t::value, int> = 0> + /* implicit */ PolyRef(Poly&& that) noexcept( + std::is_reference::value); + + template < + class I2, + std::enable_if_t::value, int> = 0> + /* implicit */ PolyRef(Poly& that) noexcept(std::is_reference::value) + : PolyRef{that, Type{}} {} + + template < + class I2, + std::enable_if_t::value, int> = 0> + /* implicit */ PolyRef(Poly const& that) noexcept( + std::is_reference::value) + : PolyRef{that, Type{}} {} + + /** + * Assign to a `Poly` from a concrete type that satisfies concept `I`. + * \post `!poly_empty(*this)` + */ + template ::value, int> = 0> + Poly& operator=(T&& t) noexcept; + + /** + * Assign to `*this` from another compatible `Poly`. + */ + template < + class I2, + std::enable_if_t::value, int> = 0> + Poly& operator=(Poly&& that) noexcept(std::is_reference::value); + + /** + * \overload + */ + template < + class I2, + std::enable_if_t::value, int> = 0> + Poly& operator=(Poly& that) noexcept(std::is_reference::value); + + /** + * \overload + */ + template < + class I2, + std::enable_if_t::value, int> = 0> + Poly& operator=(Poly const& that) noexcept( + std::is_reference::value); + + /** + * Swap which object this `Poly` references ("shallow" swap). + */ + void swap(Poly& that) noexcept; + + /** + * Get a reference to the interface, with correct `const`-ness applied. + */ + AddCvrefOf, I>& get() const noexcept; + + /** + * Get a reference to the interface, with correct `const`-ness applied. + */ + AddCvrefOf, I>& operator*() const noexcept { + return get(); + } + + /** + * Get a pointer to the interface, with correct `const`-ness applied. + */ + auto operator-> () const noexcept { + return &get(); + } +}; + +template +using PolyValOrRef = If::value, PolyRef, PolyVal>; +} // namespace detail +/// \endcond + +/** + * `Poly` is a class template that makes it relatively easy to define a + * type-erasing polymorphic object wrapper. + * + * \par Type-erasure + * + * \par + * `std::function` is one example of a type-erasing polymorphic object wrapper; + * `folly::exception_wrapper` is another. Type-erasure is often used as an + * alternative to dynamic polymorphism via inheritance-based virtual dispatch. + * The distinguishing characteristic of type-erasing wrappers are: + * \li **Duck typing:** Types do not need to inherit from an abstract base + * class in order to be assignable to a type-erasing wrapper; they merely + * need to satisfy a particular interface. + * \li **Value semantics:** Type-erasing wrappers are objects that can be + * passed around _by value_. This is in contrast to abstract base classes + * which must be passed by reference or by pointer or else suffer from + * _slicing_, which causes them to lose their polymorphic behaviors. + * Reference semantics make it difficult to reason locally about code. + * \li **Automatic memory management:** When dealing with inheritance-based + * dynamic polymorphism, it is often necessary to allocate and manage + * objects on the heap. This leads to a proliferation of `shared_ptr`s and + * `unique_ptr`s in APIs, complicating their point-of-use. APIs that take + * type-erasing wrappers, on the other hand, can often store small objects + * in-situ, with no dynamic allocation. The memory management, if any, is + * handled for you, and leads to cleaner APIs: consumers of your API don't + * need to pass `shared_ptr`; they can simply pass any object + * that satisfies the interface you require. (`std::function` is a + * particularly compelling example of this benefit. Far worse would be an + * inheritance-based callable solution like + * `shared_ptr>`. ) + * + * \par Example: Defining a type-erasing function wrapper with `folly::Poly` + * + * \par + * Defining a polymorphic wrapper with `Poly` is a matter of defining two + * things: + * \li An *interface*, consisting of public member functions, and + * \li 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 + * `std::function`-like polymorphic wrapper. Its interface has only a simgle + * member function: `operator()` + * + * // An interface for a callable object of a particular signature, Fun + * // (most interfaces don't need to be templates, FWIW). + * template + * struct IFunction; + * + * template + * struct IFunction { + * // An interface is defined as a nested class template called + * // Interface that takes a single template parameter, Base, from + * // which it inherits. + * template + * struct Interface : Base { + * // The Interface has public member functions. These become the + * // public interface of the resulting Poly instantiation. + * // (Implementation note: Poly> will publicly + * // inherit from this struct, which is what gives it the right + * // member functions.) + * R operator()(As... as) const { + * // The definition of each member function in your interface will + * // always consist of a single line dispatching to + * // folly::poly_call. The "N" corresponds to the N-th member + * // function in the list of member function bindings, Members, + * // defined below. The first argument will always be *this, and the + * // rest of the arguments should simply forward (if necessary) the + * // member function's arguments. + * return static_cast( + * folly::poly_call<0>(*this, std::forward(as)...)); + * } + * }; + * + * // The "Members" alias template is a comma-separated list of bound + * // member functions for a given concrete type "T". The + * // "FOLLY_POLY_MEMBERS" macro accepts a comma-separated list, and the + * // (optional) "FOLLY_POLY_MEMBER" macro lets you disambiguate overloads + * // by explicitly specifying the function signature the target member + * // function should have. In this case, we require "T" to have a + * // function call operator with the signature `R(As...) const`. + * // + * // If you are using a C++17-compatible compiler, you can do away with + * // the macros and write this as: + * // + * // template + * // using Members = folly::PolyMembers< + * // folly::sig(&T::operator())>; + * // + * // And since `folly::sig` is only needed for disambiguation in case of + * // overloads, if you are not concerned about objects with overloaded + * // function call operators, it could be further simplified to: + * // + * // template + * // using Members = folly::PolyMembers<&T::operator()>; + * // + * template + * using Members = FOLLY_POLY_MEMBERS( + * FOLLY_POLY_MEMBER(R(As...) const, &T::operator())); + * }; + * + * // Now that we have defined the interface, we can pass it to Poly to + * // create our type-erasing wrapper: + * template + * using Function = Poly>; + * + * \par + * Given the above definition of `Function`, users can now initialize instances + * of (say) `Function` with function objects like + * `std::plus` and `std::multiplies`, as below: + * + * Function fun = std::plus{}; + * assert(5 == fun(2, 3)); + * fun = std::multiplies{}; + * assert(6 = fun(2, 3)); + * + * \par Defining an interface with C++17 + * + * \par + * With C++17, defining an interface to be used with `Poly` is fairly + * straightforward. As in the `Function` example above, there is a struct with + * a nested `Interface` class template and a nested `Members` alias template. + * No macros are needed with C++17. + * \par + * Imagine we were defining something like a Java-style iterator. If we are + * using a C++17 compiler, our interface would look something like this: + * + * template + * struct IJavaIterator { + * template + * struct Interface : Base { + * bool Done() const { return folly::poly_call<0>(*this); } + * Value Current() const { return folly::poly_call<1>(*this); } + * void Next() { folly::poly_call<2>(*this); } + * }; + * // NOTE: This works in C++17 only: + * template + * using Members = folly::PolyMembers<&T::Done, &T::Current, &T::Next>; + * }; + * + * template + * using JavaIterator = Poly; + * + * \par + * Given the above definition, `JavaIterator` can be used to hold instances + * of any type that has `Done`, `Current`, and `Next` member functions with the + * correct (or compatible) signatures. + * + * \par + * The presence of overloaded member functions complicates this picture. Often, + * property members are faked in C++ with `const` and non-`const` member + * function overloads, like in the interface specified below: + * + * struct IIntProperty { + * template + * struct Interface : Base { + * int Value() const { return folly::poly_call<0>(*this); } + * void Value(int i) { folly::poly_call<1>(*this, i); } + * }; + * // NOTE: This works in C++17 only: + * template + * using Members = folly::PolyMembers< + * folly::sig(&T::Value), + * folly::sig(&T::Value)>; + * }; + * + * using IntProperty = Poly; + * + * \par + * Now, any object that has `Value` members of compatible signatures can be + * assigned to instances of `IntProperty` object. Note how `folly::sig` is used + * to disambiguate the overloads of `&T::Value`. + * + * \par Defining an interface with C++14 + * + * \par + * In C++14, the nice syntax above doesn't work, so we have to resort to macros. + * The two examples above would look like this: + * + * template + * struct IJavaIterator { + * template + * struct Interface : Base { + * bool Done() const { return folly::poly_call<0>(*this); } + * Value Current() const { return folly::poly_call<1>(*this); } + * void Next() { folly::poly_call<2>(*this); } + * }; + * // NOTE: This works in C++14 and C++17: + * template + * using Members = FOLLY_POLY_MEMBERS(&T::Done, &T::Current, &T::Next); + * }; + * + * template + * using JavaIterator = Poly; + * + * \par + * and + * + * struct IIntProperty { + * template + * struct Interface : Base { + * int Value() const { return folly::poly_call<0>(*this); } + * void Value(int i) { return folly::poly_call<1>(*this, i); } + * }; + * // NOTE: This works in C++14 and C++17: + * template + * using Members = FOLLY_POLY_MEMBERS( + * FOLLY_POLY_MEMBER(int() const, &T::Value), + * FOLLY_POLY_MEMBER(void(int), &T::Value)); + * }; + * + * using IntProperty = Poly; + * + * \par Extending interfaces + * + * \par + * 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::PolyExtends` to say that one + * interface extends another. + * + * struct IFoo { + * template + * struct Interface : Base { + * void Foo() const { return folly::poly_call<0>(*this); } + * }; + * template + * using Members = FOLLY_POLY_MEMBERS(&T::Foo); + * }; + * + * // The IFooBar interface extends the IFoo interface + * struct IFooBar : PolyExtends { + * template + * struct Interface : Base { + * void Bar() const { return folly::poly_call<0>(*this); } + * }; + * template + * using Members = FOLLY_POLY_MEMBERS(&T::Bar); + * }; + * + * using FooBar = Poly; + * + * \par + * Given the above defintion, instances of type `FooBar` have both `Foo()` and + * `Bar()` member functions. + * + * \par + * The sensible conversions exist between a wrapped derived type and a wrapped + * base type. For instance, assuming `IDerived` extends `IBase` with + * `PolyExtends`: + * + * Poly derived = ...; + * Poly base = derived; // This conversion is OK. + * + * \par + * As you would expect, there is no conversion in the other direction, and at + * present there is no `Poly` equivalent to `dynamic_cast`. + * + * \par Type-erasing polymorphic reference wrappers + * + * \par + * Sometimes you don't need to own a copy of an object; a reference will do. For + * that you can use `Poly` to capture a _reference_ to an object satisfying an + * interface rather than the whole object itself. The syntax is intuitive. + * + * int i = 42; + * // Capture a mutable reference to an object of any IRegular type: + * Poly intRef = i; + * assert(42 == folly::poly_cast(intRef)); + * // Assert that we captured the address of "i": + * assert(&i == &folly::poly_cast(intRef)); + * + * \par + * A reference-like `Poly` has a different interface than a value-like `Poly`. + * Rather than calling member functions with the `obj.fun()` syntax, you would + * use the `obj->fun()` syntax. This is for the sake of `const`-correctness. + * For example, consider the code below: + * + * struct IFoo { + * template + * struct Interface { + * void Foo() { folly::poly_call<0>(*this); } + * }; + * template + * using Members = folly::PolyMembers<&T::Foo>; + * }; + * + * struct SomeFoo { + * void Foo() { std::printf("SomeFoo::Foo\n"); } + * }; + * + * SomeFoo foo; + * Poly const anyFoo = foo; + * anyFoo->Foo(); // prints "SomeFoo::Foo" + * + * \par + * Notice in the above code that the `Foo` member function is non-`const`. + * Notice also that the `anyFoo` object is `const`. However, since it has + * captured a non-`const` reference to the `foo` object, it should still be + * possible to dispatch to the non-`const` `Foo` member function. When + * instantiated with a reference type, `Poly` has an overloaded `operator->` + * member that returns a pointer to the `IFoo` interface with the correct + * `const`-ness, which makes this work. + * + * \par + * The same mechanism also prevents users from calling non-`const` member + * functions on `Poly` objects that have captured `const` references, which + * would violate `const`-correctness. + * + * \par + * Sensible conversions exist between non-reference and reference `Poly`s. For + * instance: + * + * Poly value = 42; + * Poly mutable_ref = value; + * Poly const_ref = mutable_ref; + * + * assert(&poly_cast(value) == &poly_cast(mutable_ref)); + * assert(&poly_cast(value) == &poly_cast(const_ref)); + * + * \par Non-member functions (C++17) + * + * \par + * If you wanted to write the interface `ILogicallyNegatable`, which captures + * all types that can be negated with unary `operator!`, you could do it + * as we've shown above, by binding `&T::operator!` in the nested `Members` + * alias template, but that has the problem that it won't work for types that + * have defined unary `operator!` as a free function. To handle this case, + * the `Poly` library lets you use a free function instead of a member function + * when creating a binding. + * + * \par + * With C++17 you may use a lambda to create a binding, as shown in the example + * below: + * + * struct ILogicallyNegatable { + * template + * struct Interface : Base { + * bool operator!() const { return folly::poly_call<0>(*this); } + * }; + * template + * using Members = folly::PolyMembers< + * +[](T const& t) -> decltype(!t) { return !t; }>; + * }; + * + * \par + * This requires some explanation. The unary `operator+` in front of the lambda + * is necessary! It causes the lambda to decay to a C-style function pointer, + * which is one of the types that `folly::PolyMembers` accepts. The `decltype` + * in the lambda return type is also necessary. Through the magic of SFINAE, it + * will cause `Poly` to reject any types that don't support + * unary `operator!`. + * + * \par + * If you are using a free function to create a binding, the first parameter is + * implicitly the `this` parameter. It will receive the type-erased object. + * + * \par Non-member functions (C++14) + * + * \par + * If you are using a C++14 compiler, the defintion 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: + * + * struct ILogicallyNegatable { + * template + * struct Interface : Base { + * bool operator!() const { return folly::poly_call<0>(*this); } + * }; + * + * template + * static auto negate(T const& t) -> decltype(!t) { return !t; } + * + * template + * using Members = FOLLY_POLY_MEMBERS(&negate); + * }; + * + * \par + * As with the example that uses the lambda in the preceding section, the first + * parameter is implicitly the `this` parameter. It will receive the type-erased + * object. + * + * \par Multi-dispatch + * + * \par + * What if you want to create an `IAddable` interface for things that can be + * added? Adding requires _two_ objects, both of which are type-erased. This + * interface requires dispatching on both objects, doing the addition only + * if the types are the same. For this we make use of the `PolySelf` template + * alias to define an interface that takes more than one object of the the + * erased type. + * + * struct IAddable { + * template + * struct Interface : Base { + * friend PolySelf + * operator+(PolySelf const& a, PolySelf const& b) { + * return folly::poly_call<0, IAddable>(a, b); + * } + * }; + * + * template + * using Members = folly::PolyMembers< + * +[](T const& a, T const& b) -> decltype(a + b) { return a + b; }>; + * }; + * + * \par + * Given the above defintion of `IAddable` we would be able to do the following: + * + * Poly a = 2, b = 3; + * Poly c = a + b; + * assert(poly_cast(c) == 5); + * + * \par + * If `a` and `b` stored objects of different types, a `BadPolyCast` exception + * would be thrown. + * + * \par Move-only types + * + * \par + * If you want to store move-only types, then your interface should extend the + * `IMoveOnly` interface. + * + * \par Implementation notes + * \par + * `Poly` will store "small" objects in an internal buffer, avoiding the cost of + * of dynamic allocations. At present, this size is not configurable; it is + * pegged at the size of two `double`s. + * + * \par + * `Poly` objects are always nothrow movable. If you store an object in one that + * has a potentially throwing move contructor, the object will be stored on the + * heap, even if it could fit in the internal storage of the `Poly` object. + * (So be sure to give your objects nothrow move constructors!) + * + * \par + * `Poly` implements type-erasure in a manner very similar to how the compiler + * accomplishes virtual dispatch. Every `Poly` object contains a pointer to a + * table of function pointers. Member function calls involve a double- + * indirection: once through the v-pointer, and other indirect function call + * through the function pointer. + */ +template +struct Poly final : detail::PolyValOrRef { + friend detail::PolyAccess; + Poly() = default; + using detail::PolyValOrRef::PolyValOrRef; + using detail::PolyValOrRef::operator=; +}; + +/** + * Swap two `Poly` instances. + */ +template +void swap(Poly& left, Poly& right) noexcept { + left.swap(right); +} + +/** + * Pseudo-function template handy for disambiguating function overloads. + * + * For example, given: + * struct S { + * int property() const; + * void property(int); + * }; + * + * You can get a member function pointer to the first overload with: + * folly::sig(&S::property); + * + * This is arguably a nicer syntax that using the built-in `static_cast`: + * static_cast(&S::property); + * + * `sig` is also more permissive than `static_cast` about `const`. For instance, + * the following also works: + * folly::sig(&S::property); + * + * The above is permitted + */ +template +FOLLY_INLINE_CONSTEXPR detail::Sig const sig = {}; + +} // namespace folly + +#include + +#undef FOLLY_INLINE_CONSTEXPR diff --git a/folly/Traits.h b/folly/Traits.h index ebf69acf..f513462a 100644 --- a/folly/Traits.h +++ b/folly/Traits.h @@ -312,8 +312,9 @@ using is_trivially_copyable = std::is_trivially_copyable; } // namespace traits_detail struct Ignore { + Ignore() = default; template - /* implicit */ Ignore(const T&) {} + constexpr /* implicit */ Ignore(const T&) {} template const Ignore& operator=(T const&) const { return *this; } }; diff --git a/folly/Utility.h b/folly/Utility.h index bb196080..1969a964 100644 --- a/folly/Utility.h +++ b/folly/Utility.h @@ -310,4 +310,11 @@ class MoveOnly { } // namespace moveonly_ using MoveOnly = moveonly_::MoveOnly; + +/** + * A pithy alias for std::integral_constant. + */ +template +using Bool = std::integral_constant; + } // namespace folly diff --git a/folly/detail/PolyDetail.h b/folly/detail/PolyDetail.h new file mode 100644 index 00000000..144a9411 --- /dev/null +++ b/folly/detail/PolyDetail.h @@ -0,0 +1,922 @@ +/* + * Copyright 2017-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace folly { +/// \cond +namespace detail { +template +struct PolyRoot; + +using RRef_ = MetaQuoteTrait; +using LRef_ = MetaQuoteTrait; + +template +struct XRef_ : Type> {}; +template +using XRef = _t>; +template +struct XRef_ : Type>> {}; +template +struct XRef_ : Type>> {}; +template +struct XRef_ : Type> {}; + +template +using AddCvrefOf = MetaApply, A>; +} // namespace detail +/// \endcond + +template +struct Poly; + +template +detail::AddCvrefOf& poly_cast(detail::PolyRoot&); + +template +detail::AddCvrefOf const& poly_cast(detail::PolyRoot const&); + +#if !defined(__cpp_template_auto) +#define FOLLY_AUTO class +template +using PolyMembers = detail::TypeList; +#else +#define FOLLY_AUTO auto +template +struct PolyMembers; +#endif + +/// \cond +namespace detail { +/* ***************************************************************************** + * IMPLEMENTATION NOTES + * + +Building the Interface +---------------------- + +Here is a high-level description of how Poly works. Users write an interface +such as: + + struct Mine { + template + struct Interface { + int Exec() const { + return folly::poly_call<0>(*this); + } + } + template + using Members = folly::PolyMembers<&T::Exec>; + }; + +Then they instantiate Poly, which will have an Exec member function +of the correct signature. The Exec member function comes from +Mine::Interface>>, from which Poly inherits. +Here's what each piece means: + +- PolyRoot: stores Data, which is a union of a void* (used when the Poly is + storing an object on the heap or a reference) and some aligned storage (used + when the Poly is storing an object in-situ). PolyRoot also stores a vtable + pointer for interface I, which is a pointer to a struct containing function + pointers. The function pointers are bound member functions (e.g., + SomeType::Exec). More on the vtable pointer and how it is generated below. + +- PolyNode: provides the hooks used by folly::poly_call to dispatch to the +correctly bound member function for this interface. In the context of an +interface I, folly::poly_call(*this, args...) will: + 1. Fetch the vtable pointer from PolyRoot, + 2. Select the I portion of that vtable (which, in the case of interface + extension, may only be a part of the total vtable), + 3. Fetch the K-th function pointer from that vtable part, + 4. Call through that function pointer, passing Data (from PolyRoot) and any + additional arguments in the folly::poly_call invocation. + +In the case of interface extension -- for instance, if interface Mine extended +interface Yours by inheriting from PolyExtends -- then interface Mine +will have a list of base interfaces in a typelist called "Subsumptions". +Poly will fold all the subsumed interfaces together, linearly inheriting +from them. To take the example of an interface Mine that extends Yours, +Poly would inherit from this type: + + Mine::Interface< + PolyNode>>>> + +Through linear inheritance, Poly ends up with the public member functions +of both interfaces, Mine and Yours. + +VTables +------- + +As mentioned above, PolyRoot stores a vtable pointer for interface I. The +vtable is a struct whose members are function pointers. How are the types of +those function pointers computed from the interface? A dummy type is created, +Archetype, in much the same way that Poly's base type is computed. Instead +of PolyNode and PolyRoot, there is ArchetypeNode and ArchetypeRoot. These types +also provide hooks for folly::poly_call, but they are dummy hooks that do +nothing. (Actually, they call std::terminate; they will never be called.) Once +Archetype has been constructed, it is a concrete type that has all the +member functions of the interface and its subsumed interfaces. That type is +passed to Mine::Members, which takes the address of Archetype::Exec and +inspects the resulting member function type. This is done for each member in the +interface. From a list of [member] function pointers, it is a simple matter of +metaprogramming to build a struct of function pointers. std::tuple is used for +this. + +An extra field is added to the tuple for a function that handles all of the +"special" operations: destruction, copying, moving, getting the type +information, getting the address of the stored object, and fetching a fixed-up +vtable pointer for reference conversions (e.g., I -> I&, I& -> I const&, etc). + +Subsumed interfaces are handled by having VTable inherit from +BasePtr, where BasePtr has only one member of type +VTable const*. + +Now that the type of VTable is computed, how are the fields populated? +Poly default-constructs to an empty state. Its vtable pointer points to a +vtable whose fields are initialized with the addresses of functions that do +nothing but throw a BadPolyAccess exception. That way, if you call a member +function on an empty Poly, you get an exception. The function pointer +corresponding to the "special" operations points to a no-op function; copying, +moving and destroying an empty Poly does nothing. + +On the other hand, when you pass an object of type T satisfying interface I to +Poly's constructor or assignment operator, a vtable for {I,T} is reified by +passing type T to I::Members, thereby creating a list of bindings for T's member +functions. The address of this vtable gets stored in the PolyRoot subobject, +imbuing the Poly object with the behaviors of type T. The T object itself gets +stored either on the heap or in the aligned storage within the Poly object +itself, depending on the size of T and whether or not it has a noexcept move +constructor. +*/ + +template +using Uncvref = std::remove_cv_t>; + +template class U> +struct IsInstanceOf : std::false_type {}; + +template class U> +struct IsInstanceOf, U> : std::true_type {}; + +template +using Not = Bool; + +template +struct StaticConst { + static constexpr T value{}; +}; + +template +constexpr T StaticConst::value; + +template +void if_constexpr(std::true_type, Fun fun) { + fun(Identity{}); +} + +template +void if_constexpr(std::false_type, Fun) {} + +enum class Op : short { eNuke, eMove, eCopy, eType, eAddr, eRefr }; + +enum class RefType : std::uintptr_t { eRvalue, eLvalue, eConstLvalue }; + +struct Data; + +template +struct PolyVal; + +template +struct PolyRef; + +struct PolyAccess; + +template +using IsPoly = IsInstanceOf, Poly>; + +// Given an interface I and a concrete type T that satisfies the interface +// I, create a list of member function bindings from members of T to members +// of I. +template +using MembersOf = typename I::template Members>; + +// Given an interface I and a base type T, create a type that implements +// the interface I in terms of the capabilities of T. +template +using InterfaceOf = typename I::template Interface; + +[[noreturn]] void throwBadPolyAccess(); +[[noreturn]] void throwBadPolyCast(); + +#if !defined(__cpp_template_auto) +template +using Member = std::integral_constant; + +template +using MemberType = typename M::value_type; + +template +inline constexpr MemberType memberValue() noexcept { + return M::value; +} + +template +struct MakeMembers { + template + using Members = PolyMembers...>; +}; + +template +MakeMembers deduceMembers(Ts...); + +template > +struct MemberDef; + +template +struct MemberDef { + static R value(D& d, As... as) { + return folly::invoke(memberValue(), d, static_cast(as)...); + } +}; + +template +struct MemberDef { + static R value(D const& d, As... as) { + return folly::invoke(memberValue(), d, static_cast(as)...); + } +}; + +#else +template +using MemberType = decltype(M); + +template +inline constexpr MemberType memberValue() noexcept { + return M; +} +#endif + +struct PolyBase {}; + +template +struct SubsumptionsOf_ { + using type = TypeList<>; +}; + +template +using InclusiveSubsumptionsOf = TypePushFront<_t>, I>; + +template +struct SubsumptionsOf_> { + using type = TypeJoin>>; +}; + +template +using SubsumptionsOf = TypeReverseUnique<_t>>; + +struct Bottom { + template + [[noreturn]] /* implicit */ operator T &&() const { + std::terminate(); + } +}; + +using ArchetypeNode = MetaQuote; + +template +struct ArchetypeRoot; + +template +using Archetype = + TypeFold, ArchetypeRoot, ArchetypeNode>; + +struct ArchetypeBase : Bottom { + ArchetypeBase() = default; + template + /* implicit */ ArchetypeBase(T&&); + template + [[noreturn]] Bottom _polyCall_(As&&...) const { std::terminate(); } + + friend bool operator==(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator!=(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator<(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator<=(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator>(ArchetypeBase const&, ArchetypeBase const&); + friend bool operator>=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator++(ArchetypeBase const&); + friend Bottom operator++(ArchetypeBase const&, int); + friend Bottom operator--(ArchetypeBase const&); + friend Bottom operator--(ArchetypeBase const&, int); + friend Bottom operator+(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator+=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator-(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator-=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator*(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator*=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator/(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator/=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator%(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator%=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator<<(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator<<=(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator>>(ArchetypeBase const&, ArchetypeBase const&); + friend Bottom operator>>=(ArchetypeBase const&, ArchetypeBase const&); +}; + +template +struct ArchetypeRoot : ArchetypeBase { + template + using _polySelf_ = Archetype, Node>>; + using _polyInterface_ = I; +}; + +struct Data { + Data() = default; + // Suppress compiler-generated copy ops to not copy anything: + Data(Data const&) {} + Data& operator=(Data const&) { + return *this; + } + union { + void* pobj_ = nullptr; + std::aligned_storage_t buff_; + }; +}; + +template +using Arg = + If, Archetype>::value, + Poly>, + U>; + +template +using Ret = + If, Archetype>::value, + AddCvrefOf, U>, + U>; + +template +struct SignatureOf_; + +template +struct SignatureOf_ { + using type = Ret (*)(Data&, Arg...); +}; + +template +struct SignatureOf_ { + using type = Ret (*)(Data const&, Arg...); +}; + +template +struct SignatureOf_ { + using type = Ret (*)(Data&, Arg...); +}; + +template +struct SignatureOf_ { + using type = Ret (*)(Data const&, Arg...); +}; + +template +using SignatureOf = _t, I>>; + +template > +struct ArgTypes_; + +template +struct ArgTypes_ { + using type = TypeList; +}; + +template +using ArgTypes = _t>; + +template +using FnPtr = R (*)(Args...); + +struct ThrowThunk { + template + constexpr /* implicit */ operator FnPtr() const noexcept { + struct _ { + static R call(Args...) { + throwBadPolyAccess(); + } + }; + return &_::call; + } +}; + +inline constexpr ThrowThunk throw_() noexcept { + return ThrowThunk{}; +} + +template +inline constexpr bool inSitu() noexcept { + return !std::is_reference::value && + sizeof(std::decay_t) <= sizeof(Data) && + std::is_nothrow_move_constructible>::value; +} + +template +T& get(Data& d) noexcept { + if (inSitu()) { + return *(std::add_pointer_t)static_cast(&d.buff_); + } else { + return *static_cast>(d.pobj_); + } +} + +template +T const& get(Data const& d) noexcept { + if (inSitu()) { + return *(std::add_pointer_t)static_cast(&d.buff_); + } else { + return *static_cast>(d.pobj_); + } +} + +enum class State : short { eEmpty, eInSitu, eOnHeap }; + +template +U&& convert(U&& u) noexcept { + return static_cast(u); +} + +template +decltype(auto) convert(Poly u) { + return poly_cast>(u.get()); +} + +template +struct IsConstMember : std::false_type {}; + +template +struct IsConstMember : std::true_type {}; + +template +struct IsConstMember : std::true_type {}; + +template < + class T, + FOLLY_AUTO User, + class I, + class = ArgTypes, + class = Bool> +struct ThunkFn { + template + constexpr /* implicit */ operator FnPtr() const noexcept { + return nullptr; + } +}; + +template +struct ThunkFn< + T, + User, + I, + TypeList, + Bool< + !std::is_const>::value || + IsConstMember>::value>> { + template + constexpr /* implicit */ operator FnPtr() const noexcept { + struct _ { + static R call(D& d, As... as) { + return folly::invoke( + memberValue(), + get(d), + convert(static_cast(as))...); + } + }; + return &_::call; + } +}; + +template < + class I, + class = MembersOf>, + class = SubsumptionsOf> +struct VTable; + +template +inline constexpr ThunkFn thunk() noexcept { + return ThunkFn{}; +} + +template +constexpr VTable const* vtable() noexcept { + return &StaticConst>::value; +} + +template +struct VTableFor : VTable { + constexpr VTableFor() noexcept : VTable{Type{}} {} +}; + +template +constexpr VTable const* vtableFor() noexcept { + return &StaticConst>::value; +} + +template +constexpr void* vtableForRef(RefType ref) { + switch (ref) { + case RefType::eRvalue: + return const_cast*>(vtableFor()); + case RefType::eLvalue: + return const_cast*>(vtableFor()); + case RefType::eConstLvalue: + return const_cast*>(vtableFor()); + } + return nullptr; +} + +template < + class I, + class T, + std::enable_if_t::value, int> = 0> +void* execOnHeap(Op op, Data* from, void* to) { + switch (op) { + case Op::eNuke: + break; + case Op::eMove: + case Op::eCopy: + static_cast(to)->pobj_ = from->pobj_; + break; + case Op::eType: + return const_cast(static_cast(&typeid(T))); + case Op::eAddr: + if (*static_cast(to) == typeid(T)) { + return from->pobj_; + } + throwBadPolyCast(); + case Op::eRefr: + return vtableForRef>( + static_cast(reinterpret_cast(to))); + } + return nullptr; +} + +template < + class I, + class T, + std::enable_if_t>::value, int> = 0> +void* execOnHeap(Op op, Data* from, void* to) { + switch (op) { + case Op::eNuke: + delete &get(*from); + break; + case Op::eMove: + static_cast(to)->pobj_ = std::exchange(from->pobj_, nullptr); + break; + case Op::eCopy: + detail::if_constexpr(std::is_copy_constructible(), [&](auto id) { + static_cast(to)->pobj_ = new T(id(get(*from))); + }); + break; + case Op::eType: + return const_cast(static_cast(&typeid(T))); + case Op::eAddr: + if (*static_cast(to) == typeid(T)) { + return from->pobj_; + } + throwBadPolyCast(); + case Op::eRefr: + return vtableForRef>( + static_cast(reinterpret_cast(to))); + } + return nullptr; +} + +template +void* execInSitu(Op op, Data* from, void* to) { + switch (op) { + case Op::eNuke: + get(*from).~T(); + break; + case Op::eMove: + ::new (static_cast(&static_cast(to)->buff_)) + T(std::move(get(*from))); + get(*from).~T(); + break; + case Op::eCopy: + detail::if_constexpr(std::is_copy_constructible(), [&](auto id) { + ::new (static_cast(&static_cast(to)->buff_)) + T(id(get(*from))); + }); + break; + case Op::eType: + return const_cast(static_cast(&typeid(T))); + case Op::eAddr: + if (*static_cast(to) == typeid(T)) { + return &from->buff_; + } + throwBadPolyCast(); + case Op::eRefr: + return vtableForRef>( + static_cast(reinterpret_cast(to))); + } + return nullptr; +} + +inline void* noopExec(Op op, Data*, void*) { + if (op == Op::eAddr) + throwBadPolyAccess(); + return const_cast(static_cast(&typeid(void))); +} + +template +struct BasePtr { + VTable const* vptr_; +}; + +template (), int> = 0> +constexpr void* (*getOps() noexcept)(Op, Data*, void*) { + return &execInSitu; +} + +template (), int> = 0> +constexpr void* (*getOps() noexcept)(Op, Data*, void*) { + return &execOnHeap; +} + +template +struct VTable, TypeList> + : BasePtr..., std::tuple...> { + private: + template + constexpr VTable(Type, PolyMembers) noexcept + : BasePtr{vtableFor()}..., + std::tuple...>{thunk()...}, + state_{inSitu() ? State::eInSitu : State::eOnHeap}, + ops_{getOps()} {} + + public: + constexpr VTable() noexcept + : BasePtr{vtable()}..., + std::tuple...>{ + static_cast>(throw_())...}, + state_{State::eEmpty}, + ops_{&noopExec} {} + + template + explicit constexpr VTable(Type) noexcept + : VTable{Type{}, MembersOf{}} {} + + State state_; + void* (*ops_)(Op, Data*, void*); +}; + +template +constexpr VTable const& select(VTable<_t>> const& vtbl) noexcept { + return vtbl; +} + +template +constexpr VTable const& select(BasePtr<_t>> const& base) noexcept { + return *base.vptr_; +} + +struct PolyAccess { + template + static auto call(This&& _this, As&&... args) + -> decltype(static_cast(_this).template _polyCall_( + static_cast(args)...)) { + static_assert( + !IsInstanceOf, Poly>::value, + "When passing a Poly<> object to call(), you must explicitly " + "say which Interface to dispatch to, as in " + "call<0, MyInterface>(self, args...)"); + return static_cast(_this).template _polyCall_( + static_cast(args)...); + } + + template + using Iface = typename Uncvref::_polyInterface_; + + template + static typename Uncvref::template _polySelf_ self_(); + + template > + static decltype(auto) cast(Poly&& _this) { + using Ret = AddCvrefOf, Poly&&>; + return static_cast( + *static_cast>(_this.vptr_->ops_( + Op::eAddr, + const_cast(static_cast(&_this)), + const_cast(static_cast(&typeid(T)))))); + } + + template + static decltype(auto) root(Poly&& _this) noexcept { + return static_cast(_this)._polyRoot_(); + } + + template + static std::type_info const& type(PolyRoot const& _this) noexcept { + return *static_cast( + _this.vptr_->ops_(Op::eType, nullptr, nullptr)); + } + + template + static VTable> const* vtable(PolyRoot const& _this) noexcept { + return _this.vptr_; + } + + template + static Data* data(PolyRoot& _this) noexcept { + return &_this; + } + + template + static Data const* data(PolyRoot const& _this) noexcept { + return &_this; + } + + template + static Poly move(PolyRoot const& _this) noexcept { + return Poly{_this, Type{}}; + } + + template + static Poly move(PolyRoot const& _this) noexcept { + return Poly{_this, Type{}}; + } +}; + +template +struct PolyNode : Tail { + private: + friend PolyAccess; + using Tail::Tail; + + template + decltype(auto) _polyCall_(As&&... as) { + return std::get(select(*PolyAccess::vtable(*this)))( + *PolyAccess::data(*this), static_cast(as)...); + } + template + decltype(auto) _polyCall_(As&&... as) const { + return std::get(select(*PolyAccess::vtable(*this)))( + *PolyAccess::data(*this), static_cast(as)...); + } +}; + +struct MakePolyNode { + template + using apply = InterfaceOf>; +}; + +template +struct PolyRoot : private PolyBase, private Data { + friend PolyAccess; + friend Poly; + friend PolyVal; + friend PolyRef; + template + using _polySelf_ = Poly, Node>>; + using _polyInterface_ = I; + + private: + PolyRoot& _polyRoot_() noexcept { + return *this; + } + PolyRoot const& _polyRoot_() const noexcept { + return *this; + } + VTable> const* vptr_ = vtable>(); +}; + +template +using PolyImpl = + TypeFold>, PolyRoot, MakePolyNode>; + +// A const-qualified function type means the user is trying to disambiguate +// a member function pointer. +template // Fun = R(As...) const +struct Sig { + template + constexpr Fun T::*operator()(Fun T::*t) const /* nolint */ volatile noexcept { + return t; + } + template + constexpr F T::*operator()(F T::*t) const /* nolint */ volatile noexcept { + return t; + } +}; + +// A functon type with no arguments means the user is trying to disambiguate +// a member function pointer. +template +struct Sig : Sig { + using Fun = R(); + using Sig::operator(); + + template + constexpr Fun T::*operator()(Fun T::*t) const noexcept { + return t; + } +}; + +template +struct SigImpl : Sig { + using Fun = R(As...); + using Sig::operator(); + + template + constexpr Fun T::*operator()(Fun T::*t) const noexcept { + return t; + } + constexpr Fun* operator()(Fun* t) const noexcept { + return t; + } + template + constexpr F* operator()(F* t) const noexcept { + return t; + } +}; + +// The user could be trying to disambiguate either a member or a free function. +template +struct Sig : SigImpl {}; + +// This case is like the one above, except we want to add an overload that +// handles the case of a free function where the first argument is more +// const-qualified than the user explicitly specified. +template +struct Sig : SigImpl { + using CCFun = R(A const&, As...); + using SigImpl::operator(); + + constexpr CCFun* operator()(CCFun* t) const /* nolint */ volatile noexcept { + return t; + } +}; + +template < + class T, + class I, + class U = std::decay_t, + std::enable_if_t>::value, int> = 0, + std::enable_if_t, T>::value, int> = + 0, + class = MembersOf, U>> +std::true_type modelsInterface_(int); +template +std::false_type modelsInterface_(long); + +template +struct ModelsInterface : decltype(modelsInterface_(0)) {}; + +template +struct ValueCompatible : std::is_base_of {}; + +// This prevents PolyRef's converting constructors and assignment operators +// from being considered as copy constructors and assignment operators: +template +struct ValueCompatible : std::false_type {}; + +template +struct ReferenceCompatible : std::is_constructible {}; + +// This prevents PolyRef's converting constructors and assignment operators +// from being considered as copy constructors and assignment operators: +template +struct ReferenceCompatible : std::false_type {}; + +} // namespace detail +/// \endcond +} // namespace folly + +#undef FOLLY_AUTO diff --git a/folly/detail/TypeList.h b/folly/detail/TypeList.h new file mode 100644 index 00000000..bf0b303d --- /dev/null +++ b/folly/detail/TypeList.h @@ -0,0 +1,551 @@ +/* + * Copyright 2017-present Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include + +/** + * \file TypeList.h + * \author Eric Niebler + * + * The file contains facilities for manipulating lists of types, and for + * defining and composing operations over types. + * + * The type-operations behave like compile-time functions: they accept types as + * input and produce types as output. A simple example is a template alias, like + * `std::add_pointer_t`. However, templates are not themselves first class + * citizens of the language; they cannot be easily "returned" from a + * metafunction, and passing them to a metafunction is awkward and often + * requires the user to help the C++ parser by adding hints like `typename` + * and `template` to disambiguate the syntax. That makes higher-ordered + * metaprogramming difficult. (There is no simple way to e.g., compose two + * template aliases and pass the result as an argument to another template.) + * + * Instead, we wrap template aliases in a ordinary class, which _can_ be passed + * and returned simply from metafunctions. This is like Boost.MPL's notion of a + * "metafunction class"[1], and we adopt that terminology here. + * + * For the Folly.TypeList library, a metafunction class is a protocol that + * all the components of Folly.TypeList expect and agree upon. It is a class + * type that has a nested template alias called `Apply`. So for instance, + * `std::add_pointer_t` as a Folly metafunction class would look like this: + * + * struct AddPointer { + * template + * using apply = T*; + * }; + * + * Folly.TypeList gives a simple way to "lift" an ordinary template alias into + * a metafunction class: `MetaQuote`. The above `AddPointer` could instead be + * written as: + * + * using AddPointer = folly::MetaQuote; + * + * \par Naming + * + * A word about naming. Components in Folly.TypeList fall into two buckets: + * utilities for manipulating lists of types, and utilities for manipulating + * metafunction classes. The former have names that start with `Type`, as in + * `TypeList` and `TypeTransform`. The latter have names that start with `Meta`, + * as in `MetaQuote` and `MetaApply`. + * + * [1] Boost.MPL Metafunction Class: + * http://www.boost.org/libs/mpl/doc/refmanual/metafunction-class.html + */ + +namespace folly { +namespace detail { + +/** + * Handy shortcuts for some standard facilities + */ +template +using Bool = std::integral_constant; +using True = std::true_type; +using False = std::false_type; + +/** + * Given a metafunction class `Fn` and arguments `Ts...`, invoke `Fn` + * with `Ts...`. + */ +template +using MetaApply = typename Fn::template apply; + +/** + * A list of types. + */ +template +struct TypeList { + /** + * An alias for this list of types + */ + using type = TypeList; + + /** + * \return the number of types in this list. + */ + static constexpr std::size_t size() noexcept { + return sizeof...(Ts); + } + + /** + * This list of types is also a metafunction class that accepts another + * metafunction class and invokes it with all the types in the list. + */ + template + using apply = MetaApply; +}; + +/** + * A wrapper for a type + */ +template +struct Type { + /** + * An alias for the wrapped type + */ + using type = T; + + /** + * This wrapper is a metafunction class that, when applied with any number + * of arguments, returns the wrapped type. + */ + template + using apply = T; +}; + +/** + * An empty struct. + */ +struct Empty {}; + +/// \cond +namespace impl { +template +struct If_ { + template + using apply = T; +}; +template <> +struct If_ { + template + using apply = U; +}; +} // namespace impl +/// \endcond + +/** + * Like std::conditional, but with fewer template instantiations + */ +template +using If = MetaApply, Then, Else>; + +/** + * Defers the evaluation of an alias. + * + * Given a template `C` and arguments `Ts...`, then + * - If `C` is well-formed, `MetaApply>` is well- + * formed and is an alias for `C`. + * - Otherwise, `MetaApply>` is ill-formed. + */ +template