// ***************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * // ***************************************************************************** #ifndef STRING_TRAITS_H_813274321443234 #define STRING_TRAITS_H_813274321443234 #include //strlen #include #include "type_traits.h" //uniform access to string-like types, both classes and character arrays namespace zen { /* isStringLike<>: isStringLike //equals "true" isStringLike //equals "false" GetCharTypeT<>: GetCharTypeT //equals wchar_t GetCharTypeT //equals wchar_t strLength(): strLength(str); //equals str.length() strLength(array); //equals cStringLength(array) strBegin(): -> not null-terminated! -> may be nullptr if length is 0! std::wstring str(L"dummy"); char array[] = "dummy"; strBegin(str); //returns str.c_str() strBegin(array); //returns array */ //reference a sub-string for consumption by zen string_tools //=> std::string_view seems decent, but of course fucks up in one regard: construction template auto makeStringView(Iterator first, Iterator last); //e.g. this constructor is not available (at least on clang) template auto makeStringView(Iterator first, size_t len); //---------------------- implementation ---------------------- namespace impl { template //test if result of S::c_str() can convert to const Char* class HasConversion { using Yes = char[1]; using No = char[2]; static Yes& hasConversion(const Char*); static No& hasConversion(...); public: enum { value = sizeof(hasConversion(std::declval().c_str())) == sizeof(Yes) }; }; template struct GetCharTypeImpl { using Type = void; }; template struct GetCharTypeImpl { using Type = std::conditional_t::value, wchar_t, /**/ std::conditional_t::value, char, void>>; //using Type = typename S::value_type; /*DON'T use S::value_type: 1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*" 2. wxString, wxWidgets v2.9, has some questionable string design: wxString::c_str() returns a proxy (wxCStrData) which is implicitly convertible to *both* "const char*" and "const wchar_t*" while wxString::value_type is a wrapper around an unsigned int */ }; template <> struct GetCharTypeImpl { using Type = char; }; template <> struct GetCharTypeImpl { using Type = wchar_t; }; template <> struct GetCharTypeImpl, false> { using Type = char; }; template <> struct GetCharTypeImpl, false> { using Type = wchar_t; }; template <> struct GetCharTypeImpl, false> { using Type = char; }; template <> struct GetCharTypeImpl, false> { using Type = wchar_t; }; ZEN_INIT_DETECT_MEMBER_TYPE(value_type) ZEN_INIT_DETECT_MEMBER(c_str) //we don't know the exact declaration of the member attribute and it may be in a base class! ZEN_INIT_DETECT_MEMBER(length) // template class StringTraits { using CleanType = std::remove_cvref_t; using NonArrayType = std::remove_extent_t ; using NonPtrType = std::remove_pointer_t; using UndecoratedType = std::remove_cv_t ; //handle "const char* const" public: enum { isStringClass = hasMemberType_value_type && hasMember_c_str && hasMember_length }; using CharType = typename GetCharTypeImpl::Type; enum { isStringLike = std::is_same_v || std::is_same_v }; }; } template constexpr bool isStringLike = impl::StringTraits::isStringLike; template using GetCharTypeT = typename impl::StringTraits::CharType; namespace impl { //strlen/wcslen are vectorized since VS14 CTP3 inline size_t cStringLength(const char* str) { return std::strlen(str); } inline size_t cStringLength(const wchar_t* str) { return std::wcslen(str); } //no significant perf difference for "comparison" test case between cStringLength/wcslen: #if 0 template inline size_t cStringLength(const C* str) { static_assert(std::is_same_v || std::is_same_v); size_t len = 0; while (*str++ != 0) ++len; return len; } #endif template ::isStringClass>> inline const GetCharTypeT* strBegin(const S& str) //SFINAE: T must be a "string" { return str.c_str(); } inline const char* strBegin(const char* str) { return str; } inline const wchar_t* strBegin(const wchar_t* str) { return str; } inline const char* strBegin(const char& ch) { return &ch; } inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } inline const char* strBegin(const std::basic_string_view& ref) { return ref.data(); } inline const wchar_t* strBegin(const std::basic_string_view& ref) { return ref.data(); } inline const char* strBegin(const std::basic_string_view& ref) { return ref.data(); } inline const wchar_t* strBegin(const std::basic_string_view& ref) { return ref.data(); } template ::isStringClass>> inline size_t strLength(const S& str) //SFINAE: T must be a "string" { return str.length(); } inline size_t strLength(const char* str) { return cStringLength(str); } inline size_t strLength(const wchar_t* str) { return cStringLength(str); } inline size_t strLength(char) { return 1; } inline size_t strLength(wchar_t) { return 1; } inline size_t strLength(const std::basic_string_view& ref) { return ref.length(); } inline size_t strLength(const std::basic_string_view& ref) { return ref.length(); } inline size_t strLength(const std::basic_string_view& ref) { return ref.length(); } inline size_t strLength(const std::basic_string_view& ref) { return ref.length(); } } template inline auto strBegin(S&& str) { static_assert(isStringLike); return impl::strBegin(std::forward(str)); } template inline size_t strLength(S&& str) { static_assert(isStringLike); return impl::strLength(std::forward(str)); } template inline auto makeStringView(Iterator first, Iterator last) { using CharType = GetCharTypeT; return std::basic_string_view(first != last ? &*first : reinterpret_cast(0x1000), /*Win32 APIs like CompareStringOrdinal() choke on nullptr!*/ last - first); } template inline auto makeStringView(Iterator first, size_t len) { return makeStringView(first, first + len); } } #endif //STRING_TRAITS_H_813274321443234