// SPDX-License-Identifier: MIT // Copyright © 2014, 2017 David Sanders // Copyright © 2019 Dawid Drozd aka Gelldur #pragma once #include #include #include #include #include #include #include /** * Inspiration: https://akrzemi1.wordpress.com/2017/06/28/compile-time-string-concatenation/ * Based on: https://gist.github.com/dsanders11/8951887 (big thanks) * * * Motivation: it would be nice to have such define: LOCATION3 * * #define S1(x) #x * #define S2(x) S1(x) * #define LOCATION __FILE__ ":" S2(__LINE__) * #define LOCATION2 __PRETTY_FUNCTION__ ":" S2(__LINE__) <-- not working!!! * #define LOCATION3 string::make(__PRETTY_FUNCTION__) + ":" + string::make(S2(__LINE__)) */ namespace gcpp::string { // Recursive comparison of each individual character in a string // The last bit with std::enable_if uses SFINAE (Substitution Failure Is Not An Error) // to rule this function out and switch to the base case for the recursion when the Index == Length template constexpr auto compareCharacters(const Left& lhs, const Right& rhs) -> typename std::enable_if::type { return lhs[Index] == rhs[Index] ? compareCharacters(lhs, rhs) : false; } // Recursion base case. If you run past the last index of template ::type = 0> constexpr bool compareCharacters(const Left& lhs, const Right& rhs) { return true; } // Helper type traits to determine the length of either // a string literal or a StringConstant (specialized below) template struct length_of { static_assert(std::is_void::value, "Unsupported type for length_of"); static constexpr std::size_t value = 1; }; template struct length_of { static constexpr std::size_t value = N - 1; }; template struct length_of { static constexpr std::size_t value = N - 1; }; template struct length_of { static constexpr std::size_t value = N - 1; }; // This small class is the heart of the constant string implementation. // It has constructors for string literals and individual chars, as well // as operators to interact with string literals or other instances. This // allows for it to have a very natural interface, and it's all constexpr // Inspired heavily by a class described in a presentation by Scott Schurr // at Boostcon: // https://github.com/boostcon/cppnow_presentations_2012/blob/master/wed/schurr_cpp11_tools_for_class_authors.pdf template class ConstexprString { public: // Constructor which takes individual chars. Allows for unpacking // parameter packs directly into the constructor template constexpr explicit ConstexprString(Characters... characters) : _value{characters..., '\0'} {} // Copy constructor template constexpr ConstexprString(const ConstexprString& rhs, std::index_sequence dummy = ConstexprString::g_indexes) : _value{rhs[Indexes]..., '\0'} {} template constexpr ConstexprString(const ConstexprString& rhs, std::index_sequence /*dummy*/) : _value{rhs[Indexes]..., '\0'} {} template constexpr ConstexprString(const char (&value)[N + 1], std::index_sequence /*dummy*/) : ConstexprString(value[Indexes]...) {} constexpr explicit ConstexprString(const char (&value)[N + 1]) : ConstexprString(value, std::make_index_sequence{}) {} // Array subscript operator, with some basic range checking constexpr char operator[](const std::size_t index) const { return index < N ? _value[index] : throw std::out_of_range("Index out of range"); } constexpr const char* c_str() const { return _value; } constexpr std::string_view view() const { return std::string_view{_value, N}; } constexpr std::size_t length() const { return N; } std::string toString() const { return std::string(_value); } friend std::ostream& operator<<(std::ostream& os, const ConstexprString& constant) { os << constant._value; return os; } protected: const char _value[N + 1]; static constexpr auto g_indexes = typename std::make_index_sequence{}; }; // Specialize the length_of trait for the ConstexprString class template struct length_of> { static constexpr std::size_t value = N; }; template struct length_of> { static constexpr std::size_t value = N; }; template struct length_of&> { static constexpr std::size_t value = N; }; // A helper trait for checking if something is a ConstexprString // without having to know the length of the string it contains template struct is_string_constant { static constexpr bool value = false; }; template struct is_string_constant> { static constexpr bool value = true; }; template struct is_string_constant&> { static constexpr bool value = true; }; template struct is_string_constant> { static constexpr bool value = true; }; template struct is_string_constant&> { static constexpr bool value = true; }; // A helper function for concatenating ConstexprStrings // Less than human friendly concat function, wrapped by a human friendly one below template constexpr ConstexprString ConcatStrings( const Left& lhs, const Right& rhs, std::index_sequence /*dummy1*/, std::index_sequence /*dummy2*/) { return ConstexprString(lhs[IndexesLeft]..., rhs[IndexesRight]...); } // Human friendly concat function for string literals template constexpr ConstexprString::value + length_of::value> concatStrings( const Left& lhs, const Right& rhs) { return ConcatStrings(lhs, rhs, typename std::make_index_sequence::value>{}, typename std::make_index_sequence::value>{}); } // Finally, operators for dealing with a string literal LHS and ConstexprString RHS // Addition operator template constexpr ConstexprString::value> operator+(const ConstexprString& lhs, const Right& rhs) { return concatStrings(lhs, rhs); } template constexpr ConstexprString::value + N> operator+(const Left& lhs, const ConstexprString& rhs) { return concatStrings(lhs, rhs); } template constexpr ConstexprString operator+(const ConstexprString& lhs, const ConstexprString& rhs) { return concatStrings(lhs, rhs); } // Equality operator template constexpr auto operator==(const ConstexprString& lhs, const Right& rhs) -> typename std::enable_if::value, bool>::type { return compareCharacters(lhs, rhs); } template constexpr auto operator!=(const ConstexprString& lhs, const Right& rhs) -> typename std::enable_if::value, bool>::type { return !(lhs == rhs); } template constexpr auto operator==(const Left& lhs, const ConstexprString& rhs) -> typename std::enable_if::value == N, bool>::type { return compareCharacters(lhs, rhs); } template constexpr auto operator!=(const Left& lhs, const ConstexprString& rhs) -> typename std::enable_if::value == N, bool>::type { return !(lhs == rhs); } template constexpr auto operator==(const ConstexprString& lhs, const ConstexprString& rhs) -> typename std::enable_if::type { return compareCharacters(lhs, rhs); } template constexpr auto operator!=(const ConstexprString& lhs, const ConstexprString& rhs) -> typename std::enable_if::type { return !(lhs == rhs); } // Different length strings can never be equal template ::value, bool>::type = 0> constexpr bool operator==(const ConstexprString& lhs, const Right& rhs) { return false; } template ::value, bool>::type = 0> constexpr bool operator!=(const ConstexprString& lhs, const Right& rhs) { return !(lhs == rhs); } // Different length strings can never be equal template ::value != N, bool>::type = 0> constexpr bool operator==(const Left& lhs, const ConstexprString& rhs) { return false; } template ::value != N, bool>::type = 0> constexpr bool operator!=(const Left& lhs, const ConstexprString& rhs) { return !(lhs == rhs); } // Different length strings can never be equal template ::type = 0> constexpr bool operator==(const ConstexprString& lhs, const ConstexprString& rhs) { return false; } template ::type = 0> constexpr bool operator!=(const ConstexprString& lhs, const ConstexprString& rhs) { return !(lhs == rhs); } template constexpr auto make(const char (&value)[N], std::index_sequence /*dummy*/) { return ConstexprString(value[Indexes]...); } template constexpr auto make(const char* value, std::index_sequence /*dummy*/) { return ConstexprString(value[Indexes]...); } // A helper factory function for creating FixedConstexprString objects // which handles figuring out the length of the string for you template constexpr auto make(const char (&value)[N]) { return make(value, typename std::make_index_sequence{}); } template constexpr auto make(const char* value) { return make(value, typename std::make_index_sequence{}); } template constexpr auto subString(ConstexprString text) { static_assert(position < text.length(), "Out of range"); constexpr auto newSize = text.length() - position + 1; return make(text.c_str() + position); } } // namespace gcpp::string