Commit 18882128 authored by Eric Niebler's avatar Eric Niebler Committed by Facebook Github Bot

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<AbstractBase>`; 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<ICallable<void(int)>>`. )

== 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 <class Fun>
  struct IFunction;

  template <class R, class... As>
  struct IFunction<R(As...)> {
    // An interface is defined as a nested class template called
    // Interface that takes a single template parameter, Base, from
    // which it inherits.
    template <class Base>
    struct Interface : Base {
      // The Interface has public member functions. These become the
      // public interface of the resulting Poly instantiation.
      // (Implementation note: Poly<IFunction<Sig>> 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<N>.
        // 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<R>(
            folly::poly_call<0>(*this, std::forward<As>(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 <class T>
    //   using Members =
    //       folly::PolyMembers<folly::sig<R(As...) const>(&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 <class T> using Members = folly::PolyMembers<&T::operator()>;
    //
    template <class T>
    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 <class Fun>
  using Function = Poly<IFunction<Fun>>;

Given the above definition of `Function`, users can now initialize instances
of (say) `Function<int(int, int)>` with function objects like
`std::plus<int>` and `std::multiplies<int>`, as below:

  lang=c++
  Function<int(int, int)> fun = std::plus<int>{};
  assert(5 == fun(2, 3));
  fun = std::multiplies<int>{};
  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 <class Value>
  struct IJavaIterator {
    template <class Base>
    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 <class T>
    using Members = folly::PolyMembers<&T::Done, &T::Current, &T::Next>;
  };

  template <class Value>
  using JavaIterator = Poly<IJavaIterator>;

Given the above definition, `JavaIterator<int>` 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 <class Base>
    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 <class T>
    using Members = folly::PolyMembers<
      folly::sig<int() const>(&T::Value),
      folly::sig<void(int)>(&T::Value)>;
  };

  using IntProperty = Poly<IIntProperty>;

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 <class Value>
  struct IJavaIterator {
    template <class Base>
    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 <class T>
    using Members = FOLLY_POLY_MEMBERS(&T::Done, &T::Current, &T::Next);
  };

  template <class Value>
  using JavaIterator = Poly<IJavaIterator>;

and

  lang=c++
  struct IIntProperty {
    template <class Base>
    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 <class T>
    using Members = FOLLY_POLY_MEMBERS(
      FOLLY_POLY_MEMBER(int() const, &T::Value),
      FOLLY_POLY_MEMBER(void(int), &T::Value));
  };

  using IntProperty = Poly<IIntProperty>;

== 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 <class Base>
    struct Interface : Base {
      void Foo() const { return folly::poly_call<0>(*this); }
    };
    template <class T>
    using Members = FOLLY_POLY_MEMBERS(&T::Foo);
  };

  // The IFooBar interface extends the IFoo interface
  struct IFooBar : PolyExtends<IFoo> {
    template <class Base>
    struct Interface : Base {
      void Bar() const { return folly::poly_call<0>(*this); }
    };
    template <class T>
    using Members = FOLLY_POLY_MEMBERS(&T::Bar);
  };

  using FooBar = Poly<IFooBar>;

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<IDerived> derived = ...;
  Poly<IBase> 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<IRegular &> intRef = i;

  assert(42 == folly::poly_cast<int>(intRef));
  // Assert that we captured the address of "i":
  assert(&i == &folly::poly_cast<int>(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 <class Base>
    struct Interface {
      void Foo() { folly::poly_call<0>(*this); }
    };
    template <class T>
    using Members = folly::PolyMembers<&T::Foo>;
  };

  struct SomeFoo {
    void Foo() { std::printf("SomeFoo::Foo\n"); }
  };

  SomeFoo foo;
  Poly<IFoo &> 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<IRegular> value = 42;
  Poly<IRegular &> mutable_ref = value;
  Poly<IRegular const &> const_ref = mutable_ref;

  assert(&poly_cast<int>(value) == &poly_cast<int>(mutable_ref));
  assert(&poly_cast<int>(value) == &poly_cast<int>(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 <class Base>
    struct Interface : Base {
      bool operator!() const { return folly::poly_call<0>(*this); }
    };
    template <class T>
    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<ILogicallyNegatable>` 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 <class Base>
    struct Interface : Base {
      bool operator!() const { return folly::poly_call<0>(*this); }
    };
    template <class T>
    static auto negate(T const& t) -> decltype(!t) { return !t; }
    template <class T>
    using Members = FOLLY_POLY_MEMBERS(&negate<T>);
  };

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 <class Base>
    struct Interface : Base {
      friend Self<Base>
      operator+(Self<Base> const& a, Self<Base> const& b) const {
        return folly::poly_call<0>(a, b);
      }
    };
    template <class T>
    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<IAddable> a = 2, b = 3;
  Poly<IAddable> c = a + b;
  assert(poly_cast<int>(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
parent cda07daf
......@@ -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 \
......
/*
* 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 <class I>
inline PolyVal<I>::PolyVal(PolyVal&& that) noexcept {
that.vptr_->ops_(Op::eMove, &that, static_cast<Data*>(this));
vptr_ = std::exchange(that.vptr_, vtable<I>());
}
template <class I>
inline PolyVal<I>::PolyVal(PolyOrNonesuch const& that) {
that.vptr_->ops_(
Op::eCopy, const_cast<Data*>(that._data_()), PolyAccess::data(*this));
vptr_ = that.vptr_;
}
template <class I>
inline PolyVal<I>::~PolyVal() {
vptr_->ops_(Op::eNuke, this, nullptr);
}
template <class I>
inline Poly<I>& PolyVal<I>::operator=(PolyVal that) noexcept {
vptr_->ops_(Op::eNuke, _data_(), nullptr);
that.vptr_->ops_(Op::eMove, that._data_(), _data_());
vptr_ = std::exchange(that.vptr_, vtable<I>());
return static_cast<Poly<I>&>(*this);
}
template <class I>
template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>
inline PolyVal<I>::PolyVal(T&& t) {
using U = std::decay_t<T>;
static_assert(
std::is_copy_constructible<U>::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<std::decay<T>>) ||
!"Dynamic and static exception types don't match. Object would "
"be sliced when storing in Poly.");
if (inSitu<U>()) {
::new (static_cast<void*>(&_data_()->buff_)) U(static_cast<T&&>(t));
} else {
_data_()->pobj_ = new U(static_cast<T&&>(t));
}
vptr_ = vtableFor<I, U>();
}
template <class I>
template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int>>
inline PolyVal<I>::PolyVal(Poly<I2> that) {
static_assert(
!Copyable::value || std::is_copy_constructible<Poly<I2>>::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<I>(*std::exchange(that_vptr, vtable<std::decay_t<I2>>()));
}
}
template <class I>
template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>
inline Poly<I>& PolyVal<I>::operator=(T&& t) {
*this = PolyVal(static_cast<T&&>(t));
return static_cast<Poly<I>&>(*this);
}
template <class I>
template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int>>
inline Poly<I>& PolyVal<I>::operator=(Poly<I2> that) {
*this = PolyVal(std::move(that));
return static_cast<Poly<I>&>(*this);
}
template <class I>
inline void PolyVal<I>::swap(Poly<I>& 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 <class I>
inline AddCvrefOf<PolyRoot<I>, I>& PolyRef<I>::_polyRoot_() const noexcept {
return const_cast<AddCvrefOf<PolyRoot<I>, I>&>(
static_cast<PolyRoot<I> const&>(*this));
}
template <class I>
constexpr RefType PolyRef<I>::refType() noexcept {
using J = std::remove_reference_t<I>;
return std::is_rvalue_reference<I>::value
? RefType::eRvalue
: std::is_const<J>::value ? RefType::eConstLvalue : RefType::eLvalue;
}
template <class I>
template <class That, class I2>
inline PolyRef<I>::PolyRef(That&& that, Type<I2>) {
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<void*>(static_cast<void const*>(&that_data->buff_))
: that_data->pobj_;
this->vptr_ = &select<std::decay_t<I>>(
*static_cast<VTable<std::decay_t<I2>> const*>(that_vptr->ops_(
Op::eRefr, nullptr, reinterpret_cast<void*>(refType()))));
}
template <class I>
inline PolyRef<I>::PolyRef(PolyRef const& that) noexcept {
_data_()->pobj_ = that._data_()->pobj_;
this->vptr_ = that.vptr_;
}
template <class I>
inline Poly<I>& PolyRef<I>::operator=(PolyRef const& that) noexcept {
_data_()->pobj_ = that._data_()->pobj_;
this->vptr_ = that.vptr_;
return static_cast<Poly<I>&>(*this);
}
template <class I>
template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>
inline PolyRef<I>::PolyRef(T&& t) noexcept {
_data_()->pobj_ =
const_cast<void*>(static_cast<void const*>(std::addressof(t)));
this->vptr_ = vtableFor<std::decay_t<I>, AddCvrefOf<std::decay_t<T>, I>>();
}
template <class I>
template <
class I2,
std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int>>
inline PolyRef<I>::PolyRef(Poly<I2>&& that) noexcept(
std::is_reference<I2>::value)
: PolyRef{that, Type<I2>{}} {
static_assert(
Disjunction<std::is_reference<I2>, std::is_rvalue_reference<I>>::value,
"Attempting to construct a Poly that is a reference to a temporary. "
"This is probably a mistake.");
}
template <class I>
template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>
inline Poly<I>& PolyRef<I>::operator=(T&& t) noexcept {
*this = PolyRef(static_cast<T&&>(t));
return static_cast<Poly<I>&>(*this);
}
template <class I>
template <
class I2,
std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int>>
inline Poly<I>& PolyRef<I>::operator=(Poly<I2>&& that) noexcept(
std::is_reference<I2>::value) {
*this = PolyRef(std::move(that));
return static_cast<Poly<I>&>(*this);
}
template <class I>
template <
class I2,
std::enable_if_t<ReferenceCompatible<I, I2, I2&>::value, int>>
inline Poly<I>& PolyRef<I>::operator=(Poly<I2>& that) noexcept(
std::is_reference<I2>::value) {
*this = PolyRef(that);
return static_cast<Poly<I>&>(*this);
}
template <class I>
template <
class I2,
std::enable_if_t<ReferenceCompatible<I, I2, I2 const&>::value, int>>
inline Poly<I>& PolyRef<I>::operator=(Poly<I2> const& that) noexcept(
std::is_reference<I2>::value) {
*this = PolyRef(that);
return static_cast<Poly<I>&>(*this);
}
template <class I>
inline void PolyRef<I>::swap(Poly<I>& that) noexcept {
std::swap(_data_()->pobj_, that._data_()->pobj_);
std::swap(this->vptr_, that.vptr_);
}
template <class I>
inline AddCvrefOf<PolyImpl<I>, I>& PolyRef<I>::get() const noexcept {
return const_cast<AddCvrefOf<PolyImpl<I>, I>&>(
static_cast<PolyImpl<I> const&>(*this));
}
} // namespace detail
} // namespace folly
/*
* 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 <folly/Poly.h>
namespace folly {
namespace detail {
[[noreturn]] void throwBadPolyAccess() { throw BadPolyAccess(); }
[[noreturn]] void throwBadPolyCast() { throw BadPolyCast(); }
} // namespace detail
} // namespace folly
This diff is collapsed.
......@@ -312,8 +312,9 @@ using is_trivially_copyable = std::is_trivially_copyable<T>;
} // namespace traits_detail
struct Ignore {
Ignore() = default;
template <class T>
/* implicit */ Ignore(const T&) {}
constexpr /* implicit */ Ignore(const T&) {}
template <class T>
const Ignore& operator=(T const&) const { return *this; }
};
......
......@@ -310,4 +310,11 @@ class MoveOnly {
} // namespace moveonly_
using MoveOnly = moveonly_::MoveOnly;
/**
* A pithy alias for std::integral_constant<bool, B>.
*/
template <bool B>
using Bool = std::integral_constant<bool, B>;
} // namespace folly
This diff is collapsed.
This diff is collapsed.
......@@ -163,6 +163,11 @@ chain several queues together with processing steps in between.
A highly specialized data structure consisting of a pointer, a 1-bit
spin lock, and a 15-bit integral, all inside one 64-bit word.
#### [`Poly.h`](Poly.md)
A class template that makes it relatively easy to define a type-erasing
polymorphic object wrapper.
#### `Preprocessor.h`
Necessarily evil stuff.
......
This diff is collapsed.
/*
* 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 <folly/Poly.h>
#include <folly/poly/Regular.h>
namespace folly {
namespace poly {
/**
* A `Poly` interface that can be used to make Poly objects initializable from
* `nullptr` (to create an empty `Poly`) and equality comparable to `nullptr`
* (to test for emptiness).
*/
struct INullablePointer : PolyExtends<IEqualityComparable> {
template <class Base>
struct Interface : Base {
Interface() = default;
using Base::Base;
/* implicit */ Interface(std::nullptr_t) : Base{} {
static_assert(
std::is_default_constructible<PolySelf<Base>>::value,
"Cannot initialize a non-default constructible Poly with nullptr");
}
PolySelf<Base>& operator=(std::nullptr_t) {
static_assert(
std::is_default_constructible<PolySelf<Base>>::value,
"Cannot initialize a non-default constructible Poly with nullptr");
auto& self = static_cast<PolySelf<Base>&>(*this);
self = PolySelf<Base>();
return self;
}
friend bool operator==(
std::nullptr_t,
PolySelf<Base> const& self) noexcept {
return poly_empty(self);
}
friend bool operator==(
PolySelf<Base> const& self,
std::nullptr_t) noexcept {
return poly_empty(self);
}
friend bool operator!=(
std::nullptr_t,
PolySelf<Base> const& self) noexcept {
return !poly_empty(self);
}
friend bool operator!=(
PolySelf<Base> const& self,
std::nullptr_t) noexcept {
return !poly_empty(self);
}
};
};
/**
* A `Poly` interface that can be used to make `Poly` objects contextually
* convertible to `bool` (`true` if and only if non-empty). It also gives
* `Poly` objects a unary logical negation operator.
*/
struct IBooleanTestable : PolyExtends<> {
template <class Base>
struct Interface : Base {
constexpr bool operator!() const noexcept {
return poly_empty(*this);
}
constexpr explicit operator bool() const noexcept {
return !!*this;
}
};
};
} // namespace poly
} // namespace folly
/*
* 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 <folly/Poly.h>
namespace folly {
namespace poly {
/**
* A `Poly` interface for types that are equality comparable.
*/
struct IEqualityComparable : PolyExtends<> {
template <class T>
static auto isEqual_(T const& _this, T const& that)
-> decltype(std::declval<bool (&)(bool)>()(_this == that)) {
return _this == that;
}
template <class T>
using Members = FOLLY_POLY_MEMBERS(&isEqual_<T>);
};
} // namespace poly
/// \cond
namespace detail {
template <class I1, class I2>
using Comparable = Conjunction<
std::is_same<std::decay_t<I1>, std::decay_t<I2>>,
std::is_base_of<poly::IEqualityComparable, std::decay_t<I1>>>;
} // namespace detail
/// \endcond
template <
class I1,
class I2,
std::enable_if_t<detail::Comparable<I1, I2>::value, int> = 0>
bool operator==(Poly<I1> const& _this, Poly<I2> const& that) {
return (poly_empty(_this) && poly_empty(that)) ||
(poly_type(_this) == poly_type(that) &&
::folly::poly_call<0, poly::IEqualityComparable>(_this, that));
}
template <
class I1,
class I2,
std::enable_if_t<detail::Comparable<I1, I2>::value, int> = 0>
bool operator!=(Poly<I1> const& _this, Poly<I2> const& that) {
return !(_this == that);
}
namespace poly {
/**
* A `Poly` interface for types that are move-only.
*/
struct IMoveOnly : PolyExtends<> {
template <class Base>
struct Interface : Base {
Interface() = default;
Interface(Interface const&) = delete;
Interface(Interface&&) = default;
Interface& operator=(Interface const&) = delete;
Interface& operator=(Interface&&) = default;
using Base::Base;
};
};
/**
* A `Poly` interface for types that are copyable and movable.
*/
struct ISemiRegular : PolyExtends<> {};
/**
* A `Poly` interface for types that are copyable, movable, and equality
* comparable.
*/
struct IRegular : PolyExtends<ISemiRegular, IEqualityComparable> {};
} // namespace poly
} // namespace folly
......@@ -223,6 +223,10 @@ fingerprint_test_SOURCES = FingerprintTest.cpp
fingerprint_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la
TESTS += fingerprint_test
poly_test_SOURCES = PolyTest.cpp
poly_test_LDADD = libfollytestmain.la
TESTS += poly_test
portability_test_SOURCES = PortabilityTest.cpp
portability_test_LDADD = libfollytestmain.la
TESTS += portability_test
......@@ -268,6 +272,10 @@ try_test_SOURCES = TryTest.cpp
try_test_LDADD = libfollytestmain.la
TESTS += try_test
typelist_test_SOURCES = TypeListTest.cpp
typelist_test_LDADD = libfollytestmain.la
TESTS += typelist_test
uncaught_exceptions_test_SOURCES = UncaughtExceptionsTest.cpp
uncaught_exceptions_test_LDADD = libfollytestmain.la
TESTS += uncaught_exceptions_test
......
This diff is collapsed.
/*
* Copyright 2017 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 <folly/detail/TypeList.h>
#include <folly/portability/GTest.h>
using namespace folly;
using namespace detail;
namespace {
template <class T, class Ts, class = void>
struct IsApplicable_ : std::false_type {};
template <class T, class... Ts>
struct IsApplicable_<T, TypeList<Ts...>, void_t<MetaApply<T, Ts...>>>
: std::true_type {};
template <class T, class... Ts>
using IsApplicable = IsApplicable_<T, TypeList<Ts...>>;
} // namespace
TEST(TypeList, Basic) {
static_assert(TypeList<>::size() == 0, "");
static_assert(TypeList<int>::size() == 1, "");
static_assert(TypeList<int, short, float>::size() == 3, "");
}
template <class T>
using AddPointer = T*;
TEST(TypeList, Defer) {
static_assert(
std::is_same<MetaApply<MetaDefer<AddPointer, int>>, int*>::value, "");
static_assert(!IsApplicable<MetaDefer<AddPointer, int, short>>::value, "");
static_assert(!IsApplicable<MetaDefer<AddPointer, int&>>::value, "");
}
TEST(TypeList, Transform) {
using Fn = MetaQuote<AddPointer>;
using T1 = TypeTransform<TypeList<>, Fn>;
static_assert(std::is_same<T1, TypeList<>>::value, "");
using T2 = TypeTransform<TypeList<int>, Fn>;
static_assert(std::is_same<T2, TypeList<int*>>::value, "");
using T3 = TypeTransform<TypeList<int, short, void>, Fn>;
static_assert(std::is_same<T3, TypeList<int*, short*, void*>>::value, "");
}
using Nil = Empty;
template <class Car, class Cdr = Nil>
struct Cons {};
TEST(TypeList, Fold) {
using Fn = MetaQuote<Cons>;
using T1 = TypeFold<TypeList<int, short, void>, Nil, Fn>;
using E1 = Cons<int, Cons<short, Cons<void, Nil>>>;
static_assert(std::is_same<T1, E1>::value, "");
using T2 = TypeFold<TypeList<int, short, void, int*, short*, void*>, Nil, Fn>;
using E2 = Cons<
int,
Cons<short, Cons<void, Cons<int*, Cons<short*, Cons<void*, Nil>>>>>>;
static_assert(std::is_same<T2, E2>::value, "");
using T3 = TypeReverseFold<TypeList<int, short, void>, Nil, MetaFlip<Fn>>;
using E3 = Cons<void, Cons<short, Cons<int, Nil>>>;
static_assert(std::is_same<T3, E3>::value, "");
using T4 = TypeReverseFold<
TypeList<int, short, void, int*, short*, void*>,
Nil,
MetaFlip<Fn>>;
using E4 = Cons<
void*,
Cons<short*, Cons<int*, Cons<void, Cons<short, Cons<int, Nil>>>>>>;
static_assert(std::is_same<T4, E4>::value, "");
}
TEST(TypeList, Unique) {
using T1 = TypeUnique<TypeList<int, int, int, short, int, short>>;
static_assert(std::is_same<T1, TypeList<int, short>>::value, "");
using T2 = TypeReverseUnique<TypeList<int, int, int, short, int, short>>;
static_assert(std::is_same<T2, TypeList<short, int>>::value, "");
}
TEST(TypeList, PushFront) {
using T1 = TypePushFront<TypeList<>, int, short>;
static_assert(std::is_same<T1, TypeList<int, short>>::value, "");
using T2 = TypePushFront<T1, float, double, struct XXX>;
static_assert(
std::is_same<T2, TypeList<float, double, struct XXX, int, short>>::value,
"");
}
TEST(TypeList, PushBack) {
using T1 = TypePushBack<TypeList<>, int, short>;
static_assert(std::is_same<T1, TypeList<int, short>>::value, "");
using T2 = TypePushBack<T1, float, double, struct XXX>;
static_assert(
std::is_same<T2, TypeList<int, short, float, double, struct XXX>>::value,
"");
}
TEST(TypeList, Join) {
using T1 = TypeJoin<
TypeList<TypeList<int>, TypeList<short, float>, TypeList<void*>>>;
static_assert(
std::is_same<T1, TypeList<int, short, float, void*>>::value, "");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment