Browse Source

cmStack: New, mutable stack class

We would like to record additional information in the find-package
stack, but we don't have the information at the point a stack entry is
created. The most sane way to handle this appears to be making the stack
mutable, at least under specific circumstances. To facilitate this, we
need a mutable stack type.

Refactor cmConstStack into cmStack, with the ability to either allow or
forbid mutation of its data. Re-add cmConstStack as a type alias.

This doesn't yet allow mutating cmFindPackageStack, but it's a necessary
step toward being able to do so.
Matthew Woehlke 2 months ago
parent
commit
f2bdc2176f

+ 2 - 2
Source/CMakeLists.txt

@@ -148,8 +148,6 @@ add_library(
   cmComputeTargetDepends.cxx
   cmConfigureLog.h
   cmConfigureLog.cxx
-  cmConstStack.h
-  cmConstStack.tcc
   cmCPackPropertiesGenerator.h
   cmCPackPropertiesGenerator.cxx
   cmCryptoHash.cxx
@@ -458,6 +456,8 @@ add_library(
   cmSourceFileLocationKind.h
   cmSourceGroup.cxx
   cmSourceGroup.h
+  cmStack.h
+  cmStack.tcc
   cmStandardLevel.h
   cmStandardLevelResolver.cxx
   cmStandardLevelResolver.h

+ 0 - 62
Source/cmConstStack.tcc

@@ -1,62 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file LICENSE.rst or https://cmake.org/licensing for details.  */
-
-#include <cassert>
-#include <memory>
-#include <utility>
-
-template <typename T, typename Stack>
-struct cmConstStack<T, Stack>::Entry
-{
-  Entry(std::shared_ptr<Entry const> parent, T value)
-    : Value(std::move(value))
-    , Parent(std::move(parent))
-  {
-  }
-
-  T Value;
-  std::shared_ptr<Entry const> Parent;
-};
-
-template <typename T, typename Stack>
-cmConstStack<T, Stack>::cmConstStack() = default;
-
-template <typename T, typename Stack>
-Stack cmConstStack<T, Stack>::Push(T value) const
-{
-  return Stack(this->TopEntry, std::move(value));
-}
-
-template <typename T, typename Stack>
-Stack cmConstStack<T, Stack>::Pop() const
-{
-  assert(this->TopEntry);
-  return Stack(this->TopEntry->Parent);
-}
-
-template <typename T, typename Stack>
-T const& cmConstStack<T, Stack>::Top() const
-{
-  assert(this->TopEntry);
-  return this->TopEntry->Value;
-}
-
-template <typename T, typename Stack>
-bool cmConstStack<T, Stack>::Empty() const
-{
-  return !this->TopEntry;
-}
-
-template <typename T, typename Stack>
-cmConstStack<T, Stack>::cmConstStack(std::shared_ptr<Entry const> parent,
-                                     T value)
-  : TopEntry(
-      std::make_shared<Entry const>(std::move(parent), std::move(value)))
-{
-}
-
-template <typename T, typename Stack>
-cmConstStack<T, Stack>::cmConstStack(std::shared_ptr<Entry const> top)
-  : TopEntry(std::move(top))
-{
-}

+ 3 - 2
Source/cmFindPackageStack.cxx

@@ -3,5 +3,6 @@
 #define cmFindPackageStack_cxx
 #include "cmFindPackageStack.h"
 
-#include "cmConstStack.tcc" // IWYU pragma: keep
-template class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+#include "cmStack.tcc" // IWYU pragma: keep
+template class cmStack<cmFindPackageCall const, cmFindPackageStack,
+                       cmStackType::Const>;

+ 5 - 4
Source/cmFindPackageStack.h

@@ -7,7 +7,7 @@
 #include <memory>
 #include <string>
 
-#include "cmConstStack.h"
+#include "cmStack.h"
 
 /**
  * Represents one call to find_package.
@@ -25,9 +25,10 @@ public:
 class cmFindPackageStack
   : public cmConstStack<cmFindPackageCall, cmFindPackageStack>
 {
-  using cmConstStack::cmConstStack;
-  friend class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+  using cmStack::cmStack;
+  friend cmFindPackageStack::Base;
 };
 #ifndef cmFindPackageStack_cxx
-extern template class cmConstStack<cmFindPackageCall, cmFindPackageStack>;
+extern template class cmStack<cmFindPackageCall const, cmFindPackageStack,
+                              cmStackType::Const>;
 #endif

+ 3 - 2
Source/cmListFileCache.cxx

@@ -456,8 +456,9 @@ bool cmListFile::ParseString(char const* str, char const* virtual_filename,
   return !parseError;
 }
 
-#include "cmConstStack.tcc"
-template class cmConstStack<cmListFileContext, cmListFileBacktrace>;
+#include "cmStack.tcc"
+template class cmStack<cmListFileContext const, cmListFileBacktrace,
+                       cmStackType::Const>;
 
 std::ostream& operator<<(std::ostream& os, cmListFileContext const& lfc)
 {

+ 5 - 4
Source/cmListFileCache.h

@@ -12,8 +12,8 @@
 
 #include <cm/optional>
 
-#include "cmConstStack.h"
 #include "cmList.h"
+#include "cmStack.h"
 #include "cmSystemTools.h"
 
 /** \class cmListFileCache
@@ -169,11 +169,12 @@ bool operator!=(cmListFileContext const& lhs, cmListFileContext const& rhs);
 class cmListFileBacktrace
   : public cmConstStack<cmListFileContext, cmListFileBacktrace>
 {
-  using cmConstStack::cmConstStack;
-  friend class cmConstStack<cmListFileContext, cmListFileBacktrace>;
+  using cmStack::cmStack;
+  friend cmListFileBacktrace::Base;
 };
 #ifndef cmListFileCache_cxx
-extern template class cmConstStack<cmListFileContext, cmListFileBacktrace>;
+extern template class cmStack<cmListFileContext const, cmListFileBacktrace,
+                              cmStackType::Const>;
 #endif
 
 // Wrap type T as a value with a backtrace.  For purposes of

+ 1 - 0
Source/cmMakefile.cxx

@@ -50,6 +50,7 @@
 #include "cmRange.h"
 #include "cmSourceFile.h"
 #include "cmSourceFileLocation.h"
+#include "cmStack.h"
 #include "cmState.h"
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"

+ 31 - 9
Source/cmConstStack.h → Source/cmStack.h

@@ -5,19 +5,31 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <memory>
+#include <type_traits>
 
-/** Base class template for CRTP to represent a stack of constant values.
-    Provide value semantics, but use efficient reference-counting underneath
-    to avoid copies.  */
-template <typename T, typename Stack>
-class cmConstStack
+enum class cmStackType
+{
+  Const,
+  Mutable,
+};
+
+template <typename T, cmStackType Mutable>
+struct cmStackEntry;
+
+/** Base class template for CRTP to represent a stack of values.
+    Copies of the stack <i>share data</i>; mutating data on one copy will
+    change the data on <i>all</i> copies.  */
+template <typename T, typename Stack,
+          cmStackType Mutable = cmStackType::Mutable>
+class cmStack
 {
-  struct Entry;
+  using Entry = cmStackEntry<T, Mutable>;
+
   std::shared_ptr<Entry const> TopEntry;
 
 public:
   /** Default-construct an empty stack.  */
-  cmConstStack();
+  cmStack();
 
   /** Get a stack with the given call context added to the top.  */
   Stack Push(T value) const;
@@ -29,11 +41,21 @@ public:
   /** Get the value at the top of the stack.
       This may be called only if Empty() would return false.  */
   T const& Top() const;
+  template <bool E = (Mutable == cmStackType::Mutable)>
+  typename std::enable_if<E, T>::type& Top();
 
   /** Return true if this stack is empty.  */
   bool Empty() const;
 
 protected:
-  cmConstStack(std::shared_ptr<Entry const> parent, T value);
-  cmConstStack(std::shared_ptr<Entry const> top);
+  using Base = cmStack<T, Stack, Mutable>;
+
+  cmStack(std::shared_ptr<Entry const> parent, T value);
+  cmStack(std::shared_ptr<Entry const> top);
 };
+
+/** Specialization of cmStack for CRTP to represent a stack of constant values.
+    Provide value semantics, but use efficient reference-counting underneath
+    to avoid copies.  */
+template <typename T, typename Stack>
+using cmConstStack = cmStack<T const, Stack, cmStackType::Const>;

+ 85 - 0
Source/cmStack.tcc

@@ -0,0 +1,85 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file LICENSE.rst or https://cmake.org/licensing for details.  */
+
+#include <cassert>
+#include <memory>
+#include <utility>
+
+template <typename T>
+struct cmStackEntry<T, cmStackType::Mutable>
+{
+  cmStackEntry(std::shared_ptr<cmStackEntry const> parent, T value)
+    : Value(std::move(value))
+    , Parent(std::move(parent))
+  {
+  }
+
+  T mutable Value;
+  std::shared_ptr<cmStackEntry const> Parent;
+};
+
+template <typename T>
+struct cmStackEntry<T, cmStackType::Const>
+{
+  cmStackEntry(std::shared_ptr<cmStackEntry const> parent, T value)
+    : Value(std::move(value))
+    , Parent(std::move(parent))
+  {
+  }
+
+  T Value;
+  std::shared_ptr<cmStackEntry const> Parent;
+};
+
+template <typename T, typename Stack, cmStackType Mutable>
+cmStack<T, Stack, Mutable>::cmStack() = default;
+
+template <typename T, typename Stack, cmStackType Mutable>
+Stack cmStack<T, Stack, Mutable>::Push(T value) const
+{
+  return Stack(this->TopEntry, std::move(value));
+}
+
+template <typename T, typename Stack, cmStackType Mutable>
+Stack cmStack<T, Stack, Mutable>::Pop() const
+{
+  assert(this->TopEntry);
+  return Stack(this->TopEntry->Parent);
+}
+
+template <typename T, typename Stack, cmStackType Mutable>
+T const& cmStack<T, Stack, Mutable>::Top() const
+{
+  assert(this->TopEntry);
+  return this->TopEntry->Value;
+}
+
+template <typename T, typename Stack, cmStackType Mutable>
+template <bool E>
+typename std::enable_if<E, T>::type& cmStack<T, Stack, Mutable>::Top()
+{
+  static_assert(Mutable == cmStackType::Mutable,
+                "T& cmStack::Top should only exist for mutable cmStack");
+  assert(this->TopEntry);
+  return this->TopEntry->Value;
+}
+
+template <typename T, typename Stack, cmStackType Mutable>
+bool cmStack<T, Stack, Mutable>::Empty() const
+{
+  return !this->TopEntry;
+}
+
+template <typename T, typename Stack, cmStackType Mutable>
+cmStack<T, Stack, Mutable>::cmStack(std::shared_ptr<Entry const> parent,
+                                    T value)
+  : TopEntry(
+      std::make_shared<Entry const>(std::move(parent), std::move(value)))
+{
+}
+
+template <typename T, typename Stack, cmStackType Mutable>
+cmStack<T, Stack, Mutable>::cmStack(std::shared_ptr<Entry const> top)
+  : TopEntry(std::move(top))
+{
+}

+ 6 - 2
Utilities/IWYU/mapping.imp

@@ -92,8 +92,12 @@
   { include: [ "\"form.h\"", private, "\"cmCursesStandardIncludes.h\"", public ] },
   { include: [ "<form.h>", private, "\"cmCursesStandardIncludes.h\"", public ] },
 
-  # Help IWYU understand our explicit instantiation for cmConstStack.
-  { symbol: [ "cmConstStack::cmConstStack<T, Stack>", private, "\"cmConstStack.h\"", public ] },
+  # Help IWYU understand our explicit instantiation for cmStack.
+  { symbol: [ "cmStack::cmStack<T, Stack, Mutable>", private, "\"cmStack.h\"", public ] },
+  { symbol: [ "cmStack<cmFindPackageCall, cmFindPackageStack, cmStackType::Mutable>::Empty", private, "\"cmStack.h\"", public ] },
+  { symbol: [ "cmStack<cmFindPackageCall, cmFindPackageStack, cmStackType::Mutable>::Top", private, "\"cmStack.h\"", public ] },
+  { symbol: [ "cmStack<cmFindPackageCall, cmFindPackageStack, cmStackType::Mutable>::Pop", private, "\"cmStack.h\"", public ] },
+  { symbol: [ "cmStack<cmFindPackageCall, cmFindPackageStack, cmStackType::Mutable>::Push", private, "\"cmStack.h\"", public ] },
 ]
 
 # vim: set ft=toml: