2 * Copyright 2017-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
17 #pragma message "Folly.Poly requires gcc-5 or greater"
19 #include <folly/Poly.h>
21 #include <folly/Conv.h>
22 #include <folly/poly/Nullable.h>
23 #include <folly/poly/Regular.h>
24 #include <folly/portability/GTest.h>
28 using namespace folly;
29 using namespace folly::poly;
34 std::array<char, sizeof(Poly<ISemiRegular>) + 1> data_;
38 Big() : data_{}, i_(0) {
41 explicit Big(int i) : data_{}, i_(i) {
44 Big(Big const& that) : data_(that.data_), i_(that.i_) {
50 Big& operator=(Big const&) = default;
54 friend bool operator==(Big const& a, Big const& b) {
55 return a.value() == b.value();
57 friend bool operator!=(Big const& a, Big const& b) {
60 static std::ptrdiff_t s_count;
62 std::ptrdiff_t Big::s_count = 0;
65 TEST(Poly, SemiRegular) {
67 // A small object, storable in-situ:
68 Poly<ISemiRegular> p = 42;
69 EXPECT_EQ(typeid(int), poly_type(p));
70 EXPECT_EQ(42, poly_cast<int>(p));
71 EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
72 Poly<ISemiRegular> p2 = p;
73 EXPECT_EQ(typeid(int), poly_type(p2));
74 EXPECT_EQ(42, poly_cast<int>(p2));
75 EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
78 EXPECT_EQ(0, Big::s_count);
80 // A big object, stored on the heap:
81 Poly<ISemiRegular> p = Big(42);
82 EXPECT_EQ(1, Big::s_count);
83 EXPECT_EQ(typeid(Big), poly_type(p));
84 EXPECT_EQ(42, poly_cast<Big>(p).value());
85 EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
86 Poly<ISemiRegular> p2 = p;
87 EXPECT_EQ(2, Big::s_count);
88 EXPECT_EQ(typeid(Big), poly_type(p2));
89 EXPECT_EQ(42, poly_cast<Big>(p2).value());
90 EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
92 EXPECT_EQ(0, Big::s_count);
95 TEST(Poly, SemiRegularReference) {
97 Poly<ISemiRegular&> p = i;
99 EXPECT_EQ(typeid(int), poly_type(p));
100 EXPECT_EQ(42, poly_cast<int>(p));
101 EXPECT_EQ(&i, &poly_cast<int>(p));
102 EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
103 // Can't default-initialize reference-like Poly's:
104 static_assert(!std::is_default_constructible<Poly<ISemiRegular&>>::value, "");
107 TEST(Poly, Conversions) {
109 Poly<ISemiRegular> p1 = i;
110 Poly<ISemiRegular&> p2 = p1;
111 EXPECT_EQ(&poly_cast<int>(p1), &poly_cast<int>(p2));
112 Poly<ISemiRegular const&> p3 = p1;
113 Poly<ISemiRegular const&> p4 = p2;
114 EXPECT_EQ(&poly_cast<int>(p3), &poly_cast<int>(p1));
115 EXPECT_EQ(&poly_cast<int>(p4), &poly_cast<int>(p1));
117 !std::is_constructible<Poly<ISemiRegular&>, Poly<ISemiRegular const&>&>::
121 !std::is_constructible<Poly<ISemiRegular>, Poly<ISemiRegular const&>&>::
126 TEST(Poly, EqualityComparableReference) {
129 Poly<IEqualityComparable&> p1 = i;
130 Poly<IEqualityComparable&> p2 = j;
131 EXPECT_EQ(&i, &poly_cast<int>(p1));
132 EXPECT_EQ(&j, &poly_cast<int>(p2));
133 EXPECT_TRUE(p1 == p2);
134 EXPECT_FALSE(p1 != p2);
136 EXPECT_FALSE(p1 == p2);
137 EXPECT_TRUE(p1 != p2);
138 EXPECT_EQ(42, poly_cast<int>(p1));
139 EXPECT_EQ(43, poly_cast<int>(p2));
144 template <class Base>
145 struct Interface : Base {
147 folly::poly_call<0>(*this, i);
152 using Members = FOLLY_POLY_MEMBERS(&T::foo);
157 explicit foo_(int i) : j_(i) {}
167 TEST(Poly, Singular) {
168 Poly<Foo> p = foo_{42};
172 EXPECT_EQ(typeid(foo_), poly_type(p));
176 struct FooBar : PolyExtends<Foo> {
177 template <class Base>
178 struct Interface : Base {
179 std::string bar(int i) const {
180 return folly::poly_call<0>(*this, i);
185 using Members = FOLLY_POLY_MEMBERS(&T::bar);
190 explicit foo_bar(int i) : j_(i) {}
194 std::string bar(int i) const {
196 return folly::to<std::string>(i);
204 TEST(Poly, SingleInheritance) {
205 Poly<FooBar> p = foo_bar{42};
209 EXPECT_EQ("43", p.bar(1));
210 EXPECT_EQ(typeid(foo_bar), poly_type(p));
212 Poly<Foo> q = p; // OK, conversion works.
219 const_cast<Poly<Foo&> const&>(r)->foo(i);
222 Poly<FooBar const&> cr = p;
223 // cr->foo(i); // ERROR: calls a non-const member through a const reference
229 template <class Base>
230 struct Interface : Base {
231 std::string baz(int i, int j) const {
232 return folly::poly_call<0>(*this, i, j);
237 using Members = FOLLY_POLY_MEMBERS(&T::baz);
240 struct FooBarBazFizz : PolyExtends<FooBar, Baz> {
241 template <class Base>
242 struct Interface : Base {
243 std::string fizz() const {
244 return folly::poly_call<0>(*this);
249 using Members = FOLLY_POLY_MEMBERS(&T::fizz);
252 struct foo_bar_baz_fizz {
253 foo_bar_baz_fizz() = default;
254 explicit foo_bar_baz_fizz(int i) : j_(i) {}
258 std::string bar(int i) const {
259 return folly::to<std::string>(i + j_);
261 std::string baz(int i, int j) const {
262 return folly::to<std::string>(i + j);
264 std::string fizz() const {
273 TEST(Poly, MultipleInheritance) {
274 Poly<FooBarBazFizz> p = foo_bar_baz_fizz{42};
278 EXPECT_EQ("43", p.bar(1));
279 EXPECT_EQ("3", p.baz(1, 2));
280 EXPECT_EQ("fizz", p.fizz());
281 EXPECT_EQ(typeid(foo_bar_baz_fizz), poly_type(p));
286 template <class Base>
287 struct Interface : Base {
289 return folly::poly_call<0>(*this);
292 folly::poly_call<1>(*this, i);
297 using Members = FOLLY_POLY_MEMBERS(
298 FOLLY_POLY_MEMBER(int() const, &T::prop),
299 FOLLY_POLY_MEMBER(void(int), &T::prop));
303 property() = default;
304 explicit property(int i) : j(i) {}
317 TEST(Poly, OverloadedMembers) {
318 Poly<Property> p = property{42};
319 EXPECT_EQ(typeid(property), poly_type(p));
320 EXPECT_EQ(42, p.prop());
322 EXPECT_EQ(68, p.prop());
325 TEST(Poly, NullablePointer) {
326 Poly<INullablePointer> p = nullptr;
327 Poly<INullablePointer> q{};
328 EXPECT_EQ(typeid(void), poly_type(p));
329 EXPECT_TRUE(poly_empty(p));
331 EXPECT_FALSE(p != q);
332 EXPECT_TRUE(p == nullptr);
333 EXPECT_TRUE(nullptr == p);
334 EXPECT_FALSE(p != nullptr);
335 EXPECT_FALSE(nullptr != p);
337 // No null references ever.
338 Poly<INullablePointer> r = 42;
339 Poly<INullablePointer&> s = r;
340 static_assert(!poly_empty(s), "");
341 EXPECT_THROW(Poly<INullablePointer&> r(q), BadPolyAccess);
346 MoveOnly_() = default;
347 MoveOnly_(MoveOnly_&&) = default;
348 MoveOnly_(MoveOnly_ const&) = delete;
349 MoveOnly_& operator=(MoveOnly_&&) = default;
350 MoveOnly_& operator=(MoveOnly_ const&) = delete;
357 Poly<IMoveOnly&> p = i;
359 !std::is_convertible<Poly<IMoveOnly&>, Poly<IMoveOnly&&>>::value, "");
360 auto q = poly_move(p);
361 static_assert(std::is_same<decltype(q), Poly<IMoveOnly&&>>::value, "");
362 EXPECT_EQ(&poly_cast<int>(p), &poly_cast<int>(q));
366 Poly<ISemiRegular const&> p = i;
367 auto q = poly_move(p);
369 std::is_same<decltype(q), Poly<ISemiRegular const&>>::value, "");
370 EXPECT_EQ(&poly_cast<int>(p), &poly_cast<int>(q));
373 Poly<IMoveOnly> p = MoveOnly_{};
374 static_assert(!std::is_copy_constructible<Poly<IMoveOnly>>::value, "");
375 auto q = poly_move(p);
376 static_assert(std::is_same<decltype(q), Poly<IMoveOnly>>::value, "");
380 TEST(Poly, RValueRef) {
382 Poly<ISemiRegular&&> p = std::move(i);
383 static_assert(std::is_same<decltype(poly_cast<int>(p)), int&>::value, "");
384 EXPECT_EQ(&i, &poly_cast<int>(p));
391 template <class R, class... As>
392 struct IFunction<R(As...)> {
393 template <class Base>
394 struct Interface : Base {
395 R operator()(As... as) const {
396 return static_cast<R>(
397 folly::poly_call<0>(*this, std::forward<As>(as)...));
403 FOLLY_POLY_MEMBERS(FOLLY_POLY_MEMBER(R(As...) const, &T::operator()));
407 using Function = Poly<IFunction<Fun>>;
410 TEST(Poly, Function) {
411 Function<int(int, int)> fun = std::plus<int>{};
412 EXPECT_EQ(42, fun(22, 20));
413 fun = std::multiplies<int>{};
414 EXPECT_EQ(22 * 20, fun(22, 20));
418 // This multiply extends IEqualityComparable
419 struct IDiamond : PolyExtends<IRegular, INullablePointer> {};
422 TEST(Poly, DiamondInheritance) {
424 // A small object, storable in-situ:
425 Poly<IDiamond> p = 42;
426 EXPECT_EQ(typeid(int), poly_type(p));
427 EXPECT_EQ(42, poly_cast<int>(p));
428 EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
429 Poly<IDiamond> p2 = p;
430 EXPECT_EQ(typeid(int), poly_type(p2));
431 EXPECT_EQ(42, poly_cast<int>(p2));
432 EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
433 Poly<IEqualityComparable&> eq = p;
434 EXPECT_EQ(&poly_cast<int>(p), &poly_cast<int>(eq));
437 EXPECT_EQ(0, Big::s_count);
439 // A big object, stored on the heap:
440 Poly<IDiamond> p = Big(42);
441 EXPECT_EQ(1, Big::s_count);
442 EXPECT_EQ(typeid(Big), poly_type(p));
443 EXPECT_EQ(42, poly_cast<Big>(p).value());
444 EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
445 Poly<IDiamond> p2 = p;
446 EXPECT_EQ(2, Big::s_count);
447 EXPECT_EQ(typeid(Big), poly_type(p2));
448 EXPECT_EQ(42, poly_cast<Big>(p2).value());
449 EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
450 Poly<IEqualityComparable&> eq = p;
451 EXPECT_EQ(&poly_cast<Big>(p), &poly_cast<Big>(eq));
453 EXPECT_EQ(0, Big::s_count);
458 int property() const {
461 void property(int) {}
463 struct Struct2 : Struct {
476 int property(Struct const&) {
479 void property(Struct&, int) {}
488 int purr(Struct2 const&) {
495 auto m0 = folly::sig<int() const>(&Struct::property);
496 EXPECT_EQ(static_cast<int (Struct::*)() const>(&Struct::property), m0);
497 auto m1 = folly::sig<int()>(&Struct::property);
498 EXPECT_EQ(static_cast<int (Struct::*)() const>(&Struct::property), m1);
500 auto m2 = folly::sig<int() const>(&Struct2::property);
501 EXPECT_EQ(static_cast<int (Struct2::*)() const>(&Struct2::property), m2);
502 auto m3 = folly::sig<int()>(&Struct2::property);
503 EXPECT_EQ(static_cast<int (Struct2::*)() const>(&Struct2::property), m3);
505 auto m4 = folly::sig<long()>(&Struct2::meow);
506 EXPECT_EQ(&Struct2::meow, m4);
508 auto m5 = folly::sig<int()>(&Struct2::purr);
509 EXPECT_EQ(static_cast<int (Struct2::*)()>(&Struct2::purr), m5);
510 auto m6 = folly::sig<int() const>(&Struct2::purr);
511 EXPECT_EQ(static_cast<int (Struct2::*)() const>(&Struct2::purr), m6);
514 auto m0 = folly::sig<int(Struct const&)>(&::property);
515 EXPECT_EQ(static_cast<int (*)(Struct const&)>(&::property), m0);
516 auto m1 = folly::sig<int(Struct&)>(&::property);
517 EXPECT_EQ(static_cast<int (*)(Struct const&)>(&::property), m1);
519 auto m2 = folly::sig<long(Struct2&)>(&::meow);
520 EXPECT_EQ(&::meow, m2);
522 auto m3 = folly::sig<int(Struct2&)>(&::purr);
523 EXPECT_EQ(static_cast<int (*)(Struct2&)>(&::purr), m3);
524 auto m4 = folly::sig<int(Struct2 const&)>(&::purr);
525 EXPECT_EQ(static_cast<int (*)(Struct2 const&)>(&::purr), m4);
531 template <class Base>
532 struct Interface : Base {
533 friend PolySelf<Base, PolyDecay> operator+(
534 PolySelf<Base> const& a,
535 PolySelf<Base> const& b) {
536 return folly::poly_call<0, IAddable>(a, b);
540 static auto plus_(T const& a, T const& b) -> decltype(a + b) {
545 using Members = FOLLY_POLY_MEMBERS(&plus_<std::decay_t<T>>);
549 TEST(Poly, Addable) {
550 Poly<IAddable> a = 2, b = 3;
551 Poly<IAddable> c = a + b;
552 EXPECT_EQ(typeid(int), poly_type(c));
553 EXPECT_EQ(5, poly_cast<int>(c));
555 Poly<IAddable const&> aref = a, bref = b;
556 auto cc = aref + bref;
557 static_assert(std::is_same<decltype(cc), Poly<IAddable>>::value, "");
558 EXPECT_EQ(typeid(int), poly_type(cc));
559 EXPECT_EQ(5, poly_cast<int>(cc));
561 EXPECT_EQ(5, poly_cast<int>(cc));
563 EXPECT_EQ(6, poly_cast<int>(cc));
567 struct IFrobnicator {
568 template <class Base>
569 struct Interface : Base {
570 void frobnicate(folly::Poly<folly::poly::IRegular&> x) {
571 folly::poly_call<0>(*this, x);
575 using Members = FOLLY_POLY_MEMBERS(&T::frobnicate);
577 using Frobnicator = folly::Poly<IFrobnicator>;
579 struct my_frobnicator {
580 void frobnicate(folly::Poly<folly::poly::IRegular&>) {
586 TEST(Poly, PolyRefAsArg) {
587 folly::Poly<folly::poly::IRegular> x = 42;
588 Frobnicator frob = my_frobnicator{};
592 frob.frobnicate(folly::Poly<folly::poly::IRegular&>(x));