From 21584b304b21bfe6b99b6f29018c6b754ea28fc0 Mon Sep 17 00:00:00 2001 From: Noel Grandin Date: Mon, 19 Apr 2021 11:41:47 +0200 Subject: [PATCH] make OUString(OUStringLiteral) constructor constexpr with the intent that we can declare static const OUStringLiteral FOO = "bar"; static const struct { OUString s; } xxx = { FOO }; and have the xxx laid out at compile time. Kudos to mikekaganski for coming up with the idea of using the rtl_uString struct inside OUStringLiteral. We are using a union to defeat the array bounds checking that the compiler does when it evaluates constexpr values, so this might be brittle. Change-Id: I9a6247a17afab2eb28df139d532a8d864c49cd62 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114196 Tested-by: Jenkins Reviewed-by: Noel Grandin --- include/rtl/ustring.hxx | 48 +++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/include/rtl/ustring.hxx b/include/rtl/ustring.hxx index bb3390c19985..e1ae2d38f2d9 100644 --- a/include/rtl/ustring.hxx +++ b/include/rtl/ustring.hxx @@ -83,6 +83,7 @@ This class is not part of public API and is meant to be used only in LibreOffice template class SAL_WARN_UNUSED OUStringLiteral { static_assert(N != 0); static_assert(N - 1 <= std::numeric_limits::max(), "literal too long"); + friend class OUString; public: #if HAVE_CPP_CONSTEVAL @@ -95,35 +96,44 @@ public: assert(literal[N - 1] == '\0'); //TODO: Use C++20 constexpr std::copy_n (P0202R3): for (std::size_t i = 0; i != N; ++i) { - buffer[i] = literal[i]; + more.buffer[i] = literal[i]; } } - constexpr sal_Int32 getLength() const { return length; } + constexpr sal_Int32 getLength() const { return more.length; } - constexpr sal_Unicode const * getStr() const SAL_RETURNS_NONNULL { return buffer; } + constexpr sal_Unicode const * getStr() const SAL_RETURNS_NONNULL { return more.buffer; } - constexpr operator std::u16string_view() const { return {buffer, sal_uInt32(length)}; } + constexpr operator std::u16string_view() const { return {more.buffer, sal_uInt32(more.length)}; } private: static constexpr void assertLayout() { // These static_asserts verifying the layout compatibility with rtl_uString cannot be class // member declarations, as offsetof requires a complete type, so defer them to here: - static_assert(offsetof(OUStringLiteral, refCount) == offsetof(rtl_uString, refCount)); - static_assert(std::is_same_v); - static_assert(offsetof(OUStringLiteral, length) == offsetof(rtl_uString, length)); - static_assert(std::is_same_v); - static_assert(offsetof(OUStringLiteral, buffer) == offsetof(rtl_uString, buffer)); - static_assert( - std::is_same_v< - std::remove_extent_t, - std::remove_extent_t>); + static_assert(offsetof(OUStringLiteral, str.refCount) == offsetof(OUStringLiteral, more.refCount)); + static_assert(offsetof(OUStringLiteral, str.length) == offsetof(OUStringLiteral, more.length)); + static_assert(offsetof(OUStringLiteral, str.buffer) == offsetof(OUStringLiteral, more.buffer)); } - // Same layout as rtl_uString (include/rtl/ustring.h): - oslInterlockedCount refCount = 0x40000000; // SAL_STRING_STATIC_FLAG (sal/rtl/strimp.hxx) - sal_Int32 length = N - 1; - sal_Unicode buffer[N] = {}; //TODO: drop initialization for C++20 (P1331R2) +#if defined(_WIN32) +#pragma pack(push, 4) +#endif + union { + rtl_uString str; + struct { + oslInterlockedCount refCount; + sal_Int32 length; + sal_Unicode buffer[N]; + } more = + { + 0x40000000, // SAL_STRING_STATIC_FLAG (sal/rtl/strimp.hxx) + N - 1, + {} //TODO: drop initialization for C++20 (P1331R2) + }; + }; +#if defined(_WIN32) +#pragma pack(pop) +#endif }; #if defined RTL_STRING_UNITTEST @@ -385,8 +395,8 @@ public: @since LibreOffice 5.0 */ - template OUString(OUStringLiteral const & literal): - pData(const_cast(reinterpret_cast(&literal))) {} + template constexpr OUString(OUStringLiteral const & literal): + pData(const_cast(&literal.str)) {} template OUString(OUStringLiteral &&) = delete; /// @endcond #endif