debug/lifetime: Add lifetime stderr printer

This adds a class that aids in debugging by printing object lifetime
information to stderr.

Change-Id: I92b904a5e6d8d98f7412769f3670c5e2091a7bc9
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/include/meson.build b/include/meson.build
index 57a5a5e..c541513 100644
--- a/include/meson.build
+++ b/include/meson.build
@@ -3,6 +3,7 @@
 install_headers(
   'stdplus/cancel.hpp',
   'stdplus/concepts.hpp',
+  'stdplus/debug/lifetime.hpp',
   'stdplus/exception.hpp',
   'stdplus/flags.hpp',
   'stdplus/function_view.hpp',
diff --git a/include/stdplus/debug/lifetime.hpp b/include/stdplus/debug/lifetime.hpp
new file mode 100644
index 0000000..e63e640
--- /dev/null
+++ b/include/stdplus/debug/lifetime.hpp
@@ -0,0 +1,22 @@
+#pragma once
+#include <source_location>
+
+namespace stdplus::debug
+{
+
+class Lifetime
+{
+  public:
+    Lifetime(std::source_location loc = std::source_location::current());
+    Lifetime(const Lifetime& other);
+    Lifetime(Lifetime&& other);
+    Lifetime& operator=(const Lifetime& other);
+    Lifetime& operator=(Lifetime&& other);
+    ~Lifetime();
+
+  private:
+    std::source_location loc;
+    std::size_t id;
+};
+
+} // namespace stdplus::debug
diff --git a/src/debug/lifetime.cpp b/src/debug/lifetime.cpp
new file mode 100644
index 0000000..16f135d
--- /dev/null
+++ b/src/debug/lifetime.cpp
@@ -0,0 +1,66 @@
+#include <stdplus/debug/lifetime.hpp>
+#include <stdplus/print.hpp>
+
+using std::literals::string_view_literals::operator""sv;
+
+template <typename CharT>
+struct std::formatter<std::source_location, CharT>
+{
+    template <typename ParseContext>
+    constexpr auto parse(ParseContext& ctx)
+    {
+        return ctx.begin();
+    }
+
+    template <typename FormatContext>
+    constexpr auto format(const auto& v, FormatContext& ctx) const
+    {
+        return std::format_to(ctx.out(), "{}:{}({})", v.file_name(), v.line(),
+                              v.function_name());
+    }
+};
+
+namespace stdplus::debug
+{
+
+static std::size_t next_id = 0;
+
+Lifetime::Lifetime(std::source_location loc) : loc(loc), id(next_id++)
+{
+    stdplus::print(stderr, "Lifetime Construct {} {}\n", loc, id);
+}
+
+Lifetime::Lifetime(const Lifetime& other) : loc(other.loc), id(next_id++)
+{
+    stdplus::print(stderr, "Lifetime Copy {} {}->{}\n", loc, other.id, id);
+}
+
+Lifetime::Lifetime(Lifetime&& other) : loc(other.loc), id(next_id++)
+{
+    stdplus::print(stderr, "Lifetime Move {} {}->{}\n", loc, other.id, id);
+}
+
+Lifetime& Lifetime::operator=(const Lifetime& other)
+{
+    auto old_id = id;
+    id = next_id++;
+    stdplus::print(stderr, "Lifetime Copy {} {}->{} drop {}\n", loc, other.id,
+                   id, old_id);
+    return *this;
+}
+
+Lifetime& Lifetime::operator=(Lifetime&& other)
+{
+    auto old_id = id;
+    id = next_id++;
+    stdplus::print(stderr, "Lifetime Move {} {}->{} drop {}\n", loc, other.id,
+                   id, old_id);
+    return *this;
+}
+
+Lifetime::~Lifetime()
+{
+    stdplus::print(stderr, "Lifetime Destroy {} {}\n", loc, id);
+}
+
+} // namespace stdplus::debug
diff --git a/src/meson.build b/src/meson.build
index 11549a3..55051c8 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -43,6 +43,7 @@
 
 stdplus_srcs = [
   'cancel.cpp',
+  'debug/lifetime.cpp',
   'exception.cpp',
   'flags.cpp',
   'function_view.cpp',