From 49127f6a926919a20d80d109526bfa68db087e83 Mon Sep 17 00:00:00 2001 From: Martin Natano Date: Thu, 9 Jun 2022 11:59:40 +0200 Subject: [PATCH] Remove RageUtil_CachedObject I don't notice any performance drawback in gameplay, but it was massively slowing down ScreenReloadSongs. --- src/CMakeData-rage.cmake | 2 - src/Course.cpp | 2 +- src/Course.h | 3 - src/CourseUtil.cpp | 13 +-- src/CourseUtil.h | 4 +- src/RageUtil_CachedObject.cpp | 72 ------------- src/RageUtil_CachedObject.h | 193 ---------------------------------- src/Song.h | 3 - src/SongManager.cpp | 4 +- src/SongUtil.cpp | 21 ++-- src/SongUtil.h | 4 +- src/Steps.h | 5 +- src/StepsUtil.cpp | 6 -- src/StepsUtil.h | 4 +- src/Trail.h | 6 +- src/TrailUtil.cpp | 10 +- src/TrailUtil.h | 5 +- 17 files changed, 19 insertions(+), 338 deletions(-) delete mode 100644 src/RageUtil_CachedObject.cpp delete mode 100644 src/RageUtil_CachedObject.h diff --git a/src/CMakeData-rage.cmake b/src/CMakeData-rage.cmake index ff5b113f75..8a146c9658 100644 --- a/src/CMakeData-rage.cmake +++ b/src/CMakeData-rage.cmake @@ -3,7 +3,6 @@ list(APPEND SMDATA_RAGE_UTILS_SRC "RageUtil.cpp" "RageUtil_BackgroundLoader.cpp" - "RageUtil_CachedObject.cpp" "RageUtil_CharConversions.cpp" "RageUtil_FileDB.cpp" "RageUtil_WorkerThread.cpp") @@ -13,7 +12,6 @@ list(APPEND SMDATA_RAGE_UTILS_HPP "RageUtil_AutoPtr.h" # TODO: Remove the need for this and replace # with c++11 smart pointers "RageUtil_BackgroundLoader.h" - "RageUtil_CachedObject.h" "RageUtil_CharConversions.h" "RageUtil_CircularBuffer.h" "RageUtil_FileDB.h" diff --git a/src/Course.cpp b/src/Course.cpp index 93ed2950f7..4de8a617d5 100644 --- a/src/Course.cpp +++ b/src/Course.cpp @@ -94,7 +94,7 @@ Course::Course(): m_bIsAutogen(false), m_sPath(""), m_sMainTitle(""), m_bIncomplete(false), m_vEntries(), m_SortOrder_TotalDifficulty(0), m_SortOrder_Ranking(0), m_LoadedFromProfile(ProfileSlot_Invalid), m_TrailCache(), m_iTrailCacheSeed(0), m_RadarCache(), - m_setStyles(), m_CachedObject() + m_setStyles() { FOREACH_ENUM( Difficulty,dc) m_iCustomMeter[dc] = -1; diff --git a/src/Course.h b/src/Course.h index abd780f8e4..1f7cb802cb 100644 --- a/src/Course.h +++ b/src/Course.h @@ -6,7 +6,6 @@ #include "EnumHelper.h" #include "Trail.h" #include "RageTypes.h" -#include "RageUtil_CachedObject.h" #include "SongUtil.h" #include "StepsUtil.h" #include @@ -204,8 +203,6 @@ public: // Preferred styles: set m_setStyles; - - CachedObject m_CachedObject; }; #endif diff --git a/src/CourseUtil.cpp b/src/CourseUtil.cpp index cb566fa54a..f8433ef61d 100644 --- a/src/CourseUtil.cpp +++ b/src/CourseUtil.cpp @@ -537,17 +537,11 @@ void CourseID::FromCourse( const Course *p ) // Strip off leading "/". 2005/05/21 file layer changes added a leading slash. if( sPath.Left(1) == "/" ) sPath.erase( sPath.begin() ); - - m_Cache.Unset(); } Course *CourseID::ToCourse() const { Course *pCourse = nullptr; - if(m_Cache.Get(&pCourse)) - { - return pCourse; - } if(!sPath.empty()) { // HACK for backwards compatibility: @@ -558,17 +552,13 @@ Course *CourseID::ToCourse() const slash_path = "/" + slash_path; } - if(pCourse == nullptr) - { - pCourse = SONGMAN->GetCourseFromPath(slash_path); - } + pCourse = SONGMAN->GetCourseFromPath(slash_path); } if( pCourse == nullptr && !sFullTitle.empty() ) { pCourse = SONGMAN->GetCourseFromName( sFullTitle ); } - m_Cache.Set( pCourse ); return pCourse; } @@ -592,7 +582,6 @@ void CourseID::LoadFromNode( const XNode* pNode ) sPath = RString(); if( !pNode->GetAttrValue("Path", sPath) ) pNode->GetAttrValue( "FullTitle", sFullTitle ); - m_Cache.Unset(); // HACK for backwards compatibility: /AdditionalCourses has been merged into /Courses if (sPath.Left(18) == "AdditionalCourses/") diff --git a/src/CourseUtil.h b/src/CourseUtil.h index 2afcd874ed..e681f918b5 100644 --- a/src/CourseUtil.h +++ b/src/CourseUtil.h @@ -3,7 +3,6 @@ #include "GameConstantsAndTypes.h" #include "Difficulty.h" -#include "RageUtil_CachedObject.h" class Course; class Profile; @@ -72,7 +71,7 @@ namespace EditCourseUtil class CourseID { public: - CourseID(): sPath(""), sFullTitle(""), m_Cache() { Unset(); } + CourseID(): sPath(""), sFullTitle("") { Unset(); } void Unset() { FromCourse(nullptr); } void FromCourse( const Course *p ); Course *ToCourse() const; @@ -92,7 +91,6 @@ public: private: RString sPath; RString sFullTitle; - mutable CachedObjectPointer m_Cache; }; #endif diff --git a/src/RageUtil_CachedObject.cpp b/src/RageUtil_CachedObject.cpp deleted file mode 100644 index bc2bbc6373..0000000000 --- a/src/RageUtil_CachedObject.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "global.h" -#include "RageUtil_CachedObject.h" -#include "RageThreads.h" - -#if 0 -/* Example use: */ -struct Object -{ - CachedObject m_CachedObject; - - Object() { } -}; - -struct User -{ - CachedObjectPointer cache; - - Object *foobar() - { - Object *p; - if( !cache.Get(&p) ) - { - p = nullptr; - cache.Set(p); - } - return p; - } -}; - -void test() -{ - Object p; - User gar; - gar.foobar(); -} -#endif - -static RageMutex m_Mutex("CachedObjects"); -void CachedObjectHelpers::Lock() -{ - m_Mutex.Lock(); -} - -void CachedObjectHelpers::Unlock() -{ - m_Mutex.Unlock(); -} - -/* - * (c) 2007 Glenn Maynard - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons to - * whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF - * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS - * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT - * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS - * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ diff --git a/src/RageUtil_CachedObject.h b/src/RageUtil_CachedObject.h deleted file mode 100644 index 5b8f4008de..0000000000 --- a/src/RageUtil_CachedObject.h +++ /dev/null @@ -1,193 +0,0 @@ -#ifndef RAGE_UTIL_CACHED_OBJECT_H -#define RAGE_UTIL_CACHED_OBJECT_H - -#include - -template -class CachedObjectPointer; -/** @brief Utilities for working with the - CachedObjects. */ -namespace CachedObjectHelpers -{ - void Lock(); - void Unlock(); -} - -/** @brief Cached object pointers with automatic invalidation. */ -template -class CachedObject -{ -public: - CachedObject(): m_pObject(nullptr) - { - /* A new object is being constructed, so invalidate negative caching. */ - ClearCacheNegative(); - } - - CachedObject( const CachedObject &cpy ): m_pObject(nullptr) - { - ClearCacheNegative(); - } - - ~CachedObject() - { - if( m_pObject != nullptr ) - ClearCacheSpecific( m_pObject ); - } - - CachedObject &operator=( const CachedObject &rhs ) { return *this; } - - /* Clear all cached entries for this type. */ - static void ClearCacheAll() - { - CachedObjectHelpers::Lock(); - for( typename set::iterator p = m_spObjectPointers.begin(); p != m_spObjectPointers.end(); ++p ) - { - (*p)->m_pCache = nullptr; - (*p)->m_bCacheIsSet = false; - } - CachedObjectHelpers::Unlock(); - } - - /* Clear all cached entries pointing to a specific object. */ - static void ClearCacheSpecific( const T *pObject ) - { - CachedObjectHelpers::Lock(); - for( typename set::iterator p = m_spObjectPointers.begin(); p != m_spObjectPointers.end(); ++p ) - { - if( (*p)->m_pCache == pObject ) - { - (*p)->m_pCache = nullptr; - (*p)->m_bCacheIsSet = false; - } - } - CachedObjectHelpers::Unlock(); - } - - /* Clear all negative cached entries of this type. */ - static void ClearCacheNegative() - { - CachedObjectHelpers::Lock(); - for( typename set::iterator p = m_spObjectPointers.begin(); p != m_spObjectPointers.end(); ++p ) - { - if( (*p)->m_pCache == nullptr ) - (*p)->m_bCacheIsSet = false; - } - CachedObjectHelpers::Unlock(); - } - -private: - typedef CachedObjectPointer ObjectPointer; - friend class CachedObjectPointer; - - static void Register( ObjectPointer *p ) - { - m_spObjectPointers.insert( p ); - } - - static void Unregister( ObjectPointer *p ) - { - typename set::iterator it = m_spObjectPointers.find( p ); - ASSERT( it != m_spObjectPointers.end() ); - m_spObjectPointers.erase( it ); - } - - /* This points to the actual T this object is contained in. This is set - * the first time CachedObjectPointer::Set() is called for this object. - * That's more convenient than setting it ourselves; we don't need to - * do anything special in T's copy ctor. This works because there's no - * need to clear cache for an object before any CachedObjectPointers have - * ever been set for it. */ - const T *m_pObject; - static set m_spObjectPointers; -}; -template set *> CachedObject::m_spObjectPointers = set *>(); - -template -class CachedObjectPointer -{ -public: - typedef CachedObject Object; - - CachedObjectPointer() : m_pCache(nullptr), m_bCacheIsSet(false) - { - Object::Register( this ); - } - - CachedObjectPointer( const CachedObjectPointer &cpy ): - m_pCache(cpy.m_pCache), m_bCacheIsSet(cpy.m_bCacheIsSet) - { - CachedObjectHelpers::Lock(); - Object::Register( this ); - CachedObjectHelpers::Unlock(); - } - - ~CachedObjectPointer() - { - Object::Unregister( this ); - } - - bool Get( T **pRet ) const - { - CachedObjectHelpers::Lock(); - if( !m_bCacheIsSet ) - { - CachedObjectHelpers::Unlock(); - return false; - } - *pRet = m_pCache; - CachedObjectHelpers::Unlock(); - return true; - } - - void Set( T *p ) - { - CachedObjectHelpers::Lock(); - m_pCache = p; - m_bCacheIsSet = true; - if( p != nullptr ) - p->m_CachedObject.m_pObject = p; - CachedObjectHelpers::Unlock(); - } - - void Unset() - { - CachedObjectHelpers::Lock(); - m_pCache = nullptr; - m_bCacheIsSet = false; - CachedObjectHelpers::Unlock(); - } - -private: - friend class CachedObject; - - T *m_pCache; - bool m_bCacheIsSet; -}; - -#endif - -/* - * (c) 2007 Glenn Maynard - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons to - * whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF - * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS - * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT - * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS - * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ diff --git a/src/Song.h b/src/Song.h index 112ef03a4d..92981e6f70 100644 --- a/src/Song.h +++ b/src/Song.h @@ -6,7 +6,6 @@ #include "Difficulty.h" #include "EnumHelper.h" #include "RageUtil_AutoPtr.h" -#include "RageUtil_CachedObject.h" #include "RageTypes.h" #include "Steps.h" #include @@ -457,8 +456,6 @@ public: * means "this note doesn't have a keysound". */ vector m_vsKeysoundFile; - CachedObject m_CachedObject; - RString GetAttackString() const { return join(":", this->m_sAttackString); diff --git a/src/SongManager.cpp b/src/SongManager.cpp index 7f790f4ed1..11944d7b65 100644 --- a/src/SongManager.cpp +++ b/src/SongManager.cpp @@ -75,9 +75,9 @@ SongManager::SongManager() } NUM_SONG_GROUP_COLORS .Load( "SongManager", "NumSongGroupColors" ); - SONG_GROUP_COLOR .Load( "SongManager", SONG_GROUP_COLOR_NAME, NUM_SONG_GROUP_COLORS ); + SONG_GROUP_COLOR .Load( "SongManager", SONG_GROUP_COLOR_NAME, NUM_SONG_GROUP_COLORS ); NUM_COURSE_GROUP_COLORS .Load( "SongManager", "NumCourseGroupColors" ); - COURSE_GROUP_COLOR .Load( "SongManager", COURSE_GROUP_COLOR_NAME, NUM_COURSE_GROUP_COLORS ); + COURSE_GROUP_COLOR .Load( "SongManager", COURSE_GROUP_COLOR_NAME, NUM_COURSE_GROUP_COLORS ); num_profile_song_group_colors.Load("SongManager", "NumProfileSongGroupColors"); profile_song_group_colors.Load("SongManager", profile_song_group_color_name, num_profile_song_group_colors); } diff --git a/src/SongUtil.cpp b/src/SongUtil.cpp index 0ea0f34152..d715a733cd 100644 --- a/src/SongUtil.cpp +++ b/src/SongUtil.cpp @@ -1110,27 +1110,21 @@ void SongID::FromSong( const Song *p ) // Strip off leading "/". 2005/05/21 file layer changes added a leading slash. if( sDir.Left(1) == "/" ) sDir.erase( sDir.begin() ); - - m_Cache.Unset(); } Song *SongID::ToSong() const { Song *pRet = nullptr; - if( !m_Cache.Get(&pRet) ) + if(!sDir.empty()) { - if(!sDir.empty()) + // HACK for backwards compatibility: Re-add the leading "/". + // 2005/05/21 file layer changes added a leading slash. + RString sDir2 = sDir; + if(sDir2.Left(1) != "/") { - // HACK for backwards compatibility: Re-add the leading "/". - // 2005/05/21 file layer changes added a leading slash. - RString sDir2 = sDir; - if(sDir2.Left(1) != "/") - { - sDir2 = "/" + sDir2; - } - pRet = SONGMAN->GetSongFromDir(sDir2); + sDir2 = "/" + sDir2; } - m_Cache.Set( pRet ); + pRet = SONGMAN->GetSongFromDir(sDir2); } return pRet; } @@ -1146,7 +1140,6 @@ void SongID::LoadFromNode( const XNode* pNode ) { ASSERT( pNode->GetName() == "Song" ); pNode->GetAttrValue("Dir", sDir); - m_Cache.Unset(); // HACK for backwards compatibility: /AdditionalSongs has been merged into /Songs if (sDir.Left(16) == "AdditionalSongs/") diff --git a/src/SongUtil.h b/src/SongUtil.h index 44a686c4e2..d45ecb853e 100644 --- a/src/SongUtil.h +++ b/src/SongUtil.h @@ -5,7 +5,6 @@ #include "GameConstantsAndTypes.h" #include "Difficulty.h" -#include "RageUtil_CachedObject.h" #include class Song; @@ -195,14 +194,13 @@ namespace SongUtil class SongID { RString sDir; - mutable CachedObjectPointer m_Cache; public: /** * @brief Set up the SongID with default values. * * This used to call Unset() to do the same thing. */ - SongID(): sDir(""), m_Cache() { m_Cache.Unset(); } + SongID(): sDir("") {} void Unset() { FromSong(nullptr); } void FromSong( const Song *p ); Song *ToSong() const; diff --git a/src/Steps.h b/src/Steps.h index 991d07fe6f..c2143b8f3a 100644 --- a/src/Steps.h +++ b/src/Steps.h @@ -8,7 +8,6 @@ #include "RadarValues.h" #include "Difficulty.h" #include "RageUtil_AutoPtr.h" -#include "RageUtil_CachedObject.h" #include "TimingData.h" class Profile; @@ -112,7 +111,7 @@ public: vector m_sAttackString; RString GetChartName() const { return parent ? Real()->GetChartName() : this->chartName; } - void SetChartName(const RString name) { this->chartName = name; } + void SetChartName(const RString name) { this->chartName = name; } void SetFilename( RString fn ) { m_sFilename = fn; } RString GetFilename() const { return m_sFilename; } void SetSavedToDisk( bool b ) { DeAutogen(); m_bSavedToDisk = b; } @@ -196,8 +195,6 @@ public: /** @brief The Song these Steps are associated with */ Song *m_pSong; - CachedObject m_CachedObject; - void SetDisplayBPM(const DisplayBPM type) { this->displayBPMType = type; } DisplayBPM GetDisplayBPM() const { return this->displayBPMType; } void SetMinBPM(const float f) { this->specifiedBPMMin = f; } diff --git a/src/StepsUtil.cpp b/src/StepsUtil.cpp index 7f82eb9112..eb80ff7cec 100644 --- a/src/StepsUtil.cpp +++ b/src/StepsUtil.cpp @@ -262,8 +262,6 @@ void StepsID::FromSteps( const Steps *p ) uHash = 0; } } - - m_Cache.Unset(); } /* XXX: Don't allow duplicate edit descriptions, and don't allow edit descriptions @@ -295,8 +293,6 @@ Steps *StepsID::ToSteps( const Song *p, bool bAllowNull ) const if( !bAllowNull && pRet == nullptr ) FAIL_M( ssprintf("%i, %i, \"%s\"", st, dc, sDescription.c_str()) ); - - m_Cache.Set( pRet ); return pRet; } @@ -338,8 +334,6 @@ void StepsID::LoadFromNode( const XNode* pNode ) sDescription = ""; uHash = 0; } - - m_Cache.Unset(); } RString StepsID::ToString() const diff --git a/src/StepsUtil.h b/src/StepsUtil.h index 21f7c0d0fc..2cba43ff6b 100644 --- a/src/StepsUtil.h +++ b/src/StepsUtil.h @@ -3,7 +3,6 @@ #include "GameConstantsAndTypes.h" #include "Difficulty.h" -#include "RageUtil_CachedObject.h" class Steps; class Song; @@ -167,7 +166,6 @@ class StepsID Difficulty dc; RString sDescription; unsigned uHash; - mutable CachedObjectPointer m_Cache; public: /** @@ -176,7 +174,7 @@ public: * This used to call Unset(), which set the variables to * the same thing. */ StepsID(): st(StepsType_Invalid), dc(Difficulty_Invalid), - sDescription(""), uHash(0), m_Cache() {} + sDescription(""), uHash(0) {} void Unset() { FromSteps(nullptr); } void FromSteps( const Steps *p ); Steps *ToSteps( const Song *p, bool bAllowNull ) const; diff --git a/src/Trail.h b/src/Trail.h index e447496216..5366727148 100644 --- a/src/Trail.h +++ b/src/Trail.h @@ -4,7 +4,6 @@ #include "Attack.h" #include "RadarValues.h" #include "Difficulty.h" -#include "RageUtil_CachedObject.h" class Song; class Steps; @@ -73,8 +72,7 @@ public: m_CourseType(CourseType_Invalid), m_CourseDifficulty(Difficulty_Invalid), m_vEntries(), m_iSpecifiedMeter(-1), - m_bRadarValuesCached(false), m_CachedRadarValues(), - m_CachedObject() {} + m_bRadarValuesCached(false), m_CachedRadarValues() {} void Init() { m_StepsType = StepsType_Invalid; @@ -93,8 +91,6 @@ public: bool IsSecret() const; bool ContainsSong( const Song *pSong ) const; - CachedObject m_CachedObject; - // Lua void PushSelf( lua_State *L ); }; diff --git a/src/TrailUtil.cpp b/src/TrailUtil.cpp index adc16d974a..877a4dd42c 100644 --- a/src/TrailUtil.cpp +++ b/src/TrailUtil.cpp @@ -32,7 +32,6 @@ void TrailID::FromTrail( const Trail *p ) st = p->m_StepsType; cd = p->m_CourseDifficulty; } - m_Cache.Unset(); } Trail *TrailID::ToTrail( const Course *p, bool bAllowNull ) const @@ -40,12 +39,8 @@ Trail *TrailID::ToTrail( const Course *p, bool bAllowNull ) const ASSERT( p != nullptr ); Trail *pRet = nullptr; - if( !m_Cache.Get(&pRet) ) - { - if( st != StepsType_Invalid && cd != Difficulty_Invalid ) - pRet = p->GetTrail( st, cd ); - m_Cache.Set( pRet ); - } + if( st != StepsType_Invalid && cd != Difficulty_Invalid ) + pRet = p->GetTrail( st, cd ); if( !bAllowNull && pRet == nullptr ) RageException::Throw( "%i, %i, \"%s\"", st, cd, p->GetDisplayFullTitle().c_str() ); @@ -74,7 +69,6 @@ void TrailID::LoadFromNode( const XNode* pNode ) pNode->GetAttrValue( "CourseDifficulty", sTemp ); cd = StringToDifficulty( sTemp ); - m_Cache.Unset(); } RString TrailID::ToString() const diff --git a/src/TrailUtil.h b/src/TrailUtil.h index 3dfd97c5fc..8eef179db2 100644 --- a/src/TrailUtil.h +++ b/src/TrailUtil.h @@ -3,7 +3,6 @@ #include "GameConstantsAndTypes.h" #include "Difficulty.h" -#include "RageUtil_CachedObject.h" class Song; class Trail; @@ -30,11 +29,9 @@ class TrailID { StepsType st; CourseDifficulty cd; - mutable CachedObjectPointer m_Cache; public: - TrailID(): st(StepsType_Invalid), cd(Difficulty_Invalid), - m_Cache() { m_Cache.Unset(); } + TrailID(): st(StepsType_Invalid), cd(Difficulty_Invalid) {} void Unset() { FromTrail(nullptr); } void FromTrail( const Trail *p ); Trail *ToTrail( const Course *p, bool bAllowNull ) const;