From c9bf26d8c41d245b2d6d2e6c01a077898482fb3f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 29 Mar 2010 06:56:42 +0200 Subject: [PATCH] EntryID finished and passes unit test --- src/lib/meta/trait-special.hpp | 53 +++++++++ src/proc/asset/entry-id.hpp | 52 +++++---- tests/41asset.tests | 10 +- tests/components/proc/asset/entry-id-test.cpp | 106 ++++++++++++------ 4 files changed, 159 insertions(+), 62 deletions(-) create mode 100644 src/lib/meta/trait-special.hpp diff --git a/src/lib/meta/trait-special.hpp b/src/lib/meta/trait-special.hpp new file mode 100644 index 000000000..1fbb4b157 --- /dev/null +++ b/src/lib/meta/trait-special.hpp @@ -0,0 +1,53 @@ +/* + TRAIT-SPECIAL.hpp - supplement, not so commonly used definitions and specialisations + + Copyright (C) Lumiera.org + 2010, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +#ifndef LIB_META_TRAIT_SPECIAL_H +#define LIB_META_TRAIT_SPECIAL_H + + +#include "lib/meta/trait.hpp" + +#include + + +namespace lib { +namespace meta { + + + /** the tr1 hashtable(s) are notoriously difficult to handle + * when it comes to detecting capabilities by metaprogramming, + * because the're built up from a generic baseclass and especially + * inherit their iterator-types as template specialisations. + * + * Thus we provide fixed diagnostics to bypass the traits check. + */ + template + struct can_STL_ForEach > + { + enum { value = true }; + }; + + + +}} // namespace lib::meta +#endif diff --git a/src/proc/asset/entry-id.hpp b/src/proc/asset/entry-id.hpp index d99f51928..cdceb797a 100644 --- a/src/proc/asset/entry-id.hpp +++ b/src/proc/asset/entry-id.hpp @@ -27,12 +27,12 @@ ** overhead of creating individual assets for each entry. The datafields in the symbolic part ** of the ID are similar to the asset identity tuple; the idea is to promote individual entries ** to full fledged assets on demand. Alongside with the symbolic identity, which can be reduced - ** to just a Symbol and a type identifier, we store the derived hash value as LUID. + ** to just a Symbol and (compile time) type information, we store the derived hash value as LUID. ** ** @note as of 3/2010 this is an experimental setup and exists somewhat in parallel ** to the assets. We're still in the process of finding out what's really required ** to keep track of all the various kinds of objects. - ** + ** ** @see asset::Asset::Ident ** @see entry-id-test.cpp ** @@ -54,19 +54,19 @@ namespace asset { - + using std::string; using std::ostream; using lumiera::error::LUMIERA_ERROR_WRONG_TYPE; - + namespace idi { using lib::hash::LuidH; - typedef size_t HashVal; + /** build up a hash value, packaged as LUID. * @param sym symbolic ID-string to be hashed * @param seed (optional) hash value to combine with the sym. @@ -110,20 +110,26 @@ namespace asset { string symbol_; LuidH hash_; - public: - explicit - BareEntryID (string const& symbolID, idi::HashVal seed =0) /////////////TODO couldn't this be protected? + protected: + /** + * Not to be created stand-alone. + * derived classes feed down the specific type information + * encoded into a hash seed. Thus even the same symbolicID + * generates differing hash-IDs for different type parameters + */ + BareEntryID (string const& symbolID, idi::HashVal seed =0) : symbol_(util::sanitise(symbolID)) , hash_(idi::buildHash (symbol_, seed)) { } - /* default copy- and assignable */ + public: + /* default copyable and assignable */ bool isValid() const { - return bool(hash_); + return bool(hash_); } string const& @@ -135,9 +141,10 @@ namespace asset { LuidH const& getHash() const { - return hash_; + return hash_; } + /** using BareEntryID derived objects as keys within tr1::unordered_map */ struct UseEmbeddedHash : public std::unary_function @@ -196,16 +203,13 @@ namespace asset { getIdent() const { Category cat (STRUCT, idi::StructTraits::catFolder); - return Asset::Ident (this->getSym(), cat); + return Asset::Ident (this->getSym(), cat); } static idi::HashVal getTypeHash() { - Category cat (STRUCT, idi::StructTraits::catFolder); - asset::Category const& catz(cat); - size_t haha = hash_value (catz); - return haha; + return hash_value (Category (STRUCT, idi::StructTraits::catFolder)); } @@ -217,7 +221,7 @@ namespace asset { static bool canRecast (BareEntryID const& bID) { - return bID.getHash() == buildHash (bID.getSym(), getTypeHash()); + return bID.getHash() == idi::buildHash (bID.getSym(), getTypeHash()); } static EntryID @@ -235,9 +239,9 @@ namespace asset { { return "ID<"+idi::StructTraits::idSymbol+">-"+EntryID::getSym(); } - + friend ostream& operator<< (ostream& os, EntryID const& id) { return os << string(id); } - friend bool operator< (EntryID const& i1, EntryID const& i2) { return i1.getSym() < i2.getSym(); } + friend bool operator< (EntryID const& i1, EntryID const& i2) { return i1.getSym() < i2.getSym(); } }; @@ -246,9 +250,9 @@ namespace asset { { return i1.getHash() == i2.getHash(); } - - + + /** try to upcast this BareEntryID to a fully typed EntryID. * Effectively, this is the attempt to reverse a type erasure; @@ -266,8 +270,8 @@ namespace asset { { return EntryID::recast(*this); } - - - + + + } // namespace asset #endif diff --git a/tests/41asset.tests b/tests/41asset.tests index da0ac3ae1..1c5736fb9 100644 --- a/tests/41asset.tests +++ b/tests/41asset.tests @@ -33,7 +33,15 @@ return: 0 END -PLANNED "plain entry-ID" EntryID_test <-..asset.+Dummy.\.001 +out: ID<..asset.+Dummy.>-strange +out: ID<..asset.+Dummy.>-..asset.+Dummy.\.002 +out: ID-track\.001 +out: ID-track\.002 +out: ID-special +out: sizeof\( ..asset.+EntryID.+mobject.+session.+Track.+ \) = +out: sizeof\( ..asset.+BareEntryID. \) = return: 0 END diff --git a/tests/components/proc/asset/entry-id-test.cpp b/tests/components/proc/asset/entry-id-test.cpp index b0acb7d29..4c266f30e 100644 --- a/tests/components/proc/asset/entry-id-test.cpp +++ b/tests/components/proc/asset/entry-id-test.cpp @@ -27,6 +27,7 @@ #include "proc/asset/entry-id.hpp" #include "proc/mobject/session/clip.hpp" #include "proc/mobject/session/track.hpp" +#include "lib/meta/trait-special.hpp" #include "lib/util-foreach.hpp" #include "lib/symbol.hpp" @@ -37,7 +38,7 @@ using lib::test::showSizeof; using lib::test::randStr; using util::isSameObject; -using util::for_each; +using util::and_all; using lib::Literal; using std::string; using std::cout; @@ -58,6 +59,9 @@ namespace test { } + + + /*************************************************************************** * @test proof-of-concept test for a combined symbolic and hash based ID. * - create some symbolic IDs @@ -65,7 +69,7 @@ namespace test { * - check comparisons * - check hashing * - use the embedded hash ID (LUID) as hashtable key - * + * * @see lib::HashIndexed::Id * @see mobject::Placement real world usage example */ @@ -115,11 +119,6 @@ namespace test { cout << tID2 << endl; cout << tID3 << endl; - cout << showSizeof() << endl; - cout << showSizeof() << endl; - CHECK (sizeof(TrackID) == sizeof(BareEntryID)); - CHECK (sizeof(TrackID) == sizeof(lumiera_uid) + sizeof(void*)); - DummyID x (dID2); // copy ctor CHECK (x == dID2); CHECK (!isSameObject (x, dID2)); @@ -129,22 +128,22 @@ namespace test { void checkBasicProperties () { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #582 TrackID tID(" test ⚡ ☠ ☭ ⚡ track "); CHECK (tID.getIdent() == Asset::Ident("test_track", Category(STRUCT,"tracks"), "lumi", 0)); CHECK (tID.getHash() == TrackID("☢ test ☢ track ☢").getHash()); - CHECK (isSameObject (tID.getSym(), tID.getIdent().name)); - CHECK ("tracks" == TrackID().getIdent().category.path); - CHECK ("clips" == ClipID().getIdent().category.path); + CHECK (tID.getSym() == tID.getIdent().name); + CHECK (TrackID().getIdent().category == Category (STRUCT,"tracks")); + CHECK (ClipID().getIdent().category == Category (STRUCT,"clips")); - CHECK (ClipID().getSym() < ClipID().getSym()); + ClipID cID2,cID3; + CHECK (cID2.getSym() < cID3.getSym()); CHECK (ClipID("x").getSym() == ClipID(" x ").getSym()); - for (uint i=0; i<1000; ++i) + for (uint i=0; i<10000; ++i) { - TrackID arbitrary(randStr(20)); + TrackID arbitrary(randStr(30)); CHECK (0 < arbitrary.getHash()); CHECK (tID.getHash() != arbitrary.getHash()); tID = arbitrary; @@ -153,15 +152,16 @@ namespace test { CHECK (tID.getIdent()== arbitrary.getIdent()); } - CHECK (sizeof (tID) == sizeof(hash::LuidH) + sizeof(Literal)); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #582 + cout << showSizeof() << endl; + cout << showSizeof() << endl; + CHECK (sizeof(TrackID) == sizeof(BareEntryID)); + CHECK (sizeof(TrackID) == sizeof(lumiera_uid) + sizeof(void*)); } void checkComparisions () { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #582 TrackID tID1("a1"); TrackID tID2("a1"); TrackID tID3("a2"); @@ -178,15 +178,23 @@ namespace test { CHECK (tID4 >= tID3); CHECK (tID4 > tID3); - CHECK (TrackID() < TrackID()); -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #582 + TrackID trackID1, trackID2; + CHECK (trackID1 < trackID2); // auto generated IDs are prefix + running counter } + + /** @test handling of EntryIDs through their common base class, + * which means erasing the specific type information. + * While this type information can't be recovered + * after erasure, we can try to upcast back + * to a known type; this upcast is safe, + * because the embedded hash-ID + * is based on the type info. + */ void checkErasure () { -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #582 TrackID tID("suspicious"); ClipID cID("suspicious"); @@ -201,35 +209,52 @@ namespace test { CHECK (bIDt.getSym() == bIDc.getSym()); CHECK ("suspicious" == bIDc.getSym()); - TrackID tIDnew = bIDt.recast(); - ClipID cIDnew = bIDc.recast(); + using mobject::session::Track; + using mobject::session::Clip; + TrackID tIDnew = bIDt.recast(); + ClipID cIDnew = bIDc.recast(); CHECK (tIDnew == tID); CHECK (cIDnew == cID); - VERIFY_ERROR (WRONG_TYPE, bIDt.recast()); - VERIFY_ERROR (WRONG_TYPE, bIDc.recast()); + VERIFY_ERROR (WRONG_TYPE, bIDt.recast()); + VERIFY_ERROR (WRONG_TYPE, bIDc.recast()); VERIFY_ERROR (WRONG_TYPE, bIDc.recast()); + VERIFY_ERROR (WRONG_TYPE, bIDt.recast()); - CHECK (tID == bIDt.recast()); // alternatively can specify type directly - CHECK (tID == TrackID::recast (bIDt)); // equivalent static API on typed subclass + CHECK (tID == TrackID::recast (bIDt)); // equivalent static API on typed subclass + VERIFY_ERROR (WRONG_TYPE, TrackID::recast(bIDc)); + VERIFY_ERROR (WRONG_TYPE, ClipID::recast(bIDt)); + VERIFY_ERROR (WRONG_TYPE, DummyID::recast(bIDc)); + VERIFY_ERROR (WRONG_TYPE, DummyID::recast(bIDt)); - lumiera_uid plainLUID; - lumiera_uid_copy (&plainLUID, tID.getHash().get()); + // mixed equality comparisons (based on the hash) + BareEntryID bIDt_copy (bIDt); + CHECK (bIDt == bIDt_copy); + CHECK (!isSameObject (bIDt, bIDt_copy)); - CHECK (cID == ClipID::recast ("suspicious", plainLUID)); // upcast from type erased data (note: changed type) -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #582 + CHECK (tID != bIDc); + CHECK (cID != bIDt_copy); + CHECK (tID == bIDt_copy); + + CHECK (bIDt == TrackID ("suspicious")); + CHECK (bIDt != ClipID ("suspicious")); + CHECK (bIDc == ClipID ("suspicious")); + CHECK (TrackID ("suspicious") != ClipID ("suspicious")); } + + //---key--+-value-+-hash-function--- + typedef std::tr1::unordered_map Hashtable; + + /** @test build a hashtable, using EntryID as key, + * thereby using the embedded hash-ID */ void buildHashtable () - { //---key--+-value-+-hash-function--- -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #582 - typedef std::tr1::unordered_map Hashtable; - + { Hashtable tab; - for (uint i=0; i<1000; ++i) + for (uint i=0; i<1000; ++i) /////////////////////////////////////TICKET #587 we get strange collisions for 10000 entries { DummyID dummy; tab[dummy] = string(dummy); @@ -237,9 +262,16 @@ namespace test { CHECK (1000 == tab.size()); - for_each (tab.getKeys(), tab[_1] == string(_1)); /////TODO use boost::lambda to make this happen.... -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #582 + CHECK (and_all (tab, verifyEntry)); } + + + static bool + verifyEntry (Hashtable::value_type entry) + { + return entry.second == string(entry.first); + } + };