1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 #include <folly/Poly.h>
18 
19 #include <array>
20 
21 #include <folly/Conv.h>
22 #include <folly/CppAttributes.h>
23 #include <folly/poly/Nullable.h>
24 #include <folly/poly/Regular.h>
25 #include <folly/portability/GTest.h>
26 
27 using namespace folly;
28 using namespace folly::poly;
29 
30 namespace {
31 template <class T>
32 struct Big_t {
33  private:
34   std::array<char, sizeof(Poly<ISemiRegular>) + 1> data_;
35   T t_;
36 
37  public:
Big_t__anonfdb577f40111::Big_t38   Big_t() : data_{}, t_() { ++s_count; }
Big_t__anonfdb577f40111::Big_t39   explicit Big_t(T t) : data_{}, t_(t) { ++s_count; }
Big_t__anonfdb577f40111::Big_t40   Big_t(Big_t const& that) : data_(that.data_), t_(that.t_) { ++s_count; }
~Big_t__anonfdb577f40111::Big_t41   ~Big_t() { --s_count; }
42   Big_t& operator=(Big_t const&) = default;
value__anonfdb577f40111::Big_t43   T value() const { return t_; }
operator ==(Big_t const & a,Big_t const & b)44   friend bool operator==(Big_t const& a, Big_t const& b) {
45     return a.value() == b.value();
46   }
operator !=(Big_t const & a,Big_t const & b)47   friend bool operator!=(Big_t const& a, Big_t const& b) { return !(a == b); }
operator <(Big_t const & a,Big_t const & b)48   friend bool operator<(Big_t const& a, Big_t const& b) {
49     return a.value() < b.value();
50   }
51   static std::ptrdiff_t s_count;
52 };
53 template <class T>
54 std::ptrdiff_t Big_t<T>::s_count = 0;
55 
56 using Big = Big_t<int>;
57 using BigDouble = Big_t<double>;
58 } // namespace
59 
TEST(Poly,SemiRegular)60 TEST(Poly, SemiRegular) {
61   {
62     // A small object, storable in-situ:
63     Poly<ISemiRegular> p = 42;
64     EXPECT_EQ(typeid(int), poly_type(p));
65     EXPECT_EQ(42, poly_cast<int>(p));
66     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
67     Poly<ISemiRegular> p2 = p;
68     EXPECT_EQ(typeid(int), poly_type(p2));
69     EXPECT_EQ(42, poly_cast<int>(p2));
70     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
71   }
72 
73   EXPECT_EQ(0, Big::s_count);
74   {
75     // A big object, stored on the heap:
76     Poly<ISemiRegular> p = Big(42);
77     EXPECT_EQ(1, Big::s_count);
78     EXPECT_EQ(typeid(Big), poly_type(p));
79     EXPECT_EQ(42, poly_cast<Big>(p).value());
80     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
81     Poly<ISemiRegular> p2 = p;
82     EXPECT_EQ(2, Big::s_count);
83     EXPECT_EQ(typeid(Big), poly_type(p2));
84     EXPECT_EQ(42, poly_cast<Big>(p2).value());
85     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
86   }
87   EXPECT_EQ(0, Big::s_count);
88 
89   // four swap cases
90   //
91 
92   {
93     // A small object, storable in-situ:
94     Poly<ISemiRegular> p = 42;
95     EXPECT_EQ(typeid(int), poly_type(p));
96     EXPECT_EQ(42, poly_cast<int>(p));
97     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
98     // A small object, storable in-situ:
99     Poly<ISemiRegular> p2 = 4.2;
100     EXPECT_EQ(typeid(double), poly_type(p2));
101     EXPECT_EQ(4.2, poly_cast<double>(p2));
102     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
103     std::swap(p, p2);
104     EXPECT_EQ(typeid(double), poly_type(p));
105     EXPECT_EQ(4.2, poly_cast<double>(p));
106     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
107     EXPECT_EQ(typeid(int), poly_type(p2));
108     EXPECT_EQ(42, poly_cast<int>(p2));
109     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
110     using std::swap;
111     swap(p, p2);
112     EXPECT_EQ(typeid(int), poly_type(p));
113     EXPECT_EQ(42, poly_cast<int>(p));
114     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
115     EXPECT_EQ(typeid(double), poly_type(p2));
116     EXPECT_EQ(4.2, poly_cast<double>(p2));
117     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
118   }
119 
120   EXPECT_EQ(0, Big::s_count);
121   EXPECT_EQ(0, BigDouble::s_count);
122   {
123     // A big object, stored on the heap:
124     Poly<ISemiRegular> p = Big(42);
125     EXPECT_EQ(1, Big::s_count);
126     EXPECT_EQ(typeid(Big), poly_type(p));
127     EXPECT_EQ(42, poly_cast<Big>(p).value());
128     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
129     // A big object, stored on the heap:
130     Poly<ISemiRegular> p2 = BigDouble(4.2);
131     EXPECT_EQ(1, BigDouble::s_count);
132     EXPECT_EQ(typeid(BigDouble), poly_type(p2));
133     EXPECT_EQ(4.2, poly_cast<BigDouble>(p2).value());
134     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
135     std::swap(p, p2);
136     EXPECT_EQ(1, Big::s_count);
137     EXPECT_EQ(1, BigDouble::s_count);
138     EXPECT_EQ(typeid(BigDouble), poly_type(p));
139     EXPECT_EQ(4.2, poly_cast<BigDouble>(p).value());
140     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
141     EXPECT_EQ(typeid(Big), poly_type(p2));
142     EXPECT_EQ(42, poly_cast<Big>(p2).value());
143     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
144     using std::swap;
145     swap(p, p2);
146     EXPECT_EQ(1, Big::s_count);
147     EXPECT_EQ(1, BigDouble::s_count);
148     EXPECT_EQ(typeid(Big), poly_type(p));
149     EXPECT_EQ(42, poly_cast<Big>(p).value());
150     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
151     EXPECT_EQ(typeid(BigDouble), poly_type(p2));
152     EXPECT_EQ(4.2, poly_cast<BigDouble>(p2).value());
153     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
154   }
155   EXPECT_EQ(0, BigDouble::s_count);
156   EXPECT_EQ(0, Big::s_count);
157 
158   EXPECT_EQ(0, Big::s_count);
159   {
160     // A big object, stored on the heap:
161     Poly<ISemiRegular> p = Big(42);
162     EXPECT_EQ(1, Big::s_count);
163     EXPECT_EQ(typeid(Big), poly_type(p));
164     EXPECT_EQ(42, poly_cast<Big>(p).value());
165     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
166     // A small object, storable in-situ:
167     Poly<ISemiRegular> p2 = 4.2;
168     EXPECT_EQ(typeid(double), poly_type(p2));
169     EXPECT_EQ(4.2, poly_cast<double>(p2));
170     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
171     std::swap(p, p2);
172     EXPECT_EQ(1, Big::s_count);
173     EXPECT_EQ(typeid(double), poly_type(p));
174     EXPECT_EQ(4.2, poly_cast<double>(p));
175     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
176     EXPECT_EQ(typeid(Big), poly_type(p2));
177     EXPECT_EQ(42, poly_cast<Big>(p2).value());
178     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
179     using std::swap;
180     swap(p, p2);
181     EXPECT_EQ(1, Big::s_count);
182     EXPECT_EQ(typeid(Big), poly_type(p));
183     EXPECT_EQ(42, poly_cast<Big>(p).value());
184     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
185     EXPECT_EQ(typeid(double), poly_type(p2));
186     EXPECT_EQ(4.2, poly_cast<double>(p2));
187     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
188   }
189   EXPECT_EQ(0, Big::s_count);
190 
191   EXPECT_EQ(0, BigDouble::s_count);
192   {
193     // A small object, storable in-situ:
194     Poly<ISemiRegular> p = 42;
195     EXPECT_EQ(typeid(int), poly_type(p));
196     EXPECT_EQ(42, poly_cast<int>(p));
197     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
198     // A big object, stored on the heap:
199     Poly<ISemiRegular> p2 = BigDouble(4.2);
200     EXPECT_EQ(1, BigDouble::s_count);
201     EXPECT_EQ(typeid(BigDouble), poly_type(p2));
202     EXPECT_EQ(4.2, poly_cast<BigDouble>(p2).value());
203     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
204     std::swap(p, p2);
205     EXPECT_EQ(1, BigDouble::s_count);
206     EXPECT_EQ(typeid(BigDouble), poly_type(p));
207     EXPECT_EQ(4.2, poly_cast<BigDouble>(p).value());
208     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
209     EXPECT_EQ(typeid(int), poly_type(p2));
210     EXPECT_EQ(42, poly_cast<int>(p2));
211     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
212     using std::swap;
213     swap(p, p2);
214     EXPECT_EQ(1, BigDouble::s_count);
215     EXPECT_EQ(typeid(int), poly_type(p));
216     EXPECT_EQ(42, poly_cast<int>(p));
217     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
218     EXPECT_EQ(typeid(BigDouble), poly_type(p2));
219     EXPECT_EQ(4.2, poly_cast<BigDouble>(p2).value());
220     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
221   }
222   EXPECT_EQ(0, BigDouble::s_count);
223 }
224 
TEST(Poly,EqualityComparable)225 TEST(Poly, EqualityComparable) {
226   {
227     Poly<IEqualityComparable> p = 42;
228     Poly<IEqualityComparable> q = 42;
229     EXPECT_TRUE(p == q);
230     EXPECT_TRUE(q == p);
231     EXPECT_FALSE(p != q);
232     EXPECT_FALSE(q != p);
233     p = 43;
234     EXPECT_FALSE(p == q);
235     EXPECT_FALSE(q == p);
236     EXPECT_TRUE(p != q);
237     EXPECT_TRUE(q != p);
238   }
239   {
240     // empty not equal
241     Poly<IEqualityComparable> p;
242     Poly<IEqualityComparable> q = 42;
243     EXPECT_FALSE(p == q);
244     EXPECT_FALSE(q == p);
245   }
246   {
247     // empty equal
248     Poly<IEqualityComparable> p;
249     Poly<IEqualityComparable> q;
250     EXPECT_TRUE(p == q);
251     EXPECT_TRUE(q == p);
252   }
253   {
254     // mismatched types throws
255     Poly<IEqualityComparable> p = 4.2;
256     Poly<IEqualityComparable> q = 42;
257     EXPECT_THROW((void)(q == p), BadPolyCast);
258   }
259 }
260 
TEST(Poly,StrictlyOrderable)261 TEST(Poly, StrictlyOrderable) {
262   {
263     // A small object, storable in-situ:
264     Poly<IStrictlyOrderable> p = 42;
265     Poly<IStrictlyOrderable> q = 43;
266     EXPECT_TRUE(p < q);
267     EXPECT_TRUE(p <= q);
268     EXPECT_FALSE(p > q);
269     EXPECT_FALSE(p >= q);
270     EXPECT_TRUE(q > p);
271     EXPECT_TRUE(q >= p);
272     EXPECT_FALSE(q < p);
273     EXPECT_FALSE(q <= p);
274   }
275   {
276     // A big object, stored on the heap:
277     Poly<IStrictlyOrderable> p = Big(42);
278     Poly<IStrictlyOrderable> q = Big(43);
279     EXPECT_TRUE(p < q);
280   }
281   {
282     // if equal, no one is bigger
283     Poly<IStrictlyOrderable> p = 42;
284     Poly<IStrictlyOrderable> q = 42;
285     EXPECT_FALSE(p < q);
286     EXPECT_TRUE(p <= q);
287     EXPECT_FALSE(p > q);
288     EXPECT_TRUE(p >= q);
289     EXPECT_FALSE(q < p);
290     EXPECT_TRUE(q <= p);
291     EXPECT_FALSE(q > p);
292     EXPECT_TRUE(q >= p);
293   }
294   {
295     // empty is always smaller
296     Poly<IStrictlyOrderable> p;
297     Poly<IStrictlyOrderable> q = 42;
298     EXPECT_TRUE(p < q);
299     EXPECT_FALSE(q < p);
300   }
301   {
302     // mismatched types throws
303     Poly<IStrictlyOrderable> p = 4.2;
304     Poly<IStrictlyOrderable> q = 42;
305     EXPECT_THROW((void)(p < q), BadPolyCast);
306     EXPECT_THROW((void)(q < p), BadPolyCast);
307   }
308 }
309 
TEST(Poly,SemiRegularReference)310 TEST(Poly, SemiRegularReference) {
311   int i = 42;
312   Poly<ISemiRegular&> p = i;
313   EXPECT_EQ(42, i);
314   EXPECT_EQ(typeid(int), poly_type(p));
315   EXPECT_EQ(42, poly_cast<int>(p));
316   EXPECT_EQ(&i, &poly_cast<int>(p));
317   EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
318   Poly<ISemiRegular&> p2 = p;
319   EXPECT_EQ(typeid(int), poly_type(p2));
320   EXPECT_EQ(42, poly_cast<int>(p2));
321   EXPECT_EQ(&i, &poly_cast<int>(p2));
322   EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
323   std::swap(p, p2);
324   EXPECT_EQ(typeid(int), poly_type(p2));
325   EXPECT_EQ(42, poly_cast<int>(p2));
326   EXPECT_EQ(&i, &poly_cast<int>(p2));
327   EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
328   using std::swap;
329   swap(p, p2);
330   EXPECT_EQ(typeid(int), poly_type(p2));
331   EXPECT_EQ(42, poly_cast<int>(p2));
332   EXPECT_EQ(&i, &poly_cast<int>(p2));
333   EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
334   // Can't default-initialize reference-like Poly's:
335   static_assert(!std::is_default_constructible<Poly<ISemiRegular&>>::value, "");
336 }
337 
TEST(Poly,Conversions)338 TEST(Poly, Conversions) {
339   int i = 42;
340   Poly<ISemiRegular> p1 = i;
341   Poly<ISemiRegular&> p2 = p1;
342   EXPECT_EQ(&poly_cast<int>(p1), &poly_cast<int>(p2));
343   Poly<ISemiRegular const&> p3 = p1;
344   Poly<ISemiRegular const&> p4 = p2;
345   EXPECT_EQ(&poly_cast<int>(p3), &poly_cast<int>(p1));
346   EXPECT_EQ(&poly_cast<int>(p4), &poly_cast<int>(p1));
347   static_assert(
348       !std::is_constructible<Poly<ISemiRegular&>, Poly<ISemiRegular const&>&>::
349           value,
350       "");
351   static_assert(
352       !std::is_constructible<Poly<ISemiRegular>, Poly<ISemiRegular const&>&>::
353           value,
354       "");
355 }
356 
TEST(Poly,EqualityComparableReference)357 TEST(Poly, EqualityComparableReference) {
358   int i = 42;
359   int j = 42;
360   Poly<IEqualityComparable&> p1 = i;
361   Poly<IEqualityComparable&> p2 = j;
362   EXPECT_EQ(&i, &poly_cast<int>(p1));
363   EXPECT_EQ(&j, &poly_cast<int>(p2));
364   EXPECT_TRUE(p1 == p2);
365   EXPECT_FALSE(p1 != p2);
366   j = 43;
367   EXPECT_FALSE(p1 == p2);
368   EXPECT_TRUE(p1 != p2);
369   EXPECT_EQ(42, poly_cast<int>(p1));
370   EXPECT_EQ(43, poly_cast<int>(p2));
371 }
372 
373 namespace {
374 struct Foo {
375   template <class Base>
376   struct Interface : Base {
foo__anonfdb577f40211::Foo::Interface377     void foo(int& i) { folly::poly_call<0>(*this, i); }
378   };
379 
380   template <class T>
381   using Members = FOLLY_POLY_MEMBERS(&T::foo);
382 };
383 
384 struct foo_ {
385   foo_() = default;
foo___anonfdb577f40211::foo_386   explicit foo_(int i) : j_(i) {}
foo__anonfdb577f40211::foo_387   void foo(int& i) { i += j_; }
388 
389  private:
390   int j_ = 0;
391 };
392 } // namespace
393 
TEST(Poly,Singular)394 TEST(Poly, Singular) {
395   Poly<Foo> p = foo_{42};
396   int i = 1;
397   p.foo(i);
398   EXPECT_EQ(43, i);
399   EXPECT_EQ(typeid(foo_), poly_type(p));
400 }
401 
402 namespace {
403 struct FooBar : PolyExtends<Foo> {
404   template <class Base>
405   struct Interface : Base {
bar__anonfdb577f40311::FooBar::Interface406     std::string bar(int i) const { return folly::poly_call<0>(*this, i); }
407   };
408 
409   template <class T>
410   using Members = FOLLY_POLY_MEMBERS(&T::bar);
411 };
412 
413 struct foo_bar {
414   foo_bar() = default;
foo_bar__anonfdb577f40311::foo_bar415   explicit foo_bar(int i) : j_(i) {}
foo__anonfdb577f40311::foo_bar416   void foo(int& i) { i += j_; }
bar__anonfdb577f40311::foo_bar417   std::string bar(int i) const {
418     i += j_;
419     return folly::to<std::string>(i);
420   }
421 
422  private:
423   int j_ = 0;
424 };
425 } // namespace
426 
TEST(Poly,SingleInheritance)427 TEST(Poly, SingleInheritance) {
428   Poly<FooBar> p = foo_bar{42};
429   int i = 1;
430   p.foo(i);
431   EXPECT_EQ(43, i);
432   EXPECT_EQ("43", p.bar(1));
433   EXPECT_EQ(typeid(foo_bar), poly_type(p));
434 
435   Poly<Foo> q = p; // OK, conversion works.
436   q.foo(i);
437   EXPECT_EQ(85, i);
438 
439   Poly<Foo&> r = p;
440   r->foo(i);
441   EXPECT_EQ(127, i);
442   const_cast<Poly<Foo&> const&>(r)->foo(i);
443   EXPECT_EQ(169, i);
444 
445   Poly<FooBar const&> cr = p;
446   // cr->foo(i); // ERROR: calls a non-const member through a const reference
447   cr->bar(i); // OK
448 }
449 
450 namespace {
451 struct Baz {
452   template <class Base>
453   struct Interface : Base {
baz__anonfdb577f40411::Baz::Interface454     std::string baz(int i, int j) const {
455       return folly::poly_call<0>(*this, i, j);
456     }
457   };
458 
459   template <class T>
460   using Members = FOLLY_POLY_MEMBERS(&T::baz);
461 };
462 
463 struct FooBarBazFizz : PolyExtends<FooBar, Baz> {
464   template <class Base>
465   struct Interface : Base {
fizz__anonfdb577f40411::FooBarBazFizz::Interface466     std::string fizz() const { return folly::poly_call<0>(*this); }
467   };
468 
469   template <class T>
470   using Members = FOLLY_POLY_MEMBERS(&T::fizz);
471 };
472 
473 struct foo_bar_baz_fizz {
474   foo_bar_baz_fizz() = default;
foo_bar_baz_fizz__anonfdb577f40411::foo_bar_baz_fizz475   explicit foo_bar_baz_fizz(int i) : j_(i) {}
foo__anonfdb577f40411::foo_bar_baz_fizz476   void foo(int& i) { i += j_; }
bar__anonfdb577f40411::foo_bar_baz_fizz477   std::string bar(int i) const { return folly::to<std::string>(i + j_); }
baz__anonfdb577f40411::foo_bar_baz_fizz478   std::string baz(int i, int j) const { return folly::to<std::string>(i + j); }
fizz__anonfdb577f40411::foo_bar_baz_fizz479   std::string fizz() const { return "fizz"; }
480 
481  private:
482   int j_ = 0;
483 };
484 } // namespace
485 
TEST(Poly,MultipleInheritance)486 TEST(Poly, MultipleInheritance) {
487   Poly<FooBarBazFizz> p = foo_bar_baz_fizz{42};
488   int i = 1;
489   p.foo(i);
490   EXPECT_EQ(43, i);
491   EXPECT_EQ("43", p.bar(1));
492   EXPECT_EQ("3", p.baz(1, 2));
493   EXPECT_EQ("fizz", p.fizz());
494   EXPECT_EQ(typeid(foo_bar_baz_fizz), poly_type(p));
495 }
496 
497 namespace {
498 struct Property {
499   template <class Base>
500   struct Interface : Base {
prop__anonfdb577f40511::Property::Interface501     int prop() const { return folly::poly_call<0>(*this); }
prop__anonfdb577f40511::Property::Interface502     void prop(int i) { folly::poly_call<1>(*this, i); }
503   };
504 
505   template <class T>
506   using Members = FOLLY_POLY_MEMBERS(
507       FOLLY_POLY_MEMBER(int() const, &T::prop),
508       FOLLY_POLY_MEMBER(void(int), &T::prop));
509 };
510 
511 struct has_property {
512   has_property() = default;
has_property__anonfdb577f40511::has_property513   explicit has_property(int i) : j(i) {}
prop__anonfdb577f40511::has_property514   int prop() const { return j; }
prop__anonfdb577f40511::has_property515   void prop(int i) { j = i; }
516 
517  private:
518   int j = 0;
519 };
520 } // namespace
521 
TEST(Poly,OverloadedMembers)522 TEST(Poly, OverloadedMembers) {
523   Poly<Property> p = has_property{42};
524   EXPECT_EQ(typeid(has_property), poly_type(p));
525   EXPECT_EQ(42, p.prop());
526   p.prop(68);
527   EXPECT_EQ(68, p.prop());
528 }
529 
TEST(Poly,NullablePointer)530 TEST(Poly, NullablePointer) {
531   Poly<INullablePointer> p = nullptr;
532   Poly<INullablePointer> q{};
533   EXPECT_EQ(typeid(void), poly_type(p));
534   EXPECT_TRUE(poly_empty(p));
535   EXPECT_TRUE(p == q);
536   EXPECT_FALSE(p != q);
537   EXPECT_TRUE(p == nullptr);
538   EXPECT_TRUE(nullptr == p);
539   EXPECT_FALSE(p != nullptr);
540   EXPECT_FALSE(nullptr != p);
541 
542   // No null references ever.
543   Poly<INullablePointer> r = 42;
544   Poly<INullablePointer&> s = r;
545   static_assert(!poly_empty(s), "");
546   EXPECT_THROW(Poly<INullablePointer&> r_(q), BadPolyAccess);
547 }
548 
549 namespace {
550 struct MoveOnly_ {
551   MoveOnly_() = default;
552   MoveOnly_(MoveOnly_&&) = default;
553   MoveOnly_(MoveOnly_ const&) = delete;
554   MoveOnly_& operator=(MoveOnly_&&) = default;
555   MoveOnly_& operator=(MoveOnly_ const&) = delete;
556 };
557 } // namespace
558 
TEST(Poly,Move)559 TEST(Poly, Move) {
560   {
561     int i = 42;
562     Poly<IMoveOnly&> p = i;
563     static_assert(
564         !std::is_convertible<Poly<IMoveOnly&>, Poly<IMoveOnly&&>>::value, "");
565     auto q = poly_move(p);
566     static_assert(std::is_same<decltype(q), Poly<IMoveOnly&&>>::value, "");
567     EXPECT_EQ(&poly_cast<int>(p), &poly_cast<int>(q));
568   }
569   {
570     int i = 42;
571     Poly<ISemiRegular const&> p = i;
572     auto q = poly_move(p);
573     static_assert(
574         std::is_same<decltype(q), Poly<ISemiRegular const&>>::value, "");
575     EXPECT_EQ(&poly_cast<int>(p), &poly_cast<int>(q));
576   }
577   {
578     Poly<IMoveOnly> p = MoveOnly_{};
579     static_assert(!std::is_copy_constructible<Poly<IMoveOnly>>::value, "");
580     auto q = poly_move(p);
581     static_assert(std::is_same<decltype(q), Poly<IMoveOnly>>::value, "");
582   }
583 }
584 
TEST(Poly,RValueRef)585 TEST(Poly, RValueRef) {
586   int i = 42;
587   Poly<ISemiRegular&&> p = std::move(i);
588   static_assert(std::is_same<decltype(poly_cast<int>(p)), int&>::value, "");
589   EXPECT_EQ(&i, &poly_cast<int>(p));
590 }
591 
592 namespace {
593 template <class Fun>
594 struct IFunction;
595 
596 template <class R, class... As>
597 struct IFunction<R(As...)> {
598   template <class Base>
599   struct Interface : Base {
operator ()__anonfdb577f40711::IFunction::Interface600     R operator()(As... as) const {
601       return static_cast<R>(
602           folly::poly_call<0>(*this, std::forward<As>(as)...));
603     }
604   };
605 
606   template <class T>
607   using Members =
608       FOLLY_POLY_MEMBERS(FOLLY_POLY_MEMBER(R(As...) const, &T::operator()));
609 };
610 
611 template <class Fun>
612 using Function = Poly<IFunction<Fun>>;
613 } // namespace
614 
TEST(Poly,Function)615 TEST(Poly, Function) {
616   Function<int(int, int)> fun = std::plus<int>{};
617   EXPECT_EQ(42, fun(22, 20));
618   fun = std::multiplies<int>{};
619   EXPECT_EQ(22 * 20, fun(22, 20));
620 }
621 
622 namespace {
623 // This multiply extends IEqualityComparable
624 struct IDiamond : PolyExtends<IRegular, INullablePointer> {};
625 } // namespace
626 
TEST(Poly,DiamondInheritance)627 TEST(Poly, DiamondInheritance) {
628   {
629     // A small object, storable in-situ:
630     Poly<IDiamond> p = 42;
631     EXPECT_EQ(typeid(int), poly_type(p));
632     EXPECT_EQ(42, poly_cast<int>(p));
633     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
634     Poly<IDiamond> p2 = p;
635     EXPECT_EQ(typeid(int), poly_type(p2));
636     EXPECT_EQ(42, poly_cast<int>(p2));
637     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
638     Poly<IEqualityComparable&> eq = p;
639     EXPECT_EQ(&poly_cast<int>(p), &poly_cast<int>(eq));
640   }
641 
642   EXPECT_EQ(0, Big::s_count);
643   {
644     // A big object, stored on the heap:
645     Poly<IDiamond> p = Big(42);
646     EXPECT_EQ(1, Big::s_count);
647     EXPECT_EQ(typeid(Big), poly_type(p));
648     EXPECT_EQ(42, poly_cast<Big>(p).value());
649     EXPECT_THROW(poly_cast<short>(p), BadPolyCast);
650     Poly<IDiamond> p2 = p;
651     EXPECT_EQ(2, Big::s_count);
652     EXPECT_EQ(typeid(Big), poly_type(p2));
653     EXPECT_EQ(42, poly_cast<Big>(p2).value());
654     EXPECT_THROW(poly_cast<short>(p2), BadPolyCast);
655     Poly<IEqualityComparable&> eq = p;
656     EXPECT_EQ(&poly_cast<Big>(p), &poly_cast<Big>(eq));
657   }
658   EXPECT_EQ(0, Big::s_count);
659 }
660 
661 namespace {
662 struct Struct {
property__anonfdb577f40911::Struct663   int property() const { return 42; }
property__anonfdb577f40911::Struct664   void property(int) {}
665 };
666 struct Struct2 : Struct {
meow__anonfdb577f40911::Struct2667   int meow() { return 42; }
668 
purr__anonfdb577f40911::Struct2669   int purr() { return 1; }
purr__anonfdb577f40911::Struct2670   int purr() const { return 2; }
671 };
672 
property(Struct const &)673 int property(Struct const&) {
674   return 42;
675 }
property(Struct &,int)676 FOLLY_MAYBE_UNUSED void property(Struct&, int) {}
677 
meow(Struct2 &)678 int meow(Struct2&) {
679   return 42;
680 }
681 
purr(Struct2 &)682 int purr(Struct2&) {
683   return 1;
684 }
purr(Struct2 const &)685 int purr(Struct2 const&) {
686   return 2;
687 }
688 } // namespace
689 
TEST(Poly,Sig)690 TEST(Poly, Sig) {
691   {
692     auto m0 = folly::sig<int() const>(&Struct::property);
693     EXPECT_EQ(static_cast<int (Struct::*)() const>(&Struct::property), m0);
694     auto m1 = folly::sig<int()>(&Struct::property);
695     EXPECT_EQ(static_cast<int (Struct::*)() const>(&Struct::property), m1);
696 
697     auto m2 = folly::sig<int() const>(&Struct2::property);
698     EXPECT_EQ(static_cast<int (Struct2::*)() const>(&Struct2::property), m2);
699     auto m3 = folly::sig<int()>(&Struct2::property);
700     EXPECT_EQ(static_cast<int (Struct2::*)() const>(&Struct2::property), m3);
701 
702     auto m4 = folly::sig<long()>(&Struct2::meow);
703     EXPECT_EQ(&Struct2::meow, m4);
704 
705     auto m5 = folly::sig<int()>(&Struct2::purr);
706     EXPECT_EQ(static_cast<int (Struct2::*)()>(&Struct2::purr), m5);
707     auto m6 = folly::sig<int() const>(&Struct2::purr);
708     EXPECT_EQ(static_cast<int (Struct2::*)() const>(&Struct2::purr), m6);
709   }
710   {
711     auto m0 = folly::sig<int(Struct const&)>(&::property);
712     EXPECT_EQ(static_cast<int (*)(Struct const&)>(&::property), m0);
713     auto m1 = folly::sig<int(Struct&)>(&::property);
714     EXPECT_EQ(static_cast<int (*)(Struct const&)>(&::property), m1);
715 
716     auto m2 = folly::sig<long(Struct2&)>(&::meow);
717     EXPECT_EQ(&::meow, m2);
718 
719     auto m3 = folly::sig<int(Struct2&)>(&::purr);
720     EXPECT_EQ(static_cast<int (*)(Struct2&)>(&::purr), m3);
721     auto m4 = folly::sig<int(Struct2 const&)>(&::purr);
722     EXPECT_EQ(static_cast<int (*)(Struct2 const&)>(&::purr), m4);
723   }
724 }
725 
726 namespace {
727 struct IAddable {
728   template <class Base>
729   struct Interface : Base {
operator +__anonfdb577f40a11::IAddable730     friend PolySelf<Base, PolyDecay> operator+(
731         PolySelf<Base> const& a, PolySelf<Base> const& b) {
732       return folly::poly_call<0, IAddable>(a, b);
733     }
734   };
735   template <class T>
plus___anonfdb577f40a11::IAddable736   static auto plus_(T const& a, T const& b) -> decltype(a + b) {
737     return a + b;
738   }
739 
740   template <class T>
741   using Members = FOLLY_POLY_MEMBERS(&plus_<std::decay_t<T>>);
742 };
743 } // namespace
744 
TEST(Poly,Addable)745 TEST(Poly, Addable) {
746   Poly<IAddable> a = 2, b = 3;
747   Poly<IAddable> c = a + b;
748   EXPECT_EQ(typeid(int), poly_type(c));
749   EXPECT_EQ(5, poly_cast<int>(c));
750 
751   Poly<IAddable const&> aref = a, bref = b;
752   auto cc = aref + bref;
753   static_assert(std::is_same<decltype(cc), Poly<IAddable>>::value, "");
754   EXPECT_EQ(typeid(int), poly_type(cc));
755   EXPECT_EQ(5, poly_cast<int>(cc));
756   b = 4;
757   EXPECT_EQ(5, poly_cast<int>(cc));
758   cc = aref + bref;
759   EXPECT_EQ(6, poly_cast<int>(cc));
760 }
761 
762 namespace {
763 struct IFrobnicator {
764   template <class Base>
765   struct Interface : Base {
frobnicate__anonfdb577f40b11::IFrobnicator::Interface766     void frobnicate(folly::Poly<folly::poly::IRegular&> x) {
767       folly::poly_call<0>(*this, x);
768     }
769   };
770   template <class T>
771   using Members = FOLLY_POLY_MEMBERS(&T::frobnicate);
772 };
773 using Frobnicator = folly::Poly<IFrobnicator>;
774 
775 struct my_frobnicator {
frobnicate__anonfdb577f40b11::my_frobnicator776   void frobnicate(folly::Poly<folly::poly::IRegular&>) {
777     // no-op
778   }
779 };
780 } // namespace
781 
TEST(Poly,PolyRefAsArg)782 TEST(Poly, PolyRefAsArg) {
783   folly::Poly<folly::poly::IRegular> x = 42;
784   Frobnicator frob = my_frobnicator{};
785   // should not throw:
786   frob.frobnicate(x);
787   // should not throw:
788   frob.frobnicate(folly::Poly<folly::poly::IRegular&>(x));
789 }
790 
791 namespace {
792 struct ICat {
793   template <class Base>
794   struct Interface : Base {
pet__anonfdb577f40c11::ICat::Interface795     void pet() { folly::poly_call<0>(*this); }
796 
meow__anonfdb577f40c11::ICat::Interface797     int meow() const { return folly::poly_call<1>(*this); }
798   };
799 
800   template <class T>
801   using Members = FOLLY_POLY_MEMBERS(&T::pet, &T::meow);
802 };
803 
804 struct cat {
pet__anonfdb577f40c11::cat805   void pet() noexcept { ++pet_count; }
meow__anonfdb577f40c11::cat806   int meow() const noexcept { return pet_count; }
807   int pet_count = 0;
808 };
809 } // namespace
810 
TEST(Poly,NoexceptMembers)811 TEST(Poly, NoexceptMembers) {
812   cat c{};
813 
814   folly::Poly<ICat&> ref = c;
815   ref->pet();
816 
817   folly::Poly<ICat const&> cref = ref;
818   EXPECT_EQ(cref->meow(), 1);
819 }
820