Skip to content

Commit

Permalink
Merge pull request #22 from i80287/update-join-strings
Browse files Browse the repository at this point in the history
update join_strings.hpp and update it's tests
  • Loading branch information
i80287 authored Dec 25, 2024
2 parents 5a68766 + 5bded55 commit cf33844
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 137 deletions.
134 changes: 73 additions & 61 deletions misc/join_strings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,68 +20,66 @@ inline constexpr bool is_char_v = std::is_same_v<T, char> || std::is_same_v<T, w
#endif
std::is_same_v<T, char16_t> || std::is_same_v<T, char32_t>;

template <bool UseChar, class T>
template <bool UseWChar, class T>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE inline auto NumberToString(const T arg) {
static_assert(std::is_arithmetic_v<T>);

if constexpr (std::is_integral_v<T>) {
if (config::is_constant_evaluated() || config::is_gcc_constant_p(arg)) {
if (arg == 0) {
if constexpr (UseChar) {
return std::string{"0"};
} else {
if constexpr (UseWChar) {
return std::wstring{L"0"};
} else {
return std::string{"0"};
}
} else if constexpr (sizeof(T) > sizeof(int)) {
if constexpr (std::is_unsigned_v<T>) {
if (arg <= std::numeric_limits<unsigned>::max()) {
const unsigned comp_arg = static_cast<unsigned>(arg);
if constexpr (UseChar) {
return std::to_string(comp_arg);
} else {
if constexpr (UseWChar) {
return std::to_wstring(comp_arg);
} else {
return std::to_string(comp_arg);
}
}
} else {
if (arg >= std::numeric_limits<int>::min() &&
arg <= std::numeric_limits<int>::max()) {
const int comp_arg = static_cast<int>(arg);
if constexpr (UseChar) {
return std::to_string(comp_arg);
} else {
if constexpr (UseWChar) {
return std::to_wstring(comp_arg);
} else {
return std::to_string(comp_arg);
}
}
}
}
}
}

if constexpr (UseChar) {
return std::to_string(arg);
} else {
if constexpr (UseWChar) {
return std::to_wstring(arg);
} else {
return std::to_string(arg);
}
}

template <class CharType, class T, std::enable_if_t<is_char_v<CharType>, int> = 0>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE inline std::basic_string<CharType> ToStringOneArg(const T &arg) {
if constexpr (std::is_arithmetic_v<T>) {
if constexpr (std::is_same_v<T, CharType>) {
return std::basic_string<CharType>(std::size_t{1}, arg);
if constexpr (!std::is_arithmetic_v<T>) {
return std::basic_string<CharType>{std::basic_string_view<CharType>{arg}};
} else if constexpr (std::is_same_v<T, CharType>) {
return std::basic_string<CharType>(std::size_t{1}, arg);
} else {
static_assert(!is_char_v<T>, "implementation error");
const auto str = NumberToString<std::is_same_v<CharType, wchar_t>>(arg);
if constexpr (std::is_same_v<CharType, char> || std::is_same_v<CharType, wchar_t>) {
return str;
} else {
static_assert(!is_char_v<T>, "implementation error");
const auto str = NumberToString<std::is_same_v<CharType, char>>(arg);
if constexpr (std::is_same_v<CharType, char> || std::is_same_v<CharType, wchar_t>) {
return str;
} else {
return std::basic_string<CharType>(str.begin(), str.end());
}
return std::basic_string<CharType>(str.begin(), str.end());
}
} else {
return std::basic_string<CharType>{std::basic_string_view<CharType>{arg}};
}
}

Expand All @@ -90,18 +88,18 @@ ATTRIBUTE_ALWAYS_INLINE inline std::basic_string<CharType> ToStringOneArg(const
template <class CharType, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE
constexpr std::enable_if_t<is_char_v<CharType>, std::size_t> CalculateStringArgsSize(CharType /*c*/, const Args&... args) noexcept;
constexpr std::enable_if_t<is_char_v<CharType>, std::size_t> CalculateStringArgsSize(CharType /*c*/, Args... args) noexcept;

template <class CharType, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE
constexpr std::enable_if_t<is_char_v<CharType>, std::size_t> CalculateStringArgsSize(std::basic_string_view<CharType> s, const Args&... args) noexcept;
constexpr std::enable_if_t<is_char_v<CharType>, std::size_t> CalculateStringArgsSize(std::basic_string_view<CharType> s, Args... args) noexcept;

// clang-format on

template <class CharType, class... Args>
constexpr std::enable_if_t<is_char_v<CharType>, std::size_t> CalculateStringArgsSize(
CharType /*c*/, const Args &...args) noexcept {
CharType /*c*/, Args... args) noexcept {
size_t size = 1;
if constexpr (sizeof...(args) > 0) {
size += join_strings_detail::CalculateStringArgsSize<CharType>(args...);
Expand All @@ -111,7 +109,7 @@ constexpr std::enable_if_t<is_char_v<CharType>, std::size_t> CalculateStringArgs

template <class CharType, class... Args>
constexpr std::enable_if_t<is_char_v<CharType>, std::size_t> CalculateStringArgsSize(
std::basic_string_view<CharType> s, const Args &...args) noexcept {
std::basic_string_view<CharType> s, Args... args) noexcept {
size_t size = s.size();
if constexpr (sizeof...(args) > 0) {
size += join_strings_detail::CalculateStringArgsSize<CharType>(args...);
Expand All @@ -125,19 +123,20 @@ template <class CharType, class... Args>
ATTRIBUTE_NONNULL_ALL_ARGS
ATTRIBUTE_ACCESS(write_only, 1)
ATTRIBUTE_ALWAYS_INLINE
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringsInplace(CharType* result, CharType c, const Args&... args) noexcept;
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringsInplace(CharType* result, CharType c, Args... args) noexcept;

template <class CharType, class... Args>
ATTRIBUTE_NONNULL_ALL_ARGS
ATTRIBUTE_ACCESS(write_only, 1)
ATTRIBUTE_ALWAYS_INLINE
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringsInplace(CharType* result, std::basic_string_view<CharType> s, const Args&... args) noexcept;
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringsInplace(CharType* result, std::basic_string_view<CharType> s, Args... args) noexcept;

// clang-format on

template <class CharType, class... Args>
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringsInplace(
CharType *result, CharType c, const Args &...args) noexcept {
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringsInplace(CharType *result,
CharType c,
Args... args) noexcept {
*result = c;
if constexpr (sizeof...(args) > 0) {
join_strings_detail::WriteStringsInplace<CharType>(result + 1, args...);
Expand All @@ -146,7 +145,7 @@ constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringsInplace(

template <class CharType, class... Args>
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringsInplace(
CharType *result, std::basic_string_view<CharType> s, const Args &...args) noexcept {
CharType *result, std::basic_string_view<CharType> s, Args... args) noexcept {
std::char_traits<CharType>::copy(result, s.data(), s.size());
if constexpr (sizeof...(args) > 0) {
join_strings_detail::WriteStringsInplace<CharType>(result + s.size(), args...);
Expand All @@ -159,65 +158,68 @@ template <class CharType, class... Args>
ATTRIBUTE_NONNULL_ALL_ARGS
ATTRIBUTE_SIZED_ACCESS(write_only, 1, 2)
ATTRIBUTE_ALWAYS_INLINE
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringToBuffer(CharType* buffer, size_t /*buffer_size*/, const Args&... args) noexcept {
constexpr std::enable_if_t<is_char_v<CharType>, void> WriteStringToBuffer(CharType* buffer, size_t /*buffer_size*/, Args... args) noexcept {
join_strings_detail::WriteStringsInplace<CharType>(buffer, args...);
}

// clang-format on

template <class CharType, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE inline std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>>
JoinStringsImpl(const Args &...args) {
std::size_t size = CalculateStringArgsSize<CharType>(args...);
std::basic_string<CharType> result(size, CharType{});
join_strings_detail::WriteStringToBuffer<CharType>(result.data(), result.size(), args...);
return result;
[[nodiscard]] inline std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>>
JoinStringsImpl(Args... args) {
if constexpr (sizeof...(args) >= 2) {
const std::size_t size = CalculateStringArgsSize<CharType>(args...);
std::basic_string<CharType> result(size, CharType{});
join_strings_detail::WriteStringToBuffer<CharType>(result.data(), result.size(), args...);
return result;
} else {
return join_strings_detail::ToStringOneArg<CharType>(args...);
}
}

// clang-format off

template <class CharType, size_t I, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE
inline std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>> JoinStringsConvArgsToStrViewImpl(std::basic_string_view<CharType> str, const Args&... args);
inline
std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>> JoinStringsConvArgsToStrViewImpl(std::basic_string_view<CharType> str, const Args&... args);

template <class CharType, size_t I, class... Args>
[[nodiscard]]
ATTRIBUTE_ACCESS(read_only, 1)
ATTRIBUTE_ALWAYS_INLINE
inline std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>> JoinStringsConvArgsToStrViewImpl(const CharType* str, const Args&... args);
inline
std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>> JoinStringsConvArgsToStrViewImpl(const CharType* str, const Args&... args);

template <class CharType, size_t I, class T, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE
inline std::enable_if_t<is_char_v<CharType> && std::is_arithmetic_v<T>, std::basic_string<CharType>> JoinStringsConvArgsToStrViewImpl(T num, const Args&... args);
inline
std::enable_if_t<is_char_v<CharType> && std::is_arithmetic_v<T>, std::basic_string<CharType>> JoinStringsConvArgsToStrViewImpl(T num, const Args&... args);

// clang-format on

template <class CharType, size_t I, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE inline std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>>
inline std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>>
JoinStringsConvArgsToStrViewImpl(std::basic_string_view<CharType> str, const Args &...args) {
if constexpr (I == 1 + sizeof...(args)) {
return join_strings_detail::JoinStringsImpl<CharType>(str, args...);
} else {
return join_strings_detail::JoinStringsConvArgsToStrViewImpl<CharType, I + 1>(args..., str);
}
}

template <class CharType, size_t I, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE inline std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>>
inline std::enable_if_t<is_char_v<CharType>, std::basic_string<CharType>>
JoinStringsConvArgsToStrViewImpl(const CharType *str, const Args &...args) {
static_assert(I < 1 + sizeof...(args));
return join_strings_detail::JoinStringsConvArgsToStrViewImpl<CharType, I + 1>(
args..., str ? std::basic_string_view<CharType>{str} : std::basic_string_view<CharType>{});
}

template <class CharType, size_t I, class T, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE inline std::enable_if_t<is_char_v<CharType> && std::is_arithmetic_v<T>,
std::basic_string<CharType>>
inline std::enable_if_t<is_char_v<CharType> && std::is_arithmetic_v<T>, std::basic_string<CharType>>
JoinStringsConvArgsToStrViewImpl(T num, const Args &...args) {
if constexpr (std::is_same_v<T, CharType>) {
if constexpr (I == 1 + sizeof...(args)) {
Expand Down Expand Up @@ -325,27 +327,37 @@ using determine_char_t = typename determine_char_type<Args...>::type;

// clang-format off

/// @brief Join arguments @a args... (converting to string if necessary)
/// @tparam ...Args Types of the arguments to join, e.g. char types, pointers to them,
/// basic_string, basic_string_view, integral/floating point values
/// @tparam HintCharType hint char type (default: `char`).
/// Can be passed if, for instance, JoinStrings(1, 2, 3.0)
/// should be std::wstring: JoinStrings<wchar_t>(1, 2, 3.0)
/// @param ...args arguments to join
/// @return joined args as a string of type std::basic_string<CharType>
/// where CharType is deducted by the @a Args... or HintCharType
/// if @a Args... is pack of numeric types
template <class HintCharType = char, class... Args>
[[nodiscard]]
ATTRIBUTE_ALWAYS_INLINE
inline auto JoinStrings(const Args&... args) {
[[nodiscard]] ATTRIBUTE_ALWAYS_INLINE inline auto JoinStrings(const Args&... args) {
static_assert(sizeof...(args) >= 1, "Empty input is explicitly prohibited");

using DeducedCharType = join_strings_detail::determine_char_t<Args...>;

static_assert(join_strings_detail::is_char_v<HintCharType>, "Hint type should be char, wchar_t, char8_t, char16_t or char32_t");
using CharType = std::conditional_t<join_strings_detail::is_char_v<DeducedCharType>, DeducedCharType, HintCharType>;

constexpr bool kAllCharTypesAreSame = std::conjunction_v<join_strings_detail::same_char_types<Args, CharType>...>;
static_assert(
std::conjunction_v<join_strings_detail::same_char_types<Args, CharType>...>,
kAllCharTypesAreSame,
"Hint:\n"
" Some non integral/float arguments have different char types\n"
" For example, both std::string and std::wstring might have been passed to the JoinStrings\n"
);
" For example, both std::string and std::wstring might have been passed to the JoinStrings\n");

static_assert(sizeof...(args) >= 1, "Empty input is explicitly prohibited");
if constexpr (sizeof...(args) >= 2) {
if constexpr (kAllCharTypesAreSame) {
return join_strings_detail::JoinStringsConvArgsToStrViewImpl<CharType, 0>(args...);
} else {
return join_strings_detail::ToStringOneArg<CharType>(args...);
// Do not make more unreadable CEs when kAllCharTypesAreSame == false, static assertion has already failed
return std::basic_string<CharType>{};
}
}

Expand Down
Loading

0 comments on commit cf33844

Please sign in to comment.