1 /*
2   Copyright (c) 2014, Randolph Voorhies, Shane Grant
3   All rights reserved.
4 
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions are met:
7       * Redistributions of source code must retain the above copyright
8         notice, this list of conditions and the following disclaimer.
9       * Redistributions in binary form must reproduce the above copyright
10         notice, this list of conditions and the following disclaimer in the
11         documentation and/or other materials provided with the distribution.
12       * Neither the name of cereal nor the
13         names of its contributors may be used to endorse or promote products
14         derived from this software without specific prior written permission.
15 
16   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19   DISCLAIMED. IN NO EVENT SHALL RANDOLPH VOORHIES AND SHANE GRANT BE LIABLE FOR ANY
20   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 #include "common.hpp"
28 #include <boost/test/unit_test.hpp>
29 
30 struct PolyBase
31 {
PolyBasePolyBase32   PolyBase() {}
PolyBasePolyBase33   PolyBase( int xx, float yy ) : x(xx), y(yy) {}
34   int x;
35   float y;
36 
37   template <class Archive>
serializePolyBase38   void serialize( Archive & ar )
39   {
40     ar( x, y );
41   }
42 
43   virtual void foo() = 0;
44 
operator ==PolyBase45   bool operator==( PolyBase const & other ) const
46   {
47     return x == other.x && std::abs(y - other.y) < 1e-5;
48   }
49 };
50 
51 struct PolyDerived : PolyBase
52 {
PolyDerivedPolyDerived53   PolyDerived() {}
PolyDerivedPolyDerived54   PolyDerived( int xx, float yy, bool aa, double bb ) :
55     PolyBase( xx, yy ), a(aa), b(bb) {}
56 
57   bool a;
58   double b;
59 
60   template <class Archive>
serializePolyDerived61   void serialize( Archive & ar )
62   {
63     ar( cereal::base_class<PolyBase>( this ),
64         a, b );
65   }
66 
operator ==PolyDerived67   bool operator==( PolyDerived const & other ) const
68   {
69     return PolyBase::operator==( other ) && a == other.a && std::abs(b - other.b) < 1e-5;
70   }
71 
fooPolyDerived72   void foo() {}
73 };
74 
75 struct PolyLA : std::enable_shared_from_this<PolyLA>
76 {
77   virtual void foo() = 0;
78 };
79 
80 struct PolyDerivedLA : public PolyLA
81 {
PolyDerivedLAPolyDerivedLA82   PolyDerivedLA( int xx ) : x( xx ) { }
83 
84   int x;
85   std::vector<std::shared_ptr<PolyLA>> vec;
86 
87   template <class Archive>
serializePolyDerivedLA88   void serialize( Archive & ar )
89   {
90     ar( x );
91     ar( vec );
92   }
93 
94   template <class Archive>
load_and_constructPolyDerivedLA95   static void load_and_construct( Archive & ar, cereal::construct<PolyDerivedLA> & construct )
96   {
97     int xx;
98     ar( xx );
99     construct( xx );
100     ar( construct->vec );
101   }
102 
fooPolyDerivedLA103   void foo() {}
104 
operator ==PolyDerivedLA105   bool operator==( PolyDerivedLA const & other ) const
106   {
107     if( x != other.x )
108       return false;
109     if( vec.size() != other.vec.size() )
110       return false;
111     for( size_t i = 0; i < vec.size(); ++i )
112       if( !(*std::dynamic_pointer_cast<PolyDerivedLA>(vec[i]) == *std::dynamic_pointer_cast<PolyDerivedLA>(other.vec[i])) )
113         return false;
114 
115     return true;
116   }
117 };
118 
operator <<(std::ostream & os,PolyDerivedLA const & s)119 std::ostream& operator<<(std::ostream& os, PolyDerivedLA const & s)
120 {
121     os << "[x: " << s.x << "] ";
122     for( auto const & v : s.vec )
123       os << " child: " << (*std::dynamic_pointer_cast<PolyDerivedLA>(v));
124     return os;
125 }
126 
operator <<(std::ostream & os,PolyDerived const & s)127 std::ostream& operator<<(std::ostream& os, PolyDerived const & s)
128 {
129     os << "[x: " << s.x << " y: " << s.y << " a: " << s.a << " b: " << s.b << "]";
130     return os;
131 }
132 
133 CEREAL_REGISTER_TYPE(PolyDerived)
CEREAL_REGISTER_TYPE(PolyDerivedLA)134 CEREAL_REGISTER_TYPE(PolyDerivedLA)
135 
136 template <class IArchive, class OArchive>
137 void test_polymorphic()
138 {
139   std::random_device rd;
140   std::mt19937 gen(rd());
141 
142   auto rngB = [&](){ return random_value<int>( gen ) % 2 == 0; };
143   auto rngI = [&](){ return random_value<int>( gen ); };
144   auto rngF = [&](){ return random_value<float>( gen ); };
145   auto rngD = [&](){ return random_value<double>( gen ); };
146 
147   for(int ii=0; ii<100; ++ii)
148   {
149     std::shared_ptr<PolyBase> o_shared = std::make_shared<PolyDerived>( rngI(), rngF(), rngB(), rngD() );
150     std::weak_ptr<PolyBase>   o_weak = o_shared;
151     std::unique_ptr<PolyBase> o_unique( new PolyDerived( rngI(), rngF(), rngB(), rngD() ) );
152 
153     auto pda = std::make_shared<PolyDerivedLA>( rngI() );
154     pda->vec.emplace_back( std::make_shared<PolyDerivedLA>( rngI() ) );
155     std::shared_ptr<PolyLA>   o_sharedLA = pda;
156 
157     std::ostringstream os;
158     {
159       OArchive oar(os);
160 
161       oar( o_shared, o_weak, o_unique );
162       oar( o_sharedLA );
163     }
164 
165     decltype(o_shared) i_shared;
166     decltype(o_weak) i_weak;
167     decltype(o_unique) i_unique;
168     decltype(o_sharedLA) i_sharedLA;
169 
170     std::istringstream is(os.str());
171     {
172       IArchive iar(is);
173 
174       iar( i_shared, i_weak, i_unique );
175       iar( i_sharedLA );
176     }
177 
178     auto i_locked = i_weak.lock();
179     auto o_locked = o_weak.lock();
180 
181     auto i_sharedLA2 = i_sharedLA->shared_from_this();
182 
183     BOOST_CHECK_EQUAL(i_shared.get(), i_locked.get());
184     BOOST_CHECK_EQUAL(*((PolyDerived*)i_shared.get()), *((PolyDerived*)o_shared.get()));
185     BOOST_CHECK_EQUAL(*((PolyDerived*)i_shared.get()), *((PolyDerived*)i_locked.get()));
186     BOOST_CHECK_EQUAL(*((PolyDerived*)i_locked.get()), *((PolyDerived*)o_locked.get()));
187     BOOST_CHECK_EQUAL(*((PolyDerived*)i_unique.get()), *((PolyDerived*)o_unique.get()));
188     BOOST_CHECK_EQUAL(*((PolyDerivedLA*)i_sharedLA.get()), *((PolyDerivedLA*)o_sharedLA.get()));
189     BOOST_CHECK_EQUAL(*((PolyDerivedLA*)i_sharedLA2.get()), *((PolyDerivedLA*)o_sharedLA.get()));
190   }
191 }
192 
BOOST_AUTO_TEST_CASE(binary_polymorphic)193 BOOST_AUTO_TEST_CASE( binary_polymorphic )
194 {
195   test_polymorphic<cereal::BinaryInputArchive, cereal::BinaryOutputArchive>();
196 }
197 
BOOST_AUTO_TEST_CASE(portable_binary_polymorphic)198 BOOST_AUTO_TEST_CASE( portable_binary_polymorphic )
199 {
200   test_polymorphic<cereal::PortableBinaryInputArchive, cereal::PortableBinaryOutputArchive>();
201 }
202 
BOOST_AUTO_TEST_CASE(xml_polymorphic)203 BOOST_AUTO_TEST_CASE( xml_polymorphic )
204 {
205   test_polymorphic<cereal::XMLInputArchive, cereal::XMLOutputArchive>();
206 }
207 
BOOST_AUTO_TEST_CASE(json_polymorphic)208 BOOST_AUTO_TEST_CASE( json_polymorphic )
209 {
210   test_polymorphic<cereal::JSONInputArchive, cereal::JSONOutputArchive>();
211 }
212 
213