summaryrefslogtreecommitdiff
path: root/indra/test/lldoubledispatch_tut.cpp
diff options
context:
space:
mode:
authorSteven Bennetts <steve@lindenlab.com>2009-05-08 07:43:08 +0000
committerSteven Bennetts <steve@lindenlab.com>2009-05-08 07:43:08 +0000
commita4000c3744e42fcbb638e742f3b63fa31a0dee15 (patch)
tree7f472c30e65bbfa04ee9bc06631a1af305cc31fb /indra/test/lldoubledispatch_tut.cpp
parent6c4cadbb04d633ad7b762058bdeba6e1f650dafd (diff)
merge trunk@116587 skinning-7@119389 -> viewer-2.0.0-skinning-7
Diffstat (limited to 'indra/test/lldoubledispatch_tut.cpp')
-rwxr-xr-xindra/test/lldoubledispatch_tut.cpp245
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