Files
2025-12-10 14:38:26 -08:00

187 lines
6.6 KiB
C++

// *****************************************************************************
// * 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 <cstring> //strlen
#include <string_view>
#include "type_traits.h"
//uniform access to string-like types, both classes and character arrays
namespace zen
{
/* isStringLike<>:
isStringLike<const wchar_t*> //equals "true"
isStringLike<const int*> //equals "false"
GetCharTypeT<>:
GetCharTypeT<std::wstring> //equals wchar_t
GetCharTypeT<wchar_t[5]> //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 */
//---------------------- implementation ----------------------
namespace impl
{
template <class S, class Char> //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<S>().c_str())) == sizeof(Yes) };
};
template <class S, bool isStringClass> struct GetCharTypeImpl { using Type = void; };
template <class S>
struct GetCharTypeImpl<S, true>
{
using Type = std::conditional_t<HasConversion<S, wchar_t>::value, wchar_t,
/**/ std::conditional_t<HasConversion<S, char >::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<char, false> { using Type = char; };
template <> struct GetCharTypeImpl<wchar_t, false> { using Type = wchar_t; };
template <> struct GetCharTypeImpl<std::basic_string_view<char >, false> { using Type = char; };
template <> struct GetCharTypeImpl<std::basic_string_view<wchar_t >, false> { using Type = wchar_t; };
template <> struct GetCharTypeImpl<std::basic_string_view<const char >, false> { using Type = char; };
template <> struct GetCharTypeImpl<std::basic_string_view<const wchar_t>, 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 S>
class StringTraits
{
using CleanType = std::remove_cvref_t<S>;
using NonArrayType = std::remove_extent_t <CleanType>;
using NonPtrType = std::remove_pointer_t<NonArrayType>;
using UndecoratedType = std::remove_cv_t <NonPtrType>; //handle "const char* const"
public:
enum
{
isStringClass = hasMemberType_value_type<CleanType>&&
hasMember_c_str <CleanType>&&
hasMember_length <CleanType>
};
using CharType = typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type;
enum
{
isStringLike = std::is_same_v<CharType, char> ||
std::is_same_v<CharType, wchar_t>
};
};
}
template <class T>
constexpr bool isStringLike = impl::StringTraits<T>::isStringLike;
template <class T>
using GetCharTypeT = typename impl::StringTraits<T>::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); }
#if 0 //no significant perf difference for "comparison" test case between cStringLength/wcslen:
template <class C> inline
size_t cStringLength(const C* str)
{
static_assert(std::is_same_v<C, char> || std::is_same_v<C, wchar_t>);
size_t len = 0;
while (*str++ != 0)
++len;
return len;
}
#endif
template <class S, typename = std::enable_if_t<StringTraits<S>::isStringClass>> inline
const GetCharTypeT<S>* 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<char >& ref) { return ref.data(); }
inline const wchar_t* strBegin(const std::basic_string_view<wchar_t >& ref) { return ref.data(); }
inline const char* strBegin(const std::basic_string_view<const char >& ref) { return ref.data(); }
inline const wchar_t* strBegin(const std::basic_string_view<const wchar_t>& ref) { return ref.data(); }
template <class S, typename = std::enable_if_t<StringTraits<S>::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<char >& ref) { return ref.length(); }
inline size_t strLength(const std::basic_string_view<wchar_t >& ref) { return ref.length(); }
inline size_t strLength(const std::basic_string_view<const char >& ref) { return ref.length(); }
inline size_t strLength(const std::basic_string_view<const wchar_t>& ref) { return ref.length(); }
}
template <class S> inline
auto strBegin(S&& str)
{
static_assert(isStringLike<S>);
return impl::strBegin(std::forward<S>(str));
}
template <class S> inline
size_t strLength(S&& str)
{
static_assert(isStringLike<S>);
return impl::strLength(std::forward<S>(str));
}
}
#endif //STRING_TRAITS_H_813274321443234