diff --git a/CMakeLists.txt b/CMakeLists.txt index 250cc91..8b4d158 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ endforeach() project(TreeMaker VERSION ${TM_VERSION_MAJOR}.${TM_VERSION_MINOR}.${TM_VERSION_RELEASE} LANGUAGES CXX) # Set C++ standard -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/Source/tmModel/tmPtrClasses/tmDpptr.h b/Source/tmModel/tmPtrClasses/tmDpptr.h index 7ee46e8..4b2978d 100644 --- a/Source/tmModel/tmPtrClasses/tmDpptr.h +++ b/Source/tmModel/tmPtrClasses/tmDpptr.h @@ -143,7 +143,15 @@ Destructor. Notify the tmDpptrTarget that it no longer has me pointing at it template tmDpptr::~tmDpptr() { - if (mTarget) DstRemoveMeAsDpptrSrc(mTarget); + // Store both pointers as const before any operations + const tmDpptrSrc* const self = this; + T* const target = mTarget; + + // Validate pointers before use + if (self && target) { + mTarget = nullptr; // Clear pointer before potential deletion + DstRemoveMeAsDpptrSrc(target); + } } @@ -154,9 +162,10 @@ objects of the change in reference. template T* tmDpptr::operator=(T* t) { - if (mTarget) DstRemoveMeAsDpptrSrc(mTarget); + T* oldTarget = mTarget; // Store pointer before potential deletion mTarget = t; if (mTarget) DstAddMeAsDpptrSrc(mTarget); + if (oldTarget) DstRemoveMeAsDpptrSrc(oldTarget); return mTarget; } diff --git a/Source/tmModel/tmPtrClasses/tmDpptrArray.h b/Source/tmModel/tmPtrClasses/tmDpptrArray.h index e841d5d..38c2e44 100644 --- a/Source/tmModel/tmPtrClasses/tmDpptrArray.h +++ b/Source/tmModel/tmPtrClasses/tmDpptrArray.h @@ -206,8 +206,16 @@ Destructor. Inform all objects that they're no longer referenced by us template tmDpptrArray::~tmDpptrArray() { - for (std::size_t i = 0; i < this->size(); ++i) - DstRemoveMeAsDpptrSrc((*this)[i]); + // Store self pointer as const + const tmDpptrSrc* const self = this; + + for (std::size_t i = 0; i < this->size(); ++i) { + // Store target pointer as const + T* const target = (*this)[i]; + if (self && target) { + DstRemoveMeAsDpptrSrc(target); + } + } } @@ -251,13 +259,18 @@ Remove all copies of this item from the list. template void tmDpptrArray::erase_remove(T* pt) { - if (this->contains(pt)) { - tmArray::erase_remove(pt); - DstRemoveMeAsDpptrSrc(pt); - }; + // Store pointers as const + const tmDpptrSrc* const self = this; + T* const target = pt; + + if (self && target && this->contains(target)) { + tmArray::erase_remove(target); + DstRemoveMeAsDpptrSrc(target); + } } + /***** Replace all copies of one item with another *****/ @@ -265,15 +278,24 @@ template void tmDpptrArray::replace_with(T*& told, T*& tnew) { if (told == tnew) return; + + // Store pointers as const + const tmDpptrSrc* const self = this; + T* const oldTarget = told; + T* const newTarget = tnew; + iterator p = this->begin(); bool removedMe = false; - while ((p = find(p, this->end(), told)) != this->end()) { - if (!removedMe) { - DstRemoveMeAsDpptrSrc(*p); + + while ((p = find(p, this->end(), oldTarget)) != this->end()) { + if (!removedMe && self && oldTarget) { + DstRemoveMeAsDpptrSrc(oldTarget); removedMe = true; } - *p = tnew; - DstAddMeAsDpptrSrc(*p); + *p = newTarget; + if (self && newTarget) { + DstAddMeAsDpptrSrc(newTarget); + } } } @@ -284,8 +306,16 @@ Remove all items and inform referenced objects template void tmDpptrArray::clear() { - for (std::size_t i = 0; i < this->size(); ++i) - DstRemoveMeAsDpptrSrc((*this)[i]); + // Store self pointer as const + const tmDpptrSrc* const self = this; + + for (std::size_t i = 0; i < this->size(); ++i) { + // Store target pointer as const + T* const target = (*this)[i]; + if (self && target) { + DstRemoveMeAsDpptrSrc(target); + } + } tmArray::clear(); } @@ -307,10 +337,18 @@ Replace one item with another template void tmDpptrArray::ReplaceItemAt(std::size_t n, T* pt) { - T* qt = this->NthItem(n); - tmArray::ReplaceItemAt(n, pt); - DstAddMeAsDpptrSrc(pt); - DstRemoveMeAsDpptrSrc(qt); + // Store pointers as const + const tmDpptrSrc* const self = this; + T* const oldTarget = this->NthItem(n); + T* const newTarget = pt; + + if (self && oldTarget) { + tmArray::ReplaceItemAt(n, newTarget); + if (newTarget) { + DstAddMeAsDpptrSrc(newTarget); + } + DstRemoveMeAsDpptrSrc(oldTarget); + } } diff --git a/Source/tmModel/tmPtrClasses/tmDpptrSrc.h b/Source/tmModel/tmPtrClasses/tmDpptrSrc.h index a5cfef5..53aaa95 100644 --- a/Source/tmModel/tmPtrClasses/tmDpptrSrc.h +++ b/Source/tmModel/tmPtrClasses/tmDpptrSrc.h @@ -5,7 +5,7 @@ Purpose: Header file for class tmDpptrSrc Author: Robert J. Lang Modified by: Created: 2003-11-15 -Copyright: ©2003 Robert J. Lang. All Rights Reserved. +Copyright: 2003 Robert J. Lang. All Rights Reserved. *******************************************************************************/ #ifndef _TMDPPTRSRC_H_ @@ -25,9 +25,24 @@ class tmDpptrSrc protected: // Used by subclasses void DstAddMeAsDpptrSrc(tmDpptrTarget* aDpptrTarget) { - aDpptrTarget->AddDpptrSrc(this);}; + if (aDpptrTarget) { + aDpptrTarget->AddDpptrSrc(this); + } + }; void DstRemoveMeAsDpptrSrc(tmDpptrTarget* aDpptrTarget) { - aDpptrTarget->RemoveDpptrSrc(this);}; + // Early return for null pointer + if (!aDpptrTarget) return; + + // Store both pointers as const before any operations + tmDpptrSrc* const self = this; + tmDpptrTarget* const target = aDpptrTarget; + + // Validate pointers before use + if (self && target) { + target->RemoveDpptrSrc(self); + } + }; + // Implemented by subclasses virtual void RemoveDpptrTarget(tmDpptrTarget*) {}; private: diff --git a/Source/tmModel/tmTreeClasses/tmNode.cpp b/Source/tmModel/tmTreeClasses/tmNode.cpp index b6c783f..1242a64 100644 --- a/Source/tmModel/tmTreeClasses/tmNode.cpp +++ b/Source/tmModel/tmTreeClasses/tmNode.cpp @@ -9,7 +9,15 @@ Copyright: 2003 Robert J. Lang. All Rights Reserved. *******************************************************************************/ #include "tmNode.h" -#include "tmModel.h" +#include +#include +#include "tmConditionNodeFixed.h" +#include "tmConditionNodeOnEdge.h" +#include "tmConditionNodeSymmetric.h" +#include "tmConditionNodesCollinear.h" +#include "tmConditionNodesPaired.h" +#include "tmTree.h" +#include "tmPath.h" /********** class tmNode @@ -26,7 +34,7 @@ void tmNode::InitNode() // Initialize member data std::format_to_n(mLabel, MAX_LABEL_LEN, "{}", ""); - mLoc = tmPoint(0. ,0.); + mLoc = tmPoint(0., 0.); mDepth = DEPTH_NOT_SET; mElevation = 0.0; @@ -40,10 +48,8 @@ void tmNode::InitNode() mIsConditionedNode = false; // Clear owner - mNodeOwner = 0; + mNodeOwner = nullptr; } - - /***** Bare constructor for tmNode, used in stream I/O. *****/ @@ -285,7 +291,7 @@ size_t tmNode::GetNumPolygonPaths() const /***** Put a tmNode to a file in version 5 format. *****/ -void tmNode::Putv5Self(ostream& os) +void tmNode::Putv5Self(std::ostream& os) { PutPOD(os, GetTagStr()); PutPOD(os, mIndex); @@ -310,7 +316,7 @@ void tmNode::Putv5Self(ostream& os) /***** Get a tmNode from a file in version 5 format. *****/ -void tmNode::Getv5Self(istream& is) +void tmNode::Getv5Self(std::istream& is) { CheckTagStr(is); GetPOD(is, mIndex); @@ -336,7 +342,7 @@ void tmNode::Getv5Self(istream& is) Put a tmNode to a file in version 4 format. Note that we do not put any polys, vertices, or creases. *****/ -void tmNode::Putv4Self(ostream& os) +void tmNode::Putv4Self(std::ostream& os) { PutPOD(os, GetTagStr()); PutPOD(os, mIndex); @@ -358,7 +364,7 @@ void tmNode::Putv4Self(ostream& os) /***** Get a tmNode from a file in version 4 format. *****/ -void tmNode::Getv4Self(istream& is) +void tmNode::Getv4Self(std::istream& is) { CheckTagStr(is); GetPOD(is, mIndex); @@ -381,8 +387,8 @@ void tmNode::Getv4Self(istream& is) Get a node from a file in version 3 format. Note that when we read a version 3 document, we create conditions, which were previously implemented as member variables. -*****/ -void tmNode::Getv3Self(istream& is) +*****/ +void tmNode::Getv3Self(std::istream& is) { CheckTagStr(is); @@ -482,8 +488,10 @@ void tmNode::Getv3Self(istream& is) mNodeOwner = mTree; } +static inline const std::string nodeTagStr = "node"; + +const std::string& tmNode::TagStr() { + return nodeTagStr; +} + -/***** -Dynamic type implementation -*****/ -TM_IMPLEMENT_TAG(tmNode, "node") diff --git a/Source/tmModel/tmTreeClasses/tmPart.cpp b/Source/tmModel/tmTreeClasses/tmPart.cpp index 8dfe69c..03d9b3b 100644 --- a/Source/tmModel/tmTreeClasses/tmPart.cpp +++ b/Source/tmModel/tmTreeClasses/tmPart.cpp @@ -505,18 +505,11 @@ void tmPart::GetPOD(istream& is, char* c) /***** STATIC -Return a reference to the class tag static variable. Note that this number -gets changed by the dynamic type system when we call InitTypes(), which is why -this returns a reference. Each class overloads this static function, then -overrides the related virtual function GetTag() to return the class tag. +The class tag is now implemented as an inline variable in each class. +Each class has a static inline tag variable that contains the discrete +unsigned integer tag used to identify objects that are instances of that class. +The values are not necessarily the same from build to build. *****/ -size_t& tmPart::Tag() -{ - TMFAIL("tmPart::Tag()"); // should never call this - static size_t sTag = RAW_TAG; - return sTag; -} - /***** STATIC @@ -524,11 +517,13 @@ Return the class tag string. Each class overloads this static function, then overrides the related virtual function GetTagStr() to return the class tag string. *****/ + +static inline const std::string nullTagStr = "NULL"; + const string& tmPart::TagStr() { TMFAIL("tmPart::TagStr()"); // should never call this - static const string sTagStr("NULL"); - return sTagStr; + return nullTagStr; } @@ -552,7 +547,7 @@ we don't recognize it, TagToStr() will throw a EX_IO_UNRECOGNIZED_TAG. This is a good place to remind that tmPart::GetTag() returns the tag for the given part polymorphically. *****/ -void tmPart::GetPODTag(istream& is, size_t& tag) +void tmPart::GetPODTag(istream& is, size_t& tagValue) { TMASSERT(TypesAreInitialized()); string tagstr; @@ -560,7 +555,7 @@ void tmPart::GetPODTag(istream& is, size_t& tag) if (tagstr.length() != 4) { throw EX_IO_BAD_TAG(tagstr); } - tag = StrToTag(tagstr); + tagValue = StrToTag(tagstr); #if ECHO_INPUT TMLOG(wxString::Format(" GetTag() %s", tagstr.c_str())); #endif // ECHO_INPUT @@ -572,13 +567,13 @@ STATIC Return the string associated with this tag, which will be the value of P::Tag() for some class P. Throw an exception if none is found. *****/ -const string& tmPart::TagToStr(size_t tag) +const string& tmPart::TagToStr(size_t tagValue) { TMASSERT(TypesAreInitialized()); - TMASSERT(tag != RAW_TAG); - if (tag >= GetTagStrs().size()) { + TMASSERT(tagValue != RAW_TAG); + if (tagValue >= GetTagStrs().size()) { stringstream ss; - ss << "[tag ID] " << int(tag); + ss << "[tag ID] " << int(tagValue); throw EX_IO_UNRECOGNIZED_TAG(ss.str()); } return GetTagStrs()[tag]; diff --git a/Source/tmModel/tmTreeClasses/tmPart.h b/Source/tmModel/tmTreeClasses/tmPart.h index 9dedb40..4a91e58 100644 --- a/Source/tmModel/tmTreeClasses/tmPart.h +++ b/Source/tmModel/tmTreeClasses/tmPart.h @@ -36,8 +36,9 @@ public:\ virtual const std::size_t& GetTag() const {return Tag();};\ virtual const std::string& GetTagStr() const {return TagStr();};\ protected:\ - static std::size_t& Tag();\ - static const std::string& TagStr(); + static inline std::size_t tag = std::size_t(-1);\ + static const std::string& TagStr();\ + static const std::size_t& Tag() { return tag; } /***** @@ -45,14 +46,12 @@ Implementation. Note that some classes (tmPart, tmCondition) handle this themselves. *****/ #define TM_IMPLEMENT_TAG(PART_CLASS, PART_STRING) \ -size_t& PART_CLASS::Tag() {\ - static size_t sTag = std::size_t(-1);\ - return sTag;}\ const string& PART_CLASS::TagStr() {\ static const string sTagStr(PART_STRING);\ return sTagStr;} + /********** class tmPart Base class for all TreeMaker objects. They all point back to the tmTree that @@ -186,7 +185,7 @@ class tmPart { static void CheckTagStr(std::istream& is); // Dynamic type system implementation - static const std::string& TagToStr(std::size_t tag); + static const std::string& TagToStr(std::size_t tagValue); static std::size_t StrToTag(const std::string& tagstr); static tmArray& GetTagStrs(); @@ -273,7 +272,7 @@ construction. template tmPart::StringT

::StringT() { - P::Tag() = GetNumTypes(); + P::tag = GetNumTypes(); } diff --git a/Source/tmModel/tmTreeClasses/tmTree.h b/Source/tmModel/tmTreeClasses/tmTree.h index 2f212e6..0460b0a 100644 --- a/Source/tmModel/tmTreeClasses/tmTree.h +++ b/Source/tmModel/tmTreeClasses/tmTree.h @@ -13,13 +13,13 @@ Copyright: 2003 Robert J. Lang. All Rights Reserved. // Standard TreeMaker header #include "tmHeader.h" +#include "tmEdge.h" // Add this include // Standard libraries #include // TreeMaker classes #include "tmModel_fwd.h" -#include "tmTreeCleaner.h" #include "tmPart.h" #include "tmCluster.h" #include "tmEdgeOwner.h" @@ -30,6 +30,7 @@ Copyright: 2003 Robert J. Lang. All Rights Reserved. #include "tmArray.h" #include "tmArrayIterator.h" #include "tmCondition.h" +#include "tmTree_fwd.h" /**********