fix a multiline comment warning
[folly.git] / folly / test / PolyTest.cpp
1 /*
2  * Copyright 2017-present Facebook, Inc.
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 #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
17 #pragma message "Folly.Poly requires gcc-5 or greater"
18 #else
19 #include <folly/Poly.h>
20
21 #include <folly/Conv.h>
22 #include <folly/poly/Nullable.h>
23 #include <folly/poly/Regular.h>
24 #include <folly/portability/GTest.h>
25
26 #include <array>
27
28 using namespace folly;
29 using namespace folly::poly;
30
31 namespace {
32 struct Big {
33  private:
34   std::array<char, sizeof(Poly<ISemiRegular>) + 1> data_;
35   int i_;
36
37  public:
38   Big() : data_{}, i_(0) {
39     ++s_count;
40   }
41   explicit Big(int i) : data_{}, i_(i) {
42     ++s_count;
43   }
44   Big(Big const& that) : data_(that.data_), i_(that.i_) {
45     ++s_count;
46   }
47   ~Big() {
48     --s_count;
49   }
50   Big& operator=(Big const&) = default;
51   int value() const {
52     return i_;
53   }
54   friend bool operator==(Big const& a, Big const& b) {
55     return a.value() == b.value();
56   }
57   friend bool operator!=(Big const& a, Big const& b) {
58     return !(a == b);
59   }
60   static std::ptrdiff_t s_count;
61 };
62 std::ptrdiff_t Big::s_count = 0;
63 } // namespace
64
65 TEST(Poly, SemiRegular) {
66   {
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);
76   }
77
78   EXPECT_EQ(0, Big::s_count);
79   {
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);
91   }
92   EXPECT_EQ(0, Big::s_count);
93 }
94
95 TEST(Poly, SemiRegularReference) {
96   int i = 42;
97   Poly<ISemiRegular&> p = i;
98   EXPECT_EQ(42, 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, "");
105 }
106
107 TEST(Poly, Conversions) {
108   int i = 42;
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));
116   static_assert(
117       !std::is_constructible<Poly<ISemiRegular&>, Poly<ISemiRegular const&>&>::
118           value,
119       "");
120   static_assert(
121       !std::is_constructible<Poly<ISemiRegular>, Poly<ISemiRegular const&>&>::
122           value,
123       "");
124 }
125
126 TEST(Poly, EqualityComparableReference) {
127   int i = 42;
128   int j = 42;
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);
135   j = 43;
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));
140 }
141
142 namespace {
143 struct Foo {
144   template <class Base>
145   struct Interface : Base {
146     void foo(int& i) {
147       folly::poly_call<0>(*this, i);
148     }
149   };
150
151   template <class T>
152   using Members = FOLLY_POLY_MEMBERS(&T::foo);
153 };
154
155 struct foo_ {
156   foo_() = default;
157   explicit foo_(int i) : j_(i) {}
158   void foo(int& i) {
159     i += j_;
160   }
161
162  private:
163   int j_ = 0;
164 };
165 } // namespace
166
167 TEST(Poly, Singular) {
168   Poly<Foo> p = foo_{42};
169   int i = 1;
170   p.foo(i);
171   EXPECT_EQ(43, i);
172   EXPECT_EQ(typeid(foo_), poly_type(p));
173 }
174
175 namespace {
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);
181     }
182   };
183
184   template <class T>
185   using Members = FOLLY_POLY_MEMBERS(&T::bar);
186 };
187
188 struct foo_bar {
189   foo_bar() = default;
190   explicit foo_bar(int i) : j_(i) {}
191   void foo(int& i) {
192     i += j_;
193   }
194   std::string bar(int i) const {
195     i += j_;
196     return folly::to<std::string>(i);
197   }
198
199  private:
200   int j_ = 0;
201 };
202 } // namespace
203
204 TEST(Poly, SingleInheritance) {
205   Poly<FooBar> p = foo_bar{42};
206   int i = 1;
207   p.foo(i);
208   EXPECT_EQ(43, i);
209   EXPECT_EQ("43", p.bar(1));
210   EXPECT_EQ(typeid(foo_bar), poly_type(p));
211
212   Poly<Foo> q = p; // OK, conversion works.
213   q.foo(i);
214   EXPECT_EQ(85, i);
215
216   Poly<Foo&> r = p;
217   r->foo(i);
218   EXPECT_EQ(127, i);
219   const_cast<Poly<Foo&> const&>(r)->foo(i);
220   EXPECT_EQ(169, i);
221
222   Poly<FooBar const&> cr = p;
223   // cr->foo(i); // ERROR: calls a non-const member through a const reference
224   cr->bar(i); // OK
225 }
226
227 namespace {
228 struct Baz {
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);
233     }
234   };
235
236   template <class T>
237   using Members = FOLLY_POLY_MEMBERS(&T::baz);
238 };
239
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);
245     }
246   };
247
248   template <class T>
249   using Members = FOLLY_POLY_MEMBERS(&T::fizz);
250 };
251
252 struct foo_bar_baz_fizz {
253   foo_bar_baz_fizz() = default;
254   explicit foo_bar_baz_fizz(int i) : j_(i) {}
255   void foo(int& i) {
256     i += j_;
257   }
258   std::string bar(int i) const {
259     return folly::to<std::string>(i + j_);
260   }
261   std::string baz(int i, int j) const {
262     return folly::to<std::string>(i + j);
263   }
264   std::string fizz() const {
265     return "fizz";
266   }
267
268  private:
269   int j_ = 0;
270 };
271 } // namespace
272
273 TEST(Poly, MultipleInheritance) {
274   Poly<FooBarBazFizz> p = foo_bar_baz_fizz{42};
275   int i = 1;
276   p.foo(i);
277   EXPECT_EQ(43, i);
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));
282 }
283
284 namespace {
285 struct Property {
286   template <class Base>
287   struct Interface : Base {
288     int prop() const {
289       return folly::poly_call<0>(*this);
290     }
291     void prop(int i) {
292       folly::poly_call<1>(*this, i);
293     }
294   };
295
296   template <class T>
297   using Members = FOLLY_POLY_MEMBERS(
298       FOLLY_POLY_MEMBER(int() const, &T::prop),
299       FOLLY_POLY_MEMBER(void(int), &T::prop));
300 };
301
302 struct property {
303   property() = default;
304   explicit property(int i) : j(i) {}
305   int prop() const {
306     return j;
307   }
308   void prop(int i) {
309     j = i;
310   }
311
312  private:
313   int j = 0;
314 };
315 } // namespace
316
317 TEST(Poly, OverloadedMembers) {
318   Poly<Property> p = property{42};
319   EXPECT_EQ(typeid(property), poly_type(p));
320   EXPECT_EQ(42, p.prop());
321   p.prop(68);
322   EXPECT_EQ(68, p.prop());
323 }
324
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));
330   EXPECT_TRUE(p == q);
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);
336
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);
342 }
343
344 namespace {
345 struct MoveOnly_ {
346   MoveOnly_() = default;
347   MoveOnly_(MoveOnly_&&) = default;
348   MoveOnly_(MoveOnly_ const&) = delete;
349   MoveOnly_& operator=(MoveOnly_&&) = default;
350   MoveOnly_& operator=(MoveOnly_ const&) = delete;
351 };
352 } // namespace
353
354 TEST(Poly, Move) {
355   {
356     int i = 42;
357     Poly<IMoveOnly&> p = i;
358     static_assert(
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));
363   }
364   {
365     int i = 42;
366     Poly<ISemiRegular const&> p = i;
367     auto q = poly_move(p);
368     static_assert(
369         std::is_same<decltype(q), Poly<ISemiRegular const&>>::value, "");
370     EXPECT_EQ(&poly_cast<int>(p), &poly_cast<int>(q));
371   }
372   {
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, "");
377   }
378 }
379
380 TEST(Poly, RValueRef) {
381   int i = 42;
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));
385 }
386
387 namespace {
388 template <class Fun>
389 struct IFunction;
390
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)...));
398     }
399   };
400
401   template <class T>
402   using Members =
403       FOLLY_POLY_MEMBERS(FOLLY_POLY_MEMBER(R(As...) const, &T::operator()));
404 };
405
406 template <class Fun>
407 using Function = Poly<IFunction<Fun>>;
408 } // namespace
409
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));
415 }
416
417 namespace {
418 // This multiply extends IEqualityComparable
419 struct IDiamond : PolyExtends<IRegular, INullablePointer> {};
420 } // namespace
421
422 TEST(Poly, DiamondInheritance) {
423   {
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));
435   }
436
437   EXPECT_EQ(0, Big::s_count);
438   {
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));
452   }
453   EXPECT_EQ(0, Big::s_count);
454 }
455
456 namespace {
457 struct Struct {
458   int property() const {
459     return 42;
460   }
461   void property(int) {}
462 };
463 struct Struct2 : Struct {
464   int meow() {
465     return 42;
466   }
467
468   int purr() {
469     return 1;
470   }
471   int purr() const {
472     return 2;
473   }
474 };
475
476 int property(Struct const&) {
477   return 42;
478 }
479 void property(Struct&, int) {}
480
481 int meow(Struct2&) {
482   return 42;
483 }
484
485 int purr(Struct2&) {
486   return 1;
487 }
488 int purr(Struct2 const&) {
489   return 2;
490 }
491 } // namespace
492
493 TEST(Poly, Sig) {
494   {
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);
499
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);
504
505     auto m4 = folly::sig<long()>(&Struct2::meow);
506     EXPECT_EQ(&Struct2::meow, m4);
507
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);
512   }
513   {
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);
518
519     auto m2 = folly::sig<long(Struct2&)>(&::meow);
520     EXPECT_EQ(&::meow, m2);
521
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);
526   }
527 }
528
529 namespace {
530 struct IAddable {
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);
537     }
538   };
539   template <class T>
540   static auto plus_(T const& a, T const& b) -> decltype(a + b) {
541     return a + b;
542   }
543
544   template <class T>
545   using Members = FOLLY_POLY_MEMBERS(&plus_<std::decay_t<T>>);
546 };
547 } // namespace
548
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));
554
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));
560   b = 4;
561   EXPECT_EQ(5, poly_cast<int>(cc));
562   cc = aref + bref;
563   EXPECT_EQ(6, poly_cast<int>(cc));
564 }
565
566 namespace {
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);
572     }
573   };
574   template <class T>
575   using Members = FOLLY_POLY_MEMBERS(&T::frobnicate);
576 };
577 using Frobnicator = folly::Poly<IFrobnicator>;
578
579 struct my_frobnicator {
580   void frobnicate(folly::Poly<folly::poly::IRegular&>) {
581     // no-op
582   }
583 };
584 } // namespace
585
586 TEST(Poly, PolyRefAsArg) {
587   folly::Poly<folly::poly::IRegular> x = 42;
588   Frobnicator frob = my_frobnicator{};
589   // should not throw:
590   frob.frobnicate(x);
591   // should not throw:
592   frob.frobnicate(folly::Poly<folly::poly::IRegular&>(x));
593 }
594 #endif