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.
This commit is contained in:
parent
4df320bbed
commit
7204bc6f06
|
@ -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)
|
||||
|
|
|
@ -12,37 +12,87 @@ namespace CppUtilities {
|
|||
/// \cond
|
||||
namespace Helper {
|
||||
|
||||
template <class StringType, Traits::EnableIf<std::is_class<StringType>> * = nullptr> inline std::size_t computeTupleElementSize(const StringType *str)
|
||||
template <class StringType, class ViewType> using IsStringViewType = std::is_same<ViewType, std::basic_string_view<typename StringType::value_type>>;
|
||||
template <class StringType, class CharType> using IsCharType = std::is_same<typename StringType::value_type, CharType>;
|
||||
namespace Detail {
|
||||
template <typename StringType, typename T>
|
||||
auto IsStringType(int)
|
||||
-> decltype(std::declval<StringType &>().append(std::declval<const T &>()), std::declval<const T &>().size(), Traits::Bool<true>{});
|
||||
template <typename StringType, typename T> Traits::Bool<false> IsStringType(...);
|
||||
template <typename StringType> void functionTakingConstStringRef(const StringType &str);
|
||||
template <typename StringType, typename T>
|
||||
auto IsConvertibleToConstStringRef(int) -> decltype(functionTakingConstStringRef<StringType>(std::declval<const T &>()), Traits::Bool<true>{});
|
||||
template <typename StringType, typename T> Traits::Bool<false> IsConvertibleToConstStringRef(...);
|
||||
template <typename StringType, typename T>
|
||||
auto IsConvertibleToConstStringRefViaNative(int)
|
||||
-> decltype(functionTakingConstStringRef<StringType>(std::declval<const T &>().native()), Traits::Bool<true>{});
|
||||
template <typename StringType, typename T> Traits::Bool<false> IsConvertibleToConstStringRefViaNative(...);
|
||||
} // namespace Detail
|
||||
template <typename StringType, typename StringType2>
|
||||
using IsStringType = Traits::All<Traits::Not<IsStringViewType<StringType, StringType2>>, decltype(Detail::IsStringType<StringType, StringType2>(0))>;
|
||||
template <typename StringType, typename StringType2>
|
||||
using IsConvertibleToConstStringRefViaNative = Traits::All<Traits::Not<IsStringType<StringType, StringType2>>,
|
||||
decltype(Detail::IsConvertibleToConstStringRefViaNative<StringType, StringType2>(0))>;
|
||||
template <typename StringType, typename StringType2>
|
||||
using IsConvertibleToConstStringRef = Traits::All<Traits::Not<Traits::Any<IsStringType<StringType, StringType2>, IsCharType<StringType, StringType2>,
|
||||
IsConvertibleToConstStringRefViaNative<StringType, StringType2>>>,
|
||||
decltype(Detail::IsConvertibleToConstStringRef<StringType, StringType2>(0))>;
|
||||
|
||||
template <class StringType, class StringType2, Traits::EnableIf<IsStringType<StringType, StringType2>> * = nullptr>
|
||||
inline std::size_t computeTupleElementSize(const StringType2 *str)
|
||||
{
|
||||
return str->size();
|
||||
}
|
||||
|
||||
template <class StringType, Traits::EnableIf<std::is_class<StringType>> * = nullptr> inline std::size_t computeTupleElementSize(const StringType &str)
|
||||
template <class StringType, class StringType2, Traits::EnableIf<IsStringType<StringType, StringType2>> * = nullptr>
|
||||
inline std::size_t computeTupleElementSize(const StringType2 &str)
|
||||
{
|
||||
return str.size();
|
||||
}
|
||||
|
||||
template <class StringType, class ViewType,
|
||||
Traits::EnableIf<std::is_same<ViewType, std::basic_string_view<typename StringType::value_type>>> * = nullptr>
|
||||
template <class StringType, class ConvertibleType, Traits::EnableIf<IsConvertibleToConstStringRef<StringType, ConvertibleType>> * = nullptr>
|
||||
inline std::size_t computeTupleElementSize(const ConvertibleType *str)
|
||||
{
|
||||
return computeTupleElementSize<StringType, StringType>(*str);
|
||||
}
|
||||
|
||||
template <class StringType, class ConvertibleType, Traits::EnableIf<IsConvertibleToConstStringRef<StringType, ConvertibleType>> * = nullptr>
|
||||
inline std::size_t computeTupleElementSize(const ConvertibleType &str)
|
||||
{
|
||||
return computeTupleElementSize<StringType, StringType>(str);
|
||||
}
|
||||
|
||||
template <class StringType, class ConvertibleType, Traits::EnableIf<IsConvertibleToConstStringRefViaNative<StringType, ConvertibleType>> * = nullptr>
|
||||
inline std::size_t computeTupleElementSize(const ConvertibleType *str)
|
||||
{
|
||||
return computeTupleElementSize<StringType, StringType>(str->native());
|
||||
}
|
||||
|
||||
template <class StringType, class ConvertibleType, Traits::EnableIf<IsConvertibleToConstStringRefViaNative<StringType, ConvertibleType>> * = nullptr>
|
||||
inline std::size_t computeTupleElementSize(const ConvertibleType &str)
|
||||
{
|
||||
return computeTupleElementSize<StringType, StringType>(str.native());
|
||||
}
|
||||
|
||||
template <class StringType, class ViewType, Traits::EnableIf<IsStringViewType<StringType, ViewType>> * = nullptr>
|
||||
inline std::size_t computeTupleElementSize(const ViewType *str)
|
||||
{
|
||||
return str->size();
|
||||
}
|
||||
|
||||
template <class StringType, class ViewType,
|
||||
Traits::EnableIf<std::is_same<ViewType, std::basic_string_view<typename StringType::value_type>>> * = nullptr>
|
||||
template <class StringType, class ViewType, Traits::EnableIf<IsStringViewType<StringType, ViewType>> * = nullptr>
|
||||
inline std::size_t computeTupleElementSize(ViewType str)
|
||||
{
|
||||
return str.size();
|
||||
}
|
||||
|
||||
template <class StringType, class CharType, Traits::EnableIf<std::is_same<typename StringType::value_type, CharType>> * = nullptr>
|
||||
template <class StringType, class CharType, Traits::EnableIf<IsCharType<StringType, CharType>> * = nullptr>
|
||||
constexpr std::size_t computeTupleElementSize(const CharType *str)
|
||||
{
|
||||
return std::char_traits<CharType>::length(str);
|
||||
}
|
||||
|
||||
template <class StringType, class CharType, Traits::EnableIf<std::is_same<typename StringType::value_type, CharType>> * = nullptr>
|
||||
template <class StringType, class CharType, Traits::EnableIf<IsCharType<StringType, CharType>> * = nullptr>
|
||||
constexpr std::size_t computeTupleElementSize(CharType)
|
||||
{
|
||||
return 1;
|
||||
|
@ -73,37 +123,51 @@ constexpr std::size_t computeTupleElementSize(IntegralType number, typename Stri
|
|||
template <class StringType, typename TupleType, Traits::EnableIf<Traits::IsSpecializationOf<std::decay_t<TupleType>, std::tuple>> * = nullptr>
|
||||
constexpr std::size_t computeTupleElementSize(TupleType &&tuple, typename StringType::value_type base = 10);
|
||||
|
||||
template <class StringType, Traits::EnableIf<std::is_class<StringType>> * = nullptr> inline void append(StringType &target, const StringType *str)
|
||||
template <class StringType, class StringType2,
|
||||
Traits::EnableIfAny<IsStringType<StringType, StringType2>, IsConvertibleToConstStringRef<StringType, StringType2>> * = nullptr>
|
||||
inline void append(StringType &target, const StringType2 *str)
|
||||
{
|
||||
target.append(*str);
|
||||
}
|
||||
|
||||
template <class StringType, Traits::EnableIf<std::is_class<StringType>> * = nullptr> inline void append(StringType &target, const StringType &str)
|
||||
template <class StringType, class StringType2,
|
||||
Traits::EnableIfAny<IsStringType<StringType, StringType2>, IsConvertibleToConstStringRef<StringType, StringType2>> * = nullptr>
|
||||
inline void append(StringType &target, const StringType2 &str)
|
||||
{
|
||||
target.append(str);
|
||||
}
|
||||
|
||||
template <class StringType, class ViewType,
|
||||
Traits::EnableIf<std::is_same<ViewType, std::basic_string_view<typename StringType::value_type>>> * = nullptr>
|
||||
template <class StringType, class StringType2, Traits::EnableIf<IsConvertibleToConstStringRefViaNative<StringType, StringType2>> * = nullptr>
|
||||
inline void append(StringType &target, const StringType2 *str)
|
||||
{
|
||||
target.append(str->native());
|
||||
}
|
||||
|
||||
template <class StringType, class StringType2, Traits::EnableIf<IsConvertibleToConstStringRefViaNative<StringType, StringType2>> * = nullptr>
|
||||
inline void append(StringType &target, const StringType2 &str)
|
||||
{
|
||||
target.append(str.native());
|
||||
}
|
||||
|
||||
template <class StringType, class ViewType, Traits::EnableIf<IsStringViewType<StringType, ViewType>> * = nullptr>
|
||||
inline void append(StringType &target, const ViewType *str)
|
||||
{
|
||||
target.append(*str);
|
||||
}
|
||||
|
||||
template <class StringType, class ViewType,
|
||||
Traits::EnableIf<std::is_same<ViewType, std::basic_string_view<typename StringType::value_type>>> * = nullptr>
|
||||
template <class StringType, class ViewType, Traits::EnableIf<IsStringViewType<StringType, ViewType>> * = nullptr>
|
||||
inline void append(StringType &target, ViewType str)
|
||||
{
|
||||
target.append(str);
|
||||
}
|
||||
|
||||
template <class StringType, class CharType, Traits::EnableIf<std::is_same<typename StringType::value_type, CharType>> * = nullptr>
|
||||
template <class StringType, class CharType, Traits::EnableIf<IsCharType<StringType, CharType>> * = nullptr>
|
||||
inline void append(StringType &target, const CharType *str)
|
||||
{
|
||||
target.append(str);
|
||||
}
|
||||
|
||||
template <class StringType, class CharType, Traits::EnableIf<std::is_same<typename StringType::value_type, CharType>> * = nullptr>
|
||||
template <class StringType, class CharType, Traits::EnableIf<IsCharType<StringType, CharType>> * = nullptr>
|
||||
inline void append(StringType &target, CharType c)
|
||||
{
|
||||
target += c;
|
||||
|
|
|
@ -13,6 +13,10 @@ using namespace CppUtilities;
|
|||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
|
||||
#include <filesystem>
|
||||
#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<std::string, std::string>::value);
|
||||
static_assert(!Helper::IsStringType<std::string, std::wstring>::value);
|
||||
static_assert(Helper::IsStringType<std::wstring, std::wstring>::value);
|
||||
static_assert(Helper::IsStringViewType<std::string, std::string_view>::value);
|
||||
static_assert(!Helper::IsStringViewType<std::wstring, std::string_view>::value);
|
||||
static_assert(Helper::IsStringViewType<std::wstring, std::wstring_view>::value);
|
||||
static_assert(Helper::IsConvertibleToConstStringRef<std::string, ConvertibleToString>::value);
|
||||
#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
|
||||
static_assert(!Helper::IsConvertibleToConstStringRef<std::string, std::filesystem::path>::value, "conversion via native() preferred");
|
||||
#endif
|
||||
static_assert(
|
||||
!Helper::IsConvertibleToConstStringRef<std::string, std::string>::value, "yes, in this context this should not be considered convertible");
|
||||
static_assert(!Helper::IsConvertibleToConstStringRef<std::wstring, ConvertibleToString>::value);
|
||||
#ifdef CPP_UTILITIES_USE_STANDARD_FILESYSTEM
|
||||
static_assert(Helper::IsConvertibleToConstStringRefViaNative<std::string, std::filesystem::path>::value);
|
||||
#endif
|
||||
static_assert(!Helper::IsConvertibleToConstStringRefViaNative<std::string, std::string>::value);
|
||||
|
||||
// conversion of string-tuple to string (the actual string builder)
|
||||
const tuple<const char *, string, int, const char *> 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");
|
||||
|
|
Loading…
Reference in New Issue