#ifndef CONVERSION_UTILITIES_STRINGBUILDER_H #define CONVERSION_UTILITIES_STRINGBUILDER_H #include "../misc/traits.h" #include "./stringconversion.h" #include #include namespace CppUtilities { /// \cond namespace Helper { 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 StringType2 &str) { return str.size(); } 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> inline std::size_t computeTupleElementSize(ViewType str) { return str.size(); } template > * = nullptr> constexpr std::size_t computeTupleElementSize(const CharType *str) { return std::char_traits::length(str); } template > * = nullptr> constexpr std::size_t computeTupleElementSize(CharType) { return 1; } template >, std::is_integral, std::is_unsigned> * = nullptr> constexpr std::size_t computeTupleElementSize(IntegralType number, IntegralType base = 10) { auto size = std::size_t(0u); for (auto n = number; n; n /= base, ++size) ; return size; } template >, std::is_integral, std::is_signed> * = nullptr> constexpr std::size_t computeTupleElementSize(IntegralType number, IntegralType base = 10) { auto size = std::size_t(number < 0 ? 1u : 0u); for (auto n = number; n; n /= base, ++size) ; return size; } template , std::tuple>> * = nullptr> constexpr std::size_t computeTupleElementSize(TupleType &&tuple, typename StringType::value_type base = 10); template , IsConvertibleToConstStringRef> * = nullptr> inline void append(StringType &target, const StringType2 *str) { target.append(*str); } template , IsConvertibleToConstStringRef> * = nullptr> inline void append(StringType &target, const StringType2 &str) { target.append(str); } 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> inline void append(StringType &target, ViewType str) { target.append(str); } template > * = nullptr> inline void append(StringType &target, const CharType *str) { target.append(str); } template > * = nullptr> inline void append(StringType &target, CharType c) { target += c; } template >, std::is_integral, std::is_unsigned> * = nullptr> inline void append(StringType &target, IntegralType number, IntegralType base = 10) { const auto start = target.begin() + static_cast(target.size()); do { target.insert(start, static_cast(digitToChar(number % base))); number /= base; } while (number); } template >, std::is_integral, std::is_signed> * = nullptr> inline void append(StringType &target, IntegralType number, IntegralType base = 10) { if (number < 0) { target += '-'; number = -number; } const auto start = target.begin() + static_cast(target.size()); do { target.insert(start, static_cast(digitToChar(number % base))); number /= base; } while (number); } template , std::tuple>> * = nullptr> constexpr void append(StringType &target, TupleType &&tuple, typename StringType::value_type base = 10); template struct TupleToString { static inline std::size_t precomputeSize(const Tuple &tuple) { return TupleToString::precomputeSize(tuple) + computeTupleElementSize(std::get(tuple)); } static inline void append(const Tuple &tuple, StringType &str) { TupleToString::append(tuple, str); Helper::append(str, std::get(tuple)); } }; template struct TupleToString { static inline std::size_t precomputeSize(const Tuple &tuple) { return computeTupleElementSize(std::get<0>(tuple)); } static inline void append(const Tuple &tuple, StringType &str) { Helper::append(str, std::get<0>(tuple)); } }; template , std::tuple>> *> constexpr std::size_t computeTupleElementSize(TupleType &&tuple, typename StringType::value_type base) { CPP_UTILITIES_UNUSED(base) return TupleToString>>::precomputeSize(std::forward(tuple)); } template , std::tuple>> *> constexpr void append(StringType &target, TupleType &&tuple, typename StringType::value_type base) { CPP_UTILITIES_UNUSED(base) return TupleToString>>::append(std::forward(tuple), target); } } // namespace Helper /// \endcond /*! * \brief Concatenates all strings hold by the specified \a tuple. */ template inline StringType tupleToString(const std::tuple &tuple) { StringType res; res.reserve(Helper::TupleToString::precomputeSize(tuple)); Helper::TupleToString::append(tuple, res); return res; } template inline StringType argsToString(Args &&...args) { return tupleToString(std::tuple(std::forward(args)...)); } /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ template , Traits::IsSpecializingAnyOf> * = nullptr> constexpr auto operator%(const Tuple &lhs, const StringType &rhs) -> decltype(std::tuple_cat(lhs, std::tuple(rhs))) { return std::tuple_cat(lhs, std::tuple(rhs)); } /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ template > * = nullptr> constexpr auto operator%(const Tuple &lhs, const char *rhs) -> decltype(std::tuple_cat(lhs, std::tuple(rhs))) { return std::tuple_cat(lhs, std::tuple(rhs)); } /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ template , std::is_integral> * = nullptr> constexpr auto operator%(const Tuple &lhs, IntegralType rhs) -> decltype(std::tuple_cat(lhs, std::tuple(rhs))) { return std::tuple_cat(lhs, std::tuple(rhs)); } /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ template , Traits::IsSpecializationOf> * = nullptr> constexpr auto operator%(const StringType &lhs, const StringType &rhs) -> decltype(std::tuple(lhs, rhs)) { return std::tuple(lhs, rhs); } /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ template , Traits::IsSpecializationOf> * = nullptr> constexpr auto operator%(const char *lhs, const StringType &rhs) -> decltype(std::tuple(lhs, rhs)) { return std::tuple(lhs, rhs); } /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ template , Traits::IsSpecializationOf> * = nullptr> constexpr auto operator%(const StringType &lhs, const char *rhs) -> decltype(std::tuple(lhs, rhs)) { return std::tuple(lhs, rhs); } /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ template , Traits::IsSpecializationOf> * = nullptr> constexpr auto operator%(const StringType &lhs, char rhs) -> decltype(std::tuple(lhs, rhs)) { return std::tuple(lhs, rhs); } /*! * \brief Allows construction of string-tuples via %-operator, eg. string1 % "string2" % string3. */ template , Traits::IsSpecializationOf> * = nullptr> constexpr auto operator%(char lhs, const StringType &rhs) -> decltype(std::tuple(lhs, rhs)) { return std::tuple(lhs, rhs); } /*! * \brief Allows construction of final string from previously constructed string-tuple and trailing string via +-operator. * * This is meant to be used for fast string building without multiple heap allocation, eg. * * ``` * printVelocity("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)")); * ``` */ template , Traits::Any, Traits::IsSpecializationOf>> * = nullptr> inline std::string operator+(const Tuple &lhs, const StringType &rhs) { return tupleToString(std::tuple_cat(lhs, std::tuple(rhs))); } /*! * \brief Allows construction of final string from previously constructed string-tuple and trailing string via +-operator. * * This is meant to be used for fast string building without multiple heap allocation, eg. * * ``` * printVelocity("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)")); * ``` */ template > * = nullptr> inline std::string operator+(const Tuple &lhs, const char *rhs) { return tupleToString(std::tuple_cat(lhs, std::tuple(rhs))); } /*! * \brief Allows construction of final string from previously constructed string-tuple and trailing char via +-operator. * * This is meant to be used for fast string building without multiple heap allocation, eg. * * ``` * printVelocity("velocity: " % numberToString(velocityExample) % " km/h (" % numberToString(velocityExample / 3.6) + " m/s)")); * ``` */ template , std::is_integral> * = nullptr> inline std::string operator+(const Tuple &lhs, IntegralType rhs) { return tupleToString(std::tuple_cat(lhs, std::tuple(rhs))); } } // namespace CppUtilities #endif // CONVERSION_UTILITIES_STRINGBUILDER_H