From 7204bc6f06948b866030bf868bf5ba0374590cbb Mon Sep 17 00:00:00 2001 From: Martchus Date: Thu, 13 Feb 2020 17:03:20 +0100 Subject: [PATCH] Support using string builder with types convertible to target string type So e.g. std::filesystem::path can be used to build an std::string. --- CMakeLists.txt | 4 +- conversion/stringbuilder.h | 96 +++++++++++++++++++++++++++++++------- tests/conversiontests.cpp | 30 ++++++++++++ 3 files changed, 112 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 636fc73..4fb731f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,8 +112,8 @@ set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}") set(META_APP_DESCRIPTION "Useful C++ classes and routines such as argument parser, IO and conversion utilities") set(META_FEATURES_FOR_COMPILER_DETECTION_HEADER cxx_thread_local) set(META_VERSION_MAJOR 5) -set(META_VERSION_MINOR 2) -set(META_VERSION_PATCH 1) +set(META_VERSION_MINOR 3) +set(META_VERSION_PATCH 0) # find required 3rd party libraries include(3rdParty) diff --git a/conversion/stringbuilder.h b/conversion/stringbuilder.h index 932b8e3..0237275 100644 --- a/conversion/stringbuilder.h +++ b/conversion/stringbuilder.h @@ -12,37 +12,87 @@ namespace CppUtilities { /// \cond namespace Helper { -template > * = nullptr> inline std::size_t computeTupleElementSize(const StringType *str) +template using IsStringViewType = std::is_same>; +template using IsCharType = std::is_same; +namespace Detail { +template +auto IsStringType(int) + -> decltype(std::declval().append(std::declval()), std::declval().size(), Traits::Bool{}); +template Traits::Bool IsStringType(...); +template void functionTakingConstStringRef(const StringType &str); +template +auto IsConvertibleToConstStringRef(int) -> decltype(functionTakingConstStringRef(std::declval()), Traits::Bool{}); +template Traits::Bool IsConvertibleToConstStringRef(...); +template +auto IsConvertibleToConstStringRefViaNative(int) + -> decltype(functionTakingConstStringRef(std::declval().native()), Traits::Bool{}); +template Traits::Bool IsConvertibleToConstStringRefViaNative(...); +} // namespace Detail +template +using IsStringType = Traits::All>, decltype(Detail::IsStringType(0))>; +template +using IsConvertibleToConstStringRefViaNative = Traits::All>, + decltype(Detail::IsConvertibleToConstStringRefViaNative(0))>; +template +using IsConvertibleToConstStringRef = Traits::All, IsCharType, + IsConvertibleToConstStringRefViaNative>>, + decltype(Detail::IsConvertibleToConstStringRef(0))>; + +template > * = nullptr> +inline std::size_t computeTupleElementSize(const StringType2 *str) { return str->size(); } -template > * = nullptr> inline std::size_t computeTupleElementSize(const StringType &str) +template > * = nullptr> +inline std::size_t computeTupleElementSize(const StringType2 &str) { return str.size(); } -template >> * = nullptr> +template > * = nullptr> +inline std::size_t computeTupleElementSize(const ConvertibleType *str) +{ + return computeTupleElementSize(*str); +} + +template > * = nullptr> +inline std::size_t computeTupleElementSize(const ConvertibleType &str) +{ + return computeTupleElementSize(str); +} + +template > * = nullptr> +inline std::size_t computeTupleElementSize(const ConvertibleType *str) +{ + return computeTupleElementSize(str->native()); +} + +template > * = nullptr> +inline std::size_t computeTupleElementSize(const ConvertibleType &str) +{ + return computeTupleElementSize(str.native()); +} + +template > * = nullptr> inline std::size_t computeTupleElementSize(const ViewType *str) { return str->size(); } -template >> * = nullptr> +template > * = nullptr> inline std::size_t computeTupleElementSize(ViewType str) { return str.size(); } -template > * = nullptr> +template > * = nullptr> constexpr std::size_t computeTupleElementSize(const CharType *str) { return std::char_traits::length(str); } -template > * = nullptr> +template > * = nullptr> constexpr std::size_t computeTupleElementSize(CharType) { return 1; @@ -73,37 +123,51 @@ constexpr std::size_t computeTupleElementSize(IntegralType number, typename Stri template , std::tuple>> * = nullptr> constexpr std::size_t computeTupleElementSize(TupleType &&tuple, typename StringType::value_type base = 10); -template > * = nullptr> inline void append(StringType &target, const StringType *str) +template , IsConvertibleToConstStringRef> * = nullptr> +inline void append(StringType &target, const StringType2 *str) { target.append(*str); } -template > * = nullptr> inline void append(StringType &target, const StringType &str) +template , IsConvertibleToConstStringRef> * = nullptr> +inline void append(StringType &target, const StringType2 &str) { target.append(str); } -template >> * = nullptr> +template > * = nullptr> +inline void append(StringType &target, const StringType2 *str) +{ + target.append(str->native()); +} + +template > * = nullptr> +inline void append(StringType &target, const StringType2 &str) +{ + target.append(str.native()); +} + +template > * = nullptr> inline void append(StringType &target, const ViewType *str) { target.append(*str); } -template >> * = nullptr> +template > * = nullptr> inline void append(StringType &target, ViewType str) { target.append(str); } -template > * = nullptr> +template > * = nullptr> inline void append(StringType &target, const CharType *str) { target.append(str); } -template > * = nullptr> +template > * = nullptr> inline void append(StringType &target, CharType c) { target += c; diff --git a/tests/conversiontests.cpp b/tests/conversiontests.cpp index 8bf707f..18d5bd9 100644 --- a/tests/conversiontests.cpp +++ b/tests/conversiontests.cpp @@ -13,6 +13,10 @@ using namespace CppUtilities; #include #include +#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM +#include +#endif + using namespace std; using namespace CPPUNIT_NS; @@ -379,14 +383,40 @@ void ConversionTests::testStringConversions() CPPUNIT_ASSERT_EQUAL("16 GiB/s"s, bitrateToString(128.0 * 1e6, true)); } +struct ConvertibleToString { + operator std::string() const; +}; + void ConversionTests::testStringBuilder() { + // check whether type traits work as expected + static_assert(Helper::IsStringType::value); + static_assert(!Helper::IsStringType::value); + static_assert(Helper::IsStringType::value); + static_assert(Helper::IsStringViewType::value); + static_assert(!Helper::IsStringViewType::value); + static_assert(Helper::IsStringViewType::value); + static_assert(Helper::IsConvertibleToConstStringRef::value); +#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM + static_assert(!Helper::IsConvertibleToConstStringRef::value, "conversion via native() preferred"); +#endif + static_assert( + !Helper::IsConvertibleToConstStringRef::value, "yes, in this context this should not be considered convertible"); + static_assert(!Helper::IsConvertibleToConstStringRef::value); +#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM + static_assert(Helper::IsConvertibleToConstStringRefViaNative::value); +#endif + static_assert(!Helper::IsConvertibleToConstStringRefViaNative::value); + // conversion of string-tuple to string (the actual string builder) const tuple tuple("string1", "string2", 1234, "string3"); CPPUNIT_ASSERT_EQUAL("string1string21234string3"s, tupleToString(tuple)); CPPUNIT_ASSERT_EQUAL("foobarfoo2bar2"s, tupleToString("foo"s % "bar" % "foo2"s % "bar2")); CPPUNIT_ASSERT_EQUAL("v2.3.0"s, argsToString("v2.", 3, '.', 0)); CPPUNIT_ASSERT_EQUAL("v2.3.0"s, argsToString('v', make_tuple(2, '.', 3, '.', 0))); +#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM + CPPUNIT_ASSERT_EQUAL("path: foo"s, argsToString("path: ", std::filesystem::path("foo"))); +#endif // construction of string-tuple and final conversion to string works CPPUNIT_ASSERT_EQUAL_MESSAGE("result can be passed to any function taking a std::string"s, "123456789"s, "12" % string("34") % '5' % 67 + "89");