diff --git a/.github/actions/spelling/patterns/0_t.txt b/.github/actions/spelling/patterns/0_t.txt index 0e44139a32..519c4ec067 100644 --- a/.github/actions/spelling/patterns/0_t.txt +++ b/.github/actions/spelling/patterns/0_t.txt @@ -10,4 +10,4 @@ \\tests(?![a-z]) \\thread(?![a-z]) \\tools(?![a-z]) -\\types(?![a-z]) +\\types?(?![a-z]) diff --git a/src/inc/til/generational.h b/src/inc/til/generational.h new file mode 100644 index 0000000000..261fbc392b --- /dev/null +++ b/src/inc/til/generational.h @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +namespace til // Terminal Implementation Library. Also: "Today I Learned" +{ + struct generation_t + { + auto operator<=>(const generation_t&) const = default; + + constexpr void bump() noexcept + { + _value++; + } + + uint32_t _value = 0; + }; + + // It can be costly, difficult, or often impossible to compare two + // instances of a struct. This little helper can simplify this. + // + // The underlying idea is that changes in state occur much less often than + // the amount of data that's being processed in between. As such, this + // helper assumes that _any_ modification to the struct it wraps is a + // state change. When you compare the modified instance with another + // the comparison operator will then always return false. This makes + // state changes potentially more costly, because more state might be + // invalidated than was necessary, but on the other hand it makes both, + // the code simpler and the fast-path (no state change) much faster. + template + struct generational + { + generational() = default; + explicit constexpr generational(auto&&... args) : + _value{ std::forward(args)... } {} + explicit constexpr generational(generation_t generation, auto&&... args) : + _generation{ generation }, + _value{ std::forward(args)... } {} + + constexpr bool operator==(const generational& rhs) const noexcept { return generation() == rhs.generation(); } + constexpr bool operator!=(const generational& rhs) const noexcept { return generation() != rhs.generation(); } + + constexpr generation_t generation() const noexcept + { + return _generation; + } + + [[nodiscard]] constexpr const T* operator->() const noexcept + { + return &_value; + } + + [[nodiscard]] constexpr const T& operator*() const noexcept + { + return _value; + } + + [[nodiscard]] constexpr T* write() noexcept + { + _generation.bump(); + return &_value; + } + + private: + generation_t _generation; + T _value{}; + }; +} diff --git a/src/til/ut_til/GenerationalTests.cpp b/src/til/ut_til/GenerationalTests.cpp new file mode 100644 index 0000000000..ce8e4dc1cd --- /dev/null +++ b/src/til/ut_til/GenerationalTests.cpp @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "precomp.h" + +#include + +using namespace WEX::Common; +using namespace WEX::Logging; +using namespace WEX::TestExecution; + +struct Data +{ + int value = 0; +}; + +class GenerationalTests +{ + TEST_CLASS(GenerationalTests); + + TEST_METHOD(Basic) + { + til::generational src; + til::generational dst; + + // Reads via -> and *, just like std::optional, etc. + VERIFY_ARE_EQUAL(0, src->value); + VERIFY_ARE_EQUAL(0, (*src).value); + + // Writes via .write()-> + src.write()->value = 123; + // ...which makes them not compare as equal. + VERIFY_ARE_NOT_EQUAL(dst, src); + + // Synchronize the two objects by copying them + dst = src; + // ...which results in both being considered equal again + VERIFY_ARE_EQUAL(dst, src); + // ...and all values are copied over. + VERIFY_ARE_EQUAL(123, dst->value); + } +}; diff --git a/src/til/ut_til/til.unit.tests.vcxproj b/src/til/ut_til/til.unit.tests.vcxproj index 5223074ec1..824bf40bd4 100644 --- a/src/til/ut_til/til.unit.tests.vcxproj +++ b/src/til/ut_til/til.unit.tests.vcxproj @@ -20,6 +20,7 @@ + @@ -43,10 +44,12 @@ + + @@ -66,6 +69,7 @@ + @@ -85,4 +89,4 @@ - + \ No newline at end of file diff --git a/src/til/ut_til/til.unit.tests.vcxproj.filters b/src/til/ut_til/til.unit.tests.vcxproj.filters index 88fccdbd38..545bc412f5 100644 --- a/src/til/ut_til/til.unit.tests.vcxproj.filters +++ b/src/til/ut_til/til.unit.tests.vcxproj.filters @@ -28,6 +28,7 @@ + @@ -118,10 +119,19 @@ inc + + inc + + + inc + + + inc + {7cf29ba4-d33d-4c3b-82e3-ab73e5a79685} - + \ No newline at end of file diff --git a/tools/ConsoleTypes.natvis b/tools/ConsoleTypes.natvis index bf772d2191..803bf5d333 100644 --- a/tools/ConsoleTypes.natvis +++ b/tools/ConsoleTypes.natvis @@ -105,4 +105,11 @@ _ptr + + + {{ gen={_generation._value}, {_value} }} + + _value + +