Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,22 @@ jobs:
}
]
},
{ "cxxversions": ["c++23", "c++20"],
{ "cxxversions": ["c++23", "c++20", "c++17"],
"tests": [{ "stdlibs": ["libstdc++"], "tests": ["Release.Default"]}]
}
]
},
{ "versions": ["14"],
{ "versions": ["14", "13"],
"tests": [
{ "cxxversions": ["c++26", "c++23", "c++20"],
{ "cxxversions": ["c++26", "c++23", "c++20", "c++17"],
"tests": [{ "stdlibs": ["libstdc++"], "tests": ["Release.Default"]}]
}
]
},
{
"versions": ["12", "11"],
"tests": [
{ "cxxversions": ["c++23", "c++20", "c++17"],
"tests": [{ "stdlibs": ["libstdc++"], "tests": ["Release.Default"]}]
}
]
Expand All @@ -75,7 +83,7 @@ jobs:
}
]
},
{ "cxxversions": ["c++23", "c++20"],
{ "cxxversions": ["c++23", "c++20", "c++17"],
"tests": [
{"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]}
]
Expand All @@ -84,7 +92,7 @@ jobs:
},
{ "versions": ["21", "20", "19"],
"tests": [
{ "cxxversions": ["c++26", "c++23", "c++20"],
{ "cxxversions": ["c++26", "c++23", "c++20", "c++17"],
"tests": [
{"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]}
]
Expand All @@ -93,10 +101,10 @@ jobs:
},
{ "versions": ["18", "17"],
"tests": [
{ "cxxversions": ["c++26", "c++23", "c++20"],
{ "cxxversions": ["c++26", "c++23", "c++20", "c++17"],
"tests": [{"stdlibs": ["libc++"], "tests": ["Release.Default"]}]
},
{ "cxxversions": ["c++20"],
{ "cxxversions": ["c++20", "c++17"],
"tests": [{"stdlibs": ["libstdc++"], "tests": ["Release.Default"]}]
}
]
Expand All @@ -105,7 +113,7 @@ jobs:
"appleclang": [
{ "versions": ["latest"],
"tests": [
{ "cxxversions": ["c++26", "c++23", "c++20"],
{ "cxxversions": ["c++26", "c++23", "c++20", "c++17"],
"tests": [{ "stdlibs": ["libc++"], "tests": ["Release.Default"]}]
}
]
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ You can disable building tests by setting CMake option `BEMAN_CSTRING_VIEW_BUILD

| Compiler | Version | C++ Standards | Standard Library |
|------------|---------|---------------|-------------------|
| GCC | 15-14 | C++26-C++20 | libstdc++ |
| Clang | 22-19 | C++26-C++20 | libstdc++, libc++ |
| Clang | 18-17 | C++26-C++20 | libc++ |
| Clang | 18-17 | C++20 | libstdc++ |
| AppleClang | latest | C++26-C++20 | libc++ |
| GCC | 15-13 | C++26-C++17 | libstdc++ |
| GCC | 12-11 | C++23-C++17 | libstdc++ |
| Clang | 22-19 | C++26-C++17 | libstdc++, libc++ |
| Clang | 18-17 | C++26-C++17 | libc++ |
| Clang | 18-17 | C++20, C++17 | libstdc++ |
| AppleClang | latest | C++26-C++17 | libc++ |
| MSVC | latest | C++23 | MSVC STL |

## Development
Expand Down
14 changes: 14 additions & 0 deletions examples/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using namespace std::literals;
using namespace beman::literals;

#if __cpp_lib_three_way_comparison
std::string_view to_string(std::strong_ordering order) {
if (order == std::strong_ordering::equal) {
return "equal";
Expand All @@ -23,25 +24,30 @@ std::string_view to_string(std::strong_ordering order) {
return "internal error";
}
}
#endif

int main() {
std::string s = "hello world";
beman::cstring_view z0 = "hello";
beman::cstring_view z1 = s;
beman::cstring_view empty;
std::cout << z0 << "\n";
#if __cpp_lib_starts_ends_with >= 201711L
std::cout << s.starts_with(z0) << "\n";
std::cout << z0.starts_with(s) << "\n";
std::cout << z0.starts_with("hello") << "\n";
std::cout << z0.starts_with("hello"_csv) << "\n";
#endif
std::cout << std::hash<beman::cstring_view>{}(z1) << "\n";
std::cout << z1 << std::endl;
std::cout << ("hello"_csv == "hello"sv) << "\n";
std::cout << ("hello"_csv == "hello"_csv) << "\n";
std::cout << ("hello"_csv != "goodbye"sv) << "\n";
std::cout << ("hello"_csv != "goodbye"_csv) << "\n";
std::cout << (z0 == z1) << "\n";
#if __cpp_lib_three_way_comparison
std::cout << to_string(z0 <=> z1) << "\n";
#endif
std::cout << z0[z0.size()] * 1 << "\n";
std::cout << z0.c_str() << "\n";
std::cout << "\"" << empty << "\"\n";
Expand All @@ -51,21 +57,29 @@ int main() {
beman::wcstring_view wz0 = L"hello";
beman::wcstring_view wz1 = ws;
beman::wcstring_view wempty;
#if __cpp_lib_format >= 201907L
std::wcout << std::format(L"{}\n", wz0);
#endif
#if __cpp_lib_starts_ends_with >= 201711L
std::cout << ws.starts_with(wz0) << "\n";
std::cout << wz0.starts_with(ws) << "\n";
std::cout << wz0.starts_with(L"hello") << "\n";
std::cout << wz0.starts_with(L"hello"_csv) << "\n";
#endif
std::cout << std::hash<beman::wcstring_view>{}(wz1) << "\n";
std::wcout << wz1 << std::endl;
std::cout << (L"hello"_csv == L"hello"sv) << "\n";
std::cout << (L"hello"_csv == L"hello"_csv) << "\n";
std::cout << (L"hello"_csv != L"goodbye"sv) << "\n";
std::cout << (L"hello"_csv != L"goodbye"_csv) << "\n";
std::cout << (wz0 == wz1) << "\n";
#if __cpp_lib_three_way_comparison
std::cout << to_string(wz0 <=> wz1) << "\n";
#endif
std::cout << wz0[wz0.size()] * 1 << "\n";
#if __cpp_lib_format >= 201907L
std::wcout << std::format(L"{}\n", wz0.c_str());
std::wcout << std::format(L"\"{}\"\n", wempty);
#endif
std::cout << (wempty == L""_csv) << "\n";
}
112 changes: 97 additions & 15 deletions include/beman/cstring_view/cstring_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,31 @@

#include <cassert>
#include <cstddef>
#include <compare>
#include <format>
#if __has_include(<compare>)
#include <compare>
#endif
#if __has_include(<format>)
#include <format>
#endif
#include <ranges>
#include <stdexcept>
#include <string_view>
#include <string>

namespace beman {

namespace detail {

template <typename T>
struct type_identity {
using type = T;
};

template <typename T>
using type_identity_t = typename type_identity<T>::type;

} // namespace detail

// [cstring.view.template], class template basic_cstring_view
template <class charT, class traits = std::char_traits<charT>>
class basic_cstring_view; // partially freestanding
Expand All @@ -29,21 +45,41 @@ namespace ranges {
*/
// [cstring.view.comparison], non-member comparison functions
template <class charT, class traits>
constexpr bool operator==(basic_cstring_view<charT, traits> x,
std::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;
constexpr bool operator==(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;

#if __cpp_lib_three_way_comparison
template <class charT, class traits>
constexpr auto operator<=>(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;
#else
template <class charT, class traits>
constexpr auto operator<=>(basic_cstring_view<charT, traits> x,
std::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;
constexpr bool operator!=(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;
template <class charT, class traits>
constexpr bool operator<(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;
template <class charT, class traits>
constexpr bool operator>(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;
template <class charT, class traits>
constexpr bool operator<=(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;
template <class charT, class traits>
constexpr bool operator>=(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept;
#endif

// [cstring.view.io], inserters and extractors
template <class charT, class traits>
std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os,
basic_cstring_view<charT, traits> str);

// basic_cstring_view typedef-names
using cstring_view = basic_cstring_view<char>;
using u8cstring_view = basic_cstring_view<char8_t>;
using cstring_view = basic_cstring_view<char>;
#if __cpp_char8_t >= 201811L
using u8cstring_view = basic_cstring_view<char8_t>;
#endif
using u16cstring_view = basic_cstring_view<char16_t>;
using u32cstring_view = basic_cstring_view<char32_t>;
using wcstring_view = basic_cstring_view<wchar_t>;
Expand All @@ -53,8 +89,10 @@ template <class T>
struct hash;
template <>
struct hash<cstring_view>;
#if __cpp_char8_t >= 201811L
template <>
struct hash<u8cstring_view>;
#endif
template <>
struct hash<u16cstring_view>;
template <>
Expand All @@ -76,8 +114,10 @@ inline namespace cstring_view_literals {
#pragma warning(disable : 4455)
#endif
// [cstring.view.literals], suffix for basic_cstring_view literals
constexpr cstring_view operator"" csv(const char* str, size_t len) noexcept;
constexpr u8cstring_view operator"" csv(const char8_t* str, size_t len) noexcept;
constexpr cstring_view operator"" csv(const char* str, size_t len) noexcept;
#if __cpp_char8_t >= 201811L
constexpr u8cstring_view operator"" csv(const char8_t* str, size_t len) noexcept;
#endif
constexpr u16cstring_view operator"" csv(const char16_t* str, size_t len) noexcept;
constexpr u32cstring_view operator"" csv(const char32_t* str, size_t len) noexcept;
constexpr wcstring_view operator"" csv(const wchar_t* str, size_t len) noexcept;
Expand Down Expand Up @@ -153,7 +193,12 @@ class basic_cstring_view {
}
constexpr const_reference at(size_type pos) const {
if (pos > size_) {
#if __cpp_lib_format >= 201907L
throw std::out_of_range(std::format("basic_cstring_view::at: pos ({}) > size() {}", pos, size_));
#else
throw std::out_of_range(std::string{"basic_cstring_view::at: pos ("} + std::to_string(pos) +
std::string{") > size() "} + std::to_string(size_));
#endif
}
return data_[pos];
}
Expand Down Expand Up @@ -210,6 +255,7 @@ class basic_cstring_view {
return std::basic_string_view<charT, traits>(*this).compare(pos1, n1, s, n2);
}

#if __cpp_lib_starts_ends_with >= 201711L
constexpr bool starts_with(std::basic_string_view<charT, traits> x) const noexcept {
return std::basic_string_view<charT, traits>(*this).starts_with(x);
}
Expand All @@ -228,6 +274,7 @@ class basic_cstring_view {
constexpr bool ends_with(const charT* x) const {
return std::basic_string_view<charT, traits>(*this).ends_with(x);
}
#endif

constexpr bool contains(std::basic_string_view<charT, traits> x) const noexcept {
return std::basic_string_view<charT, traits>(*this).contains(x);
Expand Down Expand Up @@ -321,10 +368,12 @@ class basic_cstring_view {
inline namespace literals {
inline namespace cstring_view_literals {
// [cstring.view.literals], suffix for basic_cstring_view literals
constexpr cstring_view operator""_csv(const char* str, size_t len) noexcept { return basic_cstring_view(str, len); }
constexpr cstring_view operator""_csv(const char* str, size_t len) noexcept { return basic_cstring_view(str, len); }
#if __cpp_char8_t >= 201811L
constexpr u8cstring_view operator""_csv(const char8_t* str, size_t len) noexcept {
return basic_cstring_view(str, len);
}
#endif
constexpr u16cstring_view operator""_csv(const char16_t* str, size_t len) noexcept {
return basic_cstring_view(str, len);
}
Expand All @@ -338,16 +387,45 @@ constexpr wcstring_view operator""_csv(const wchar_t* str, size_t len) noexcept
} // namespace literals

template <class charT, class traits>
constexpr bool operator==(basic_cstring_view<charT, traits> x,
std::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
constexpr bool operator==(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
return std::basic_string_view<charT, traits>(x) == std::basic_string_view<charT, traits>(y);
}

#if __cpp_lib_three_way_comparison
template <class charT, class traits>
constexpr auto operator<=>(basic_cstring_view<charT, traits> x,
std::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
constexpr auto operator<=>(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
return std::basic_string_view<charT, traits>(x) <=> std::basic_string_view<charT, traits>(y);
}
#else
template <class charT, class traits>
constexpr bool operator!=(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
return std::basic_string_view<charT, traits>(x) != std::basic_string_view<charT, traits>(y);
}
// Definitions
template <class charT, class traits>
constexpr bool operator<(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
return std::basic_string_view<charT, traits>(x) < std::basic_string_view<charT, traits>(y);
}
template <class charT, class traits>
constexpr bool operator>(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
return std::basic_string_view<charT, traits>(x) > std::basic_string_view<charT, traits>(y);
}
template <class charT, class traits>
constexpr bool operator<=(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
return std::basic_string_view<charT, traits>(x) <= std::basic_string_view<charT, traits>(y);
}
template <class charT, class traits>
constexpr bool operator>=(basic_cstring_view<charT, traits> x,
detail::type_identity_t<basic_cstring_view<charT, traits>> y) noexcept {
return std::basic_string_view<charT, traits>(x) >= std::basic_string_view<charT, traits>(y);
}
#endif

template <class charT, class traits>
std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os,
Expand All @@ -357,6 +435,7 @@ std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>&

} // namespace beman

#if __cpp_lib_format >= 201907L
// [format.formatter.spec]
template <class charT, class traits>
struct std::formatter<beman::basic_cstring_view<charT, traits>, charT> {
Expand All @@ -370,15 +449,18 @@ struct std::formatter<beman::basic_cstring_view<charT, traits>, charT> {
private:
formatter<std::basic_string_view<charT, traits>, charT> sv_formatter;
};
#endif

template <>
struct std::hash<beman::cstring_view> {
auto operator()(const beman::cstring_view& sv) const noexcept { return std::hash<string_view>{}(sv); }
};
#if __cpp_char8_t >= 201811L
template <>
struct std::hash<beman::u8cstring_view> {
auto operator()(const beman::u8cstring_view& sv) const noexcept { return std::hash<u8string_view>{}(sv); }
};
#endif
template <>
struct std::hash<beman::u16cstring_view> {
auto operator()(const beman::u16cstring_view& sv) const noexcept { return std::hash<u16string_view>{}(sv); }
Expand Down
6 changes: 5 additions & 1 deletion tests/beman/cstring_view/cstring_view.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@ TEST(StringView, ConstructionDestruction) {
EXPECT_EQ(h1.c_str(), h2.c_str());
EXPECT_NE(h1.c_str(), s.c_str());
EXPECT_TRUE(h1 == h2);
EXPECT_TRUE(s == h1);
EXPECT_TRUE(h1 == s);
#if __cpp_lib_starts_ends_with >= 201711L
EXPECT_TRUE(h1.starts_with("he"));
EXPECT_TRUE(h1.ends_with("lo"));
#endif
EXPECT_TRUE(h1 == "hello");
EXPECT_TRUE(h1 == "hello"sv);
#if __cpp_lib_three_way_comparison
EXPECT_EQ(h1 <=> h1, std::strong_ordering::equal);
#endif
EXPECT_EQ(h1[0], 'h');
auto first = h1.substr(0, 2);
auto end = h1.substr(2);
Expand Down
Loading