From 629b205da46690f8626ffef14d15e6c4694b36fb Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Sun, 20 Aug 2023 18:03:59 -0700 Subject: [PATCH] dev(ustring.h): string literal operator for ustring and ustringhash (#3939) * Add string literal operators for ustring and ustringhash so you can write: ``` "foo"_us "bar"_ush ``` Note that on Cuda (but not on the host), this ustringhash construction is constexpr. * Add a new `ustringhash(const char*, size_t)` constructor, which is also constexpr on the device side. * Mark the existing `ustringhash(string_view)` constructor as constexpr on the device side. We can mark those things as constexpr on the device side because on the Cuda side, the implementation only takes the hash (which is constexpr), and does not actually add a ustring to the table (which cannot be constexpr). To make such declarations easy, add a new macro to platform.h, `OIIO_DEVICE_CONSTEXPR` which is constexpr for the device, merely inline for the host. --------- Signed-off-by: Larry Gritz --- src/include/OpenImageIO/platform.h | 9 ++++++ src/include/OpenImageIO/ustring.h | 46 ++++++++++++++++++++++++++---- src/libutil/ustring_test.cpp | 12 ++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/include/OpenImageIO/platform.h b/src/include/OpenImageIO/platform.h index 2548d42968..ded9d69e02 100644 --- a/src/include/OpenImageIO/platform.h +++ b/src/include/OpenImageIO/platform.h @@ -527,6 +527,15 @@ #endif +// OIIO_DEVICE_CONSTEXPR is like OIIO_HOSTDEVICE, but it's `constexpr` only on +// the Cuda device side, and merely inline (not constexpr) on the host side. +#ifdef __CUDA_ARCH__ +# define OIIO_DEVICE_CONSTEXPR __device__ constexpr +#else +# define OIIO_DEVICE_CONSTEXPR /*__host__*/ inline +#endif + + // OIIO_PRETTY_FUNCTION gives a text string of the current function // declaration. diff --git a/src/include/OpenImageIO/ustring.h b/src/include/OpenImageIO/ustring.h index c3e6f1a20d..bedadde5c0 100644 --- a/src/include/OpenImageIO/ustring.h +++ b/src/include/OpenImageIO/ustring.h @@ -815,23 +815,41 @@ class OIIO_UTIL_API ustringhash { } /// Construct a ustringhash from a null-terminated C string (char *). - OIIO_HOSTDEVICE explicit ustringhash(const char* str) + OIIO_DEVICE_CONSTEXPR explicit ustringhash(const char* str) { #ifdef __CUDA_ARCH__ - m_hash = Strutil::strhash(str); // GPU: just compute the hash + // GPU: just compute the hash. This can be constexpr! + m_hash = Strutil::strhash(str); #else - m_hash = ustring(str).hash(); // CPU: make ustring, get its hash + // CPU: make ustring, get its hash. Note that ustring ctr can't be + // constexpr because it has to modify the internal ustring table. + m_hash = ustring(str).hash(); +#endif + } + + OIIO_DEVICE_CONSTEXPR explicit ustringhash(const char* str, size_t len) + { +#ifdef __CUDA_ARCH__ + // GPU: just compute the hash. This can be constexpr! + m_hash = Strutil::strhash(len, str); +#else + // CPU: make ustring, get its hash. Note that ustring ctr can't be + // constexpr because it has to modify the internal ustring table. + m_hash = ustring(str, len).hash(); #endif } /// Construct a ustringhash from a string_view, which can be /// auto-converted from either a std::string. - OIIO_HOSTDEVICE explicit ustringhash(string_view str) + OIIO_DEVICE_CONSTEXPR explicit ustringhash(string_view str) { #ifdef __CUDA_ARCH__ - m_hash = Strutil::strhash(str); // GPU: just compute the hash + // GPU: just compute the hash. This can be constexpr! + m_hash = Strutil::strhash(str); #else - m_hash = ustring(str).hash(); // CPU: make ustring, get its hash + // CPU: make ustring, get its hash. Note that ustring ctr can't be + // constexpr because it has to modify the internal ustring table. + m_hash = ustring(str).hash(); #endif } @@ -1013,6 +1031,22 @@ inline ustring::ustring(ustringhash hash) +/// ustring string literal operator +inline ustring operator""_us(const char* str, std::size_t len) +{ + return ustring(str, len); +} + + +/// ustringhash string literal operator +OIIO_DEVICE_CONSTEXPR ustringhash operator""_ush(const char* str, + std::size_t len) +{ + return ustringhash(str, len); +} + + + #if OIIO_VERSION_LESS(3, 0, 0) /// Deprecated -- This is too easy to confuse with the ustringhash class. And /// also it is unnecessary if you use std::hash. This will be removed diff --git a/src/libutil/ustring_test.cpp b/src/libutil/ustring_test.cpp index 9d8d60fd18..9fd77239cc 100644 --- a/src/libutil/ustring_test.cpp +++ b/src/libutil/ustring_test.cpp @@ -156,6 +156,12 @@ test_ustring() // std::hash OIIO_CHECK_EQUAL(std::hash {}(foo), foo.hash()); + + // string literals + auto whichtype = "foo"_us; + OIIO_CHECK_EQUAL(whichtype, ustring("foo")); + OIIO_CHECK_ASSERT((std::is_same::value)); + OIIO_CHECK_ASSERT(!(std::is_same::value)); } @@ -227,6 +233,12 @@ test_ustringhash() // formatting string OIIO_CHECK_EQUAL(Strutil::fmt::format("{}", hfoo), "foo"); + + // string literals + auto whichtype = "foo"_ush; + OIIO_CHECK_EQUAL(whichtype, ustringhash("foo")); + OIIO_CHECK_ASSERT((std::is_same::value)); + OIIO_CHECK_ASSERT(!(std::is_same::value)); }