PageRenderTime 23ms CodeModel.GetById 3ms app.highlight 17ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/test/lldoubledispatch_tut.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 239 lines | 143 code | 27 blank | 69 comment | 0 complexity | 6c869bd1c0d0ce72089590d657239eb2 MD5 | raw file
  1/**
  2 * @file   lldoubledispatch_tut.cpp
  3 * @author Nat Goodspeed
  4 * @date   2008-11-13
  5 * @brief  Test for lldoubledispatch.h
  6 *
  7 * This program tests the DoubleDispatch class, using a variation on the example
  8 * from Scott Meyers' "More Effective C++", Item 31.
  9 *
 10 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
 11 * Second Life Viewer Source Code
 12 * Copyright (C) 2010, Linden Research, Inc.
 13 * 
 14 * This library is free software; you can redistribute it and/or
 15 * modify it under the terms of the GNU Lesser General Public
 16 * License as published by the Free Software Foundation;
 17 * version 2.1 of the License only.
 18 * 
 19 * This library is distributed in the hope that it will be useful,
 20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 22 * Lesser General Public License for more details.
 23 * 
 24 * You should have received a copy of the GNU Lesser General Public
 25 * License along with this library; if not, write to the Free Software
 26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 27 * 
 28 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 29 * $/LicenseInfo$
 30 */
 31
 32// Precompiled header
 33#include "linden_common.h"
 34// associated header
 35#include "lldoubledispatch.h"
 36// STL headers
 37// std headers
 38#include <string>
 39#include <iostream>
 40#include <typeinfo>
 41// external library headers
 42// other Linden headers
 43#include "lltut.h"
 44
 45
 46/*---------------------------- Class hierarchy -----------------------------*/
 47// All objects are GameObjects.
 48class GameObject
 49{
 50public:
 51    GameObject(const std::string& name): mName(name) {}
 52    virtual ~GameObject() {}
 53    virtual std::string stringize() { return std::string(typeid(*this).name()) + " " + mName; }
 54
 55protected:
 56    std::string mName;
 57};
 58
 59// SpaceStation, Asteroid and SpaceShip are peer GameObjects.
 60struct SpaceStation: public GameObject
 61{
 62    SpaceStation(const std::string& name): GameObject(name) {}
 63    // Only a dummy SpaceStation is constructed without a name
 64    SpaceStation(): GameObject("dummy") {}
 65};
 66
 67struct Asteroid: public GameObject
 68{
 69    Asteroid(const std::string& name): GameObject(name) {}
 70    Asteroid(): GameObject("dummy") {}
 71};
 72
 73struct SpaceShip: public GameObject
 74{
 75    SpaceShip(const std::string& name): GameObject(name) {}
 76    SpaceShip(): GameObject("dummy") {}
 77};
 78
 79// SpaceShip is specialized further into CommercialShip and MilitaryShip.
 80struct CommercialShip: public SpaceShip
 81{
 82    CommercialShip(const std::string& name): SpaceShip(name) {}
 83    CommercialShip(): SpaceShip("dummy") {}
 84};
 85
 86struct MilitaryShip: public SpaceShip
 87{
 88    MilitaryShip(const std::string& name): SpaceShip(name) {}
 89    MilitaryShip(): SpaceShip("dummy") {}
 90};
 91
 92/*-------------------------- Collision functions ---------------------------*/
 93// This mechanism permits us to overcome a limitation of Meyers' approach:  we
 94// can declare the parameter types exactly as we want, rather than having to
 95// make them all GameObject& parameters.
 96std::string shipAsteroid(SpaceShip& ship, Asteroid& rock)
 97{
 98//  std::cout << rock.stringize() << " has pulverized " << ship.stringize() << std::endl;
 99    return "shipAsteroid";
100}
101
102std::string militaryShipAsteroid(MilitaryShip& ship, Asteroid& rock)
103{
104//  std::cout << rock.stringize() << " has severely damaged " << ship.stringize() << std::endl;
105    return "militaryShipAsteroid";
106}
107
108std::string shipStation(SpaceShip& ship, SpaceStation& dock)
109{
110//  std::cout << ship.stringize() << " has docked at " << dock.stringize() << std::endl;
111    return "shipStation";
112}
113
114std::string asteroidStation(Asteroid& rock, SpaceStation& dock)
115{
116//  std::cout << rock.stringize() << " has damaged " << dock.stringize() << std::endl;
117    return "asteroidStation";
118}
119
120/*------------------------------- Test code --------------------------------*/
121namespace tut
122{
123    struct dispatch_data
124    {
125        dispatch_data():
126            home(new SpaceStation("Terra Station")),
127            obstacle(new Asteroid("Ganymede")),
128            tug(new CommercialShip("Pilotfish")),
129            patrol(new MilitaryShip("Enterprise"))
130        {}
131
132        // Instantiate and populate the DoubleDispatch object.
133        typedef LLDoubleDispatch<std::string, GameObject> DD;
134        DD dispatcher;
135
136        // Instantiate a few GameObjects.  Make sure we refer to them
137        // polymorphically, and don't let them leak.
138        std::auto_ptr<GameObject> home;
139        std::auto_ptr<GameObject> obstacle;
140        std::auto_ptr<GameObject> tug;
141        std::auto_ptr<GameObject> patrol;
142
143        // prototype objects
144        Asteroid dummyAsteroid;
145        SpaceShip dummyShip;
146        MilitaryShip dummyMilitary;
147        CommercialShip dummyCommercial;
148        SpaceStation dummyStation;
149    };
150    typedef test_group<dispatch_data> dispatch_group;
151    typedef dispatch_group::object dispatch_object;
152    tut::dispatch_group ddgr("double dispatch");
153
154    template<> template<>
155    void dispatch_object::test<1>()
156    {
157        // Describe param types using explicit DD::Type objects
158        // (order-sensitive add() variant)
159        dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(), shipAsteroid, true);
160        // naive adding, won't work
161        dispatcher.add(DD::Type<MilitaryShip>(), DD::Type<Asteroid>(), militaryShipAsteroid, true);
162        dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(), shipStation, true);
163        dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(), asteroidStation, true);
164
165        // Try colliding them.
166        ensure_equals(dispatcher(*home, *tug),        // reverse params, SpaceShip subclass
167                      "shipStation");
168        ensure_equals(dispatcher(*patrol, *home),     // forward params, SpaceShip subclass
169                      "shipStation");
170        ensure_equals(dispatcher(*obstacle, *home),   // forward params
171                      "asteroidStation");
172        ensure_equals(dispatcher(*home, *obstacle),   // reverse params
173                      "asteroidStation");
174        ensure_equals(dispatcher(*tug, *obstacle),    // forward params, SpaceShip subclass
175                      "shipAsteroid");
176        ensure_equals(dispatcher(*obstacle, *patrol), // reverse params, SpaceShip subclass
177                      // won't use militaryShipAsteroid() because it was added
178                      // in wrong order
179                      "shipAsteroid");
180    }
181
182    template<> template<>
183    void dispatch_object::test<2>()
184    {
185        // Describe param types using explicit DD::Type objects
186        // (order-sensitive add() variant)
187        // adding in correct order
188        dispatcher.add(DD::Type<MilitaryShip>(), DD::Type<Asteroid>(), militaryShipAsteroid, true);
189        dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(), shipAsteroid, true);
190        dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(), shipStation, true);
191        dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(), asteroidStation, true);
192        
193        ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
194        ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
195    }
196
197    template<> template<>
198    void dispatch_object::test<3>()
199    {
200        // Describe param types with actual prototype instances
201        // (order-insensitive add() variant)
202        dispatcher.add(dummyMilitary, dummyAsteroid, militaryShipAsteroid);
203        dispatcher.add(dummyShip, dummyAsteroid, shipAsteroid);
204        dispatcher.add(dummyShip, dummyStation, shipStation);
205        dispatcher.add(dummyAsteroid, dummyStation, asteroidStation);
206
207        ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
208        ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
209        ensure_equals(dispatcher(*obstacle, *patrol), "");
210    }
211
212    template<> template<>
213    void dispatch_object::test<4>()
214    {
215        // Describe param types with actual prototype instances
216        // (order-insensitive add() variant)
217        dispatcher.add(dummyShip, dummyAsteroid, shipAsteroid);
218        // Even if we add the militaryShipAsteroid in the wrong order, it
219        // should still work.
220        dispatcher.add(dummyMilitary, dummyAsteroid, militaryShipAsteroid);
221        dispatcher.add(dummyShip, dummyStation, shipStation);
222        dispatcher.add(dummyAsteroid, dummyStation, asteroidStation);
223
224        ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
225        ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
226    }
227
228    template<> template<>
229    void dispatch_object::test<5>()
230    {
231        dispatcher.add<SpaceShip, Asteroid>(shipAsteroid);
232        dispatcher.add<MilitaryShip, Asteroid>(militaryShipAsteroid);
233        dispatcher.add<SpaceShip, SpaceStation>(shipStation);
234        dispatcher.add<Asteroid, SpaceStation>(asteroidStation);
235
236        ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
237        ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
238    }
239} // namespace tut