From b2034add10610baf2d0905c8d445ac9e42362054 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Tue, 21 Apr 2026 09:21:40 -0700 Subject: [PATCH 1/2] Fix set_value template deduction for smart string types The set_value_type<> specializations for std::wstring, BSTR, unique_bstr, shared_bstr, unique_cotaskmem_string, and shared_cotaskmem_string incorrectly used const-qualified template parameters (e.g. set_value_type). Since reg_view_t::set_value deduces R from 'const R& value', the template parameter R is always the non-const type. This caused a static_assert failure when calling set_value with any of these types. Remove the const qualifier from all six affected specializations so the deduced type matches the specialization. Fixes #624 --- include/wil/registry_helpers.h | 12 +++++------ tests/RegistryTests.cpp | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/include/wil/registry_helpers.h b/include/wil/registry_helpers.h index f796864f..f651afe2 100644 --- a/include/wil/registry_helpers.h +++ b/include/wil/registry_helpers.h @@ -1016,7 +1016,7 @@ namespace reg } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + constexpr DWORD set_value_type<::std::wstring>() WI_NOEXCEPT { return REG_SZ; } @@ -1035,13 +1035,13 @@ namespace reg } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + constexpr DWORD set_value_type() WI_NOEXCEPT { return REG_SZ; } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + constexpr DWORD set_value_type<::wil::unique_bstr>() WI_NOEXCEPT { return REG_SZ; } @@ -1056,7 +1056,7 @@ namespace reg } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + constexpr DWORD set_value_type<::wil::shared_bstr>() WI_NOEXCEPT { return REG_SZ; } @@ -1070,7 +1070,7 @@ namespace reg } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + constexpr DWORD set_value_type<::wil::unique_cotaskmem_string>() WI_NOEXCEPT { return REG_SZ; } @@ -1084,7 +1084,7 @@ namespace reg } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + constexpr DWORD set_value_type<::wil::shared_cotaskmem_string>() WI_NOEXCEPT { return REG_SZ; } diff --git a/tests/RegistryTests.cpp b/tests/RegistryTests.cpp index f774e95c..abd0d727 100644 --- a/tests/RegistryTests.cpp +++ b/tests/RegistryTests.cpp @@ -2272,6 +2272,43 @@ TEST_CASE("BasicRegistryTests::string types", "[registry]") #endif } + SECTION("strings set_value with std::wstring lvalue: with opened key") + { + wil::unique_hkey hkey; + REQUIRE_SUCCEEDED(wil::reg::create_unique_key_nothrow(HKEY_CURRENT_USER, testSubkey, hkey, wil::reg::key_access::readwrite)); + + // verify non-const std::wstring lvalue works with set_value (issue #624) + std::wstring stringValue{L"wstring lvalue test"}; + wil::reg::set_value(hkey.get(), stringValueName, stringValue); + auto result = wil::reg::get_value(hkey.get(), stringValueName); + REQUIRE(result == stringValue); + + // verify const std::wstring lvalue also works + const std::wstring constStringValue{L"const wstring test"}; + wil::reg::set_value(hkey.get(), stringValueName, constStringValue); + result = wil::reg::get_value(hkey.get(), stringValueName); + REQUIRE(result == constStringValue); + + // verify empty std::wstring + std::wstring emptyValue; + wil::reg::set_value(hkey.get(), stringValueName, emptyValue); + result = wil::reg::get_value(hkey.get(), stringValueName); + REQUIRE(result.empty()); + } + + SECTION("strings set_value with std::wstring lvalue: with string key") + { + std::wstring stringValue{L"wstring lvalue subkey test"}; + wil::reg::set_value(HKEY_CURRENT_USER, testSubkey, stringValueName, stringValue); + auto result = wil::reg::get_value(HKEY_CURRENT_USER, testSubkey, stringValueName); + REQUIRE(result == stringValue); + + const std::wstring constStringValue{L"const wstring subkey test"}; + wil::reg::set_value(HKEY_CURRENT_USER, testSubkey, stringValueName, constStringValue); + result = wil::reg::get_value(HKEY_CURRENT_USER, testSubkey, stringValueName); + REQUIRE(result == constStringValue); + } + #if defined(__cpp_lib_optional) SECTION("strings set_value_string/try_get_value_string: with open key") { From e49d00951273f0b7c4609e3628b6f6c7e26cd114 Mon Sep 17 00:00:00 2001 From: Ben Hillis Date: Wed, 10 Jun 2026 10:06:32 -0700 Subject: [PATCH 2/2] Refactor set_value_type into set_value_type_t trait struct Per review feedback, convert the set_value_type function family into a set_value_type_t trait struct with a constexpr DWORD value member, and make set_value_type() a thin wrapper that applies wistd::remove_cv_t. This removes the need for callers to strip cv-qualifiers from the deduced type while keeping the type fully specializable for future use. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- include/wil/registry_helpers.h | 89 ++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/include/wil/registry_helpers.h b/include/wil/registry_helpers.h index f651afe2..8e4a8e02 100644 --- a/include/wil/registry_helpers.h +++ b/include/wil/registry_helpers.h @@ -925,10 +925,17 @@ namespace reg static_assert(!wistd::is_same_v, "Unsupported type for get_value_type"); } - template - DWORD set_value_type() WI_NOEXCEPT + template + struct set_value_type_t { static_assert(!wistd::is_same_v, "Unsupported type for set_value_type"); + }; + + // Applies remove_cv_t so callers don't have to strip cv-qualifiers from the deduced type + template + constexpr DWORD set_value_type() WI_NOEXCEPT + { + return set_value_type_t>::value; } template <> @@ -937,10 +944,10 @@ namespace reg return get_value_flags_from_value_type(REG_DWORD); } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + struct set_value_type_t { - return REG_DWORD; - } + static constexpr DWORD value = REG_DWORD; + }; template <> constexpr DWORD get_value_type() WI_NOEXCEPT @@ -948,10 +955,10 @@ namespace reg return get_value_flags_from_value_type(REG_DWORD); } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + struct set_value_type_t { - return REG_DWORD; - } + static constexpr DWORD value = REG_DWORD; + }; template <> constexpr DWORD get_value_type() WI_NOEXCEPT @@ -959,10 +966,10 @@ namespace reg return get_value_flags_from_value_type(REG_DWORD); } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + struct set_value_type_t { - return REG_DWORD; - } + static constexpr DWORD value = REG_DWORD; + }; template <> constexpr DWORD get_value_type() WI_NOEXCEPT @@ -970,10 +977,10 @@ namespace reg return get_value_flags_from_value_type(REG_DWORD); } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + struct set_value_type_t { - return REG_DWORD; - } + static constexpr DWORD value = REG_DWORD; + }; template <> constexpr DWORD get_value_type() WI_NOEXCEPT @@ -981,10 +988,10 @@ namespace reg return get_value_flags_from_value_type(REG_QWORD); } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + struct set_value_type_t { - return REG_QWORD; - } + static constexpr DWORD value = REG_QWORD; + }; template <> constexpr DWORD get_value_type() WI_NOEXCEPT @@ -992,10 +999,10 @@ namespace reg return get_value_flags_from_value_type(REG_QWORD); } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + struct set_value_type_t { - return REG_QWORD; - } + static constexpr DWORD value = REG_QWORD; + }; template <> constexpr DWORD get_value_type() WI_NOEXCEPT @@ -1003,10 +1010,10 @@ namespace reg return get_value_flags_from_value_type(REG_SZ); } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + struct set_value_type_t { - return REG_SZ; - } + static constexpr DWORD value = REG_SZ; + }; #if (WIL_USE_STL && defined(WIL_ENABLE_EXCEPTIONS)) || defined(WIL_DOXYGEN) template <> @@ -1016,10 +1023,10 @@ namespace reg } template <> - constexpr DWORD set_value_type<::std::wstring>() WI_NOEXCEPT + struct set_value_type_t<::std::wstring> { - return REG_SZ; - } + static constexpr DWORD value = REG_SZ; + }; #endif #if defined(__WIL_OLEAUTO_H_) @@ -1035,16 +1042,16 @@ namespace reg } template <> - constexpr DWORD set_value_type() WI_NOEXCEPT + struct set_value_type_t { - return REG_SZ; - } + static constexpr DWORD value = REG_SZ; + }; template <> - constexpr DWORD set_value_type<::wil::unique_bstr>() WI_NOEXCEPT + struct set_value_type_t<::wil::unique_bstr> { - return REG_SZ; - } + static constexpr DWORD value = REG_SZ; + }; #endif // #if defined(__WIL_OLEAUTO_H_) #if defined(__WIL_OLEAUTO_H_STL) @@ -1056,10 +1063,10 @@ namespace reg } template <> - constexpr DWORD set_value_type<::wil::shared_bstr>() WI_NOEXCEPT + struct set_value_type_t<::wil::shared_bstr> { - return REG_SZ; - } + static constexpr DWORD value = REG_SZ; + }; #endif // #if defined(__WIL_OLEAUTO_H_STL) #if defined(__WIL_OBJBASE_H_) @@ -1070,10 +1077,10 @@ namespace reg } template <> - constexpr DWORD set_value_type<::wil::unique_cotaskmem_string>() WI_NOEXCEPT + struct set_value_type_t<::wil::unique_cotaskmem_string> { - return REG_SZ; - } + static constexpr DWORD value = REG_SZ; + }; #endif // defined(__WIL_OBJBASE_H_) #if defined(__WIL_OBJBASE_H_STL) @@ -1084,10 +1091,10 @@ namespace reg } template <> - constexpr DWORD set_value_type<::wil::shared_cotaskmem_string>() WI_NOEXCEPT + struct set_value_type_t<::wil::shared_cotaskmem_string> { - return REG_SZ; - } + static constexpr DWORD value = REG_SZ; + }; #endif // #if defined(__WIL_OBJBASE_H_STL) } // namespace reg_value_type_info