diff options
Diffstat (limited to 'indra/test/lldoubledispatch_tut.cpp')
-rwxr-xr-x | indra/test/lldoubledispatch_tut.cpp | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/indra/test/lldoubledispatch_tut.cpp b/indra/test/lldoubledispatch_tut.cpp new file mode 100755 index 0000000000..63ef4d4497 --- /dev/null +++ b/indra/test/lldoubledispatch_tut.cpp @@ -0,0 +1,245 @@ +/** + * @file lldoubledispatch_tut.cpp + * @author Nat Goodspeed + * @date 2008-11-13 + * @brief Test for lldoubledispatch.h + * + * This program tests the DoubleDispatch class, using a variation on the example + * from Scott Meyers' "More Effective C++", Item 31. + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 2008-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +// Precompiled header +#include "linden_common.h" +// associated header +#include "lldoubledispatch.h" +// STL headers +// std headers +#include <string> +#include <iostream> +#include <typeinfo> +// external library headers +// other Linden headers +#include "lltut.h" + + +/*---------------------------- Class hierarchy -----------------------------*/ +// All objects are GameObjects. +class GameObject +{ +public: + GameObject(const std::string& name): mName(name) {} + virtual ~GameObject() {} + virtual std::string stringize() { return std::string(typeid(*this).name()) + " " + mName; } + +protected: + std::string mName; +}; + +// SpaceStation, Asteroid and SpaceShip are peer GameObjects. +struct SpaceStation: public GameObject +{ + SpaceStation(const std::string& name): GameObject(name) {} + // Only a dummy SpaceStation is constructed without a name + SpaceStation(): GameObject("dummy") {} +}; + +struct Asteroid: public GameObject +{ + Asteroid(const std::string& name): GameObject(name) {} + Asteroid(): GameObject("dummy") {} +}; + +struct SpaceShip: public GameObject +{ + SpaceShip(const std::string& name): GameObject(name) {} + SpaceShip(): GameObject("dummy") {} +}; + +// SpaceShip is specialized further into CommercialShip and MilitaryShip. +struct CommercialShip: public SpaceShip +{ + CommercialShip(const std::string& name): SpaceShip(name) {} + CommercialShip(): SpaceShip("dummy") {} +}; + +struct MilitaryShip: public SpaceShip +{ + MilitaryShip(const std::string& name): SpaceShip(name) {} + MilitaryShip(): SpaceShip("dummy") {} +}; + +/*-------------------------- Collision functions ---------------------------*/ +// This mechanism permits us to overcome a limitation of Meyers' approach: we +// can declare the parameter types exactly as we want, rather than having to +// make them all GameObject& parameters. +std::string shipAsteroid(SpaceShip& ship, Asteroid& rock) +{ +// std::cout << rock.stringize() << " has pulverized " << ship.stringize() << std::endl; + return "shipAsteroid"; +} + +std::string militaryShipAsteroid(MilitaryShip& ship, Asteroid& rock) +{ +// std::cout << rock.stringize() << " has severely damaged " << ship.stringize() << std::endl; + return "militaryShipAsteroid"; +} + +std::string shipStation(SpaceShip& ship, SpaceStation& dock) +{ +// std::cout << ship.stringize() << " has docked at " << dock.stringize() << std::endl; + return "shipStation"; +} + +std::string asteroidStation(Asteroid& rock, SpaceStation& dock) +{ +// std::cout << rock.stringize() << " has damaged " << dock.stringize() << std::endl; + return "asteroidStation"; +} + +/*------------------------------- Test code --------------------------------*/ +namespace tut +{ + struct dispatch_data + { + dispatch_data(): + home(new SpaceStation("Terra Station")), + obstacle(new Asteroid("Ganymede")), + tug(new CommercialShip("Pilotfish")), + patrol(new MilitaryShip("Enterprise")) + {} + + // Instantiate and populate the DoubleDispatch object. + typedef LLDoubleDispatch<std::string, GameObject> DD; + DD dispatcher; + + // Instantiate a few GameObjects. Make sure we refer to them + // polymorphically, and don't let them leak. + std::auto_ptr<GameObject> home; + std::auto_ptr<GameObject> obstacle; + std::auto_ptr<GameObject> tug; + std::auto_ptr<GameObject> patrol; + + // prototype objects + Asteroid dummyAsteroid; + SpaceShip dummyShip; + MilitaryShip dummyMilitary; + CommercialShip dummyCommercial; + SpaceStation dummyStation; + }; + typedef test_group<dispatch_data> dispatch_group; + typedef dispatch_group::object dispatch_object; + tut::dispatch_group ddgr("double dispatch"); + + template<> template<> + void dispatch_object::test<1>() + { + // Describe param types using explicit DD::Type objects + // (order-sensitive add() variant) + dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(), shipAsteroid, true); + // naive adding, won't work + dispatcher.add(DD::Type<MilitaryShip>(), DD::Type<Asteroid>(), militaryShipAsteroid, true); + dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(), shipStation, true); + dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(), asteroidStation, true); + + // Try colliding them. + ensure_equals(dispatcher(*home, *tug), // reverse params, SpaceShip subclass + "shipStation"); + ensure_equals(dispatcher(*patrol, *home), // forward params, SpaceShip subclass + "shipStation"); + ensure_equals(dispatcher(*obstacle, *home), // forward params + "asteroidStation"); + ensure_equals(dispatcher(*home, *obstacle), // reverse params + "asteroidStation"); + ensure_equals(dispatcher(*tug, *obstacle), // forward params, SpaceShip subclass + "shipAsteroid"); + ensure_equals(dispatcher(*obstacle, *patrol), // reverse params, SpaceShip subclass + // won't use militaryShipAsteroid() because it was added + // in wrong order + "shipAsteroid"); + } + + template<> template<> + void dispatch_object::test<2>() + { + // Describe param types using explicit DD::Type objects + // (order-sensitive add() variant) + // adding in correct order + dispatcher.add(DD::Type<MilitaryShip>(), DD::Type<Asteroid>(), militaryShipAsteroid, true); + dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(), shipAsteroid, true); + dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(), shipStation, true); + dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(), asteroidStation, true); + + ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid"); + ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid"); + } + + template<> template<> + void dispatch_object::test<3>() + { + // Describe param types with actual prototype instances + // (order-insensitive add() variant) + dispatcher.add(dummyMilitary, dummyAsteroid, militaryShipAsteroid); + dispatcher.add(dummyShip, dummyAsteroid, shipAsteroid); + dispatcher.add(dummyShip, dummyStation, shipStation); + dispatcher.add(dummyAsteroid, dummyStation, asteroidStation); + + ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid"); + ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid"); + ensure_equals(dispatcher(*obstacle, *patrol), ""); + } + + template<> template<> + void dispatch_object::test<4>() + { + // Describe param types with actual prototype instances + // (order-insensitive add() variant) + dispatcher.add(dummyShip, dummyAsteroid, shipAsteroid); + // Even if we add the militaryShipAsteroid in the wrong order, it + // should still work. + dispatcher.add(dummyMilitary, dummyAsteroid, militaryShipAsteroid); + dispatcher.add(dummyShip, dummyStation, shipStation); + dispatcher.add(dummyAsteroid, dummyStation, asteroidStation); + + ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid"); + ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid"); + } + + template<> template<> + void dispatch_object::test<5>() + { + dispatcher.add<SpaceShip, Asteroid>(shipAsteroid); + dispatcher.add<MilitaryShip, Asteroid>(militaryShipAsteroid); + dispatcher.add<SpaceShip, SpaceStation>(shipStation); + dispatcher.add<Asteroid, SpaceStation>(asteroidStation); + + ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid"); + ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid"); + } +} // namespace tut |