Skip to content

Commit

Permalink
[impl] add interop with std::string_view
Browse files Browse the repository at this point in the history
  • Loading branch information
biojppm committed May 9, 2023
1 parent d78a450 commit 7feda45
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 10 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ set(C4CORE_SRC_FILES
c4/std/std_fwd.hpp
c4/std/string.hpp
c4/std/string_fwd.hpp
c4/std/string_view.hpp
c4/std/tuple.hpp
c4/std/vector.hpp
c4/std/vector_fwd.hpp
Expand Down
8 changes: 8 additions & 0 deletions changelog/current.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ assert(to_substr((char*)ptr).len == 3); // as before
- add simultaneous ctors from `char[]` and `char*`. Using SFINAE to narrow the `char*` overload prevents it from overriding the `char[]` overload. Thanks to @huangqinjin for the idea (see [#97](https://github.com/biojppm/c4core/issues/97)).
- remove unneeded constructors of `csubstr` from non-const chars.
- to each single-argument ctor, add corresponding functions `to_csubstr()` and `to_substr()` to enable clients coercing their types in generic code such as `c4::cat()` and `c4::format()`.
- Add interop with `std::string_view` when the standard is at least C++17 ([#PR101](https://github.com/biojppm/c4core/pulls/101)):
- provided in the header [`c4/std/string_view.hpp`](src/c4/std/string_view.hpp)
- similarly to existing interop headers, this is opt-in and requires explicit inclusion
- implemented:
- `to_csubstr()` (since `std::string_view` is not writeable, cannot provide `to_csubstr()`)
- `to_chars()` (since `std::string_view` is not writeable, cannot provide `from_chars()`)
- comparison operators
- [PR#105](https://github.com/biojppm/c4core/pull/105): Add macros in `c4/language.hpp` for compile-time flow of exceptions:
- `C4_EXCEPTIONS`: defined when exceptions are enabled
- `C4_IF_EXCEPTIONS(code_with_exc, code_without_exc)`: select statements for exceptions enabled/disabled
Expand All @@ -36,6 +43,7 @@ assert(to_substr((char*)ptr).len == 3); // as before
- `C4_IF_RTTI_(code_with_rtti, code_without_rtti)`: select code tokens for rtti enabled/disabled
### Fixes
- [PR#115](https://github.com/biojppm/c4core/pull/115) - Refactor of `c4::blob`/`c4::cblob`. Use SFINAE to invalidate some of the constructors.
Expand Down
1 change: 1 addition & 0 deletions src/c4/std/std.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "c4/std/vector.hpp"
#include "c4/std/string.hpp"
#include "c4/std/string_view.hpp"
#include "c4/std/tuple.hpp"

#endif // _C4_STD_STD_HPP_
71 changes: 71 additions & 0 deletions src/c4/std/string_view.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#ifndef _C4_STD_STRING_VIEW_HPP_
#define _C4_STD_STRING_VIEW_HPP_

/** @file string_view.hpp */

#ifndef C4CORE_SINGLE_HEADER
#include "c4/language.hpp"
#endif

#if (C4_CPP >= 17 && defined(__cpp_lib_string_view))

#ifndef C4CORE_SINGLE_HEADER
#include "c4/substr.hpp"
#endif

#include <string_view>


namespace c4 {

//-----------------------------------------------------------------------------

/** create a csubstr from an existing std::string_view. */
C4_ALWAYS_INLINE c4::csubstr to_csubstr(std::string_view s) noexcept
{
return c4::csubstr(s.data(), s.size());
}


//-----------------------------------------------------------------------------

C4_ALWAYS_INLINE bool operator== (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) == 0; }
C4_ALWAYS_INLINE bool operator!= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) != 0; }
C4_ALWAYS_INLINE bool operator>= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) >= 0; }
C4_ALWAYS_INLINE bool operator> (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) > 0; }
C4_ALWAYS_INLINE bool operator<= (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) <= 0; }
C4_ALWAYS_INLINE bool operator< (c4::csubstr ss, std::string_view s) { return ss.compare(s.data(), s.size()) < 0; }

C4_ALWAYS_INLINE bool operator== (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) == 0; }
C4_ALWAYS_INLINE bool operator!= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) != 0; }
C4_ALWAYS_INLINE bool operator<= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) >= 0; }
C4_ALWAYS_INLINE bool operator< (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) > 0; }
C4_ALWAYS_INLINE bool operator>= (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) <= 0; }
C4_ALWAYS_INLINE bool operator> (std::string_view s, c4::csubstr ss) { return ss.compare(s.data(), s.size()) < 0; }


//-----------------------------------------------------------------------------

/** copy an std::string_view to a writeable substr */
inline size_t to_chars(c4::substr buf, std::string_view s)
{
C4_ASSERT(!buf.overlaps(to_csubstr(s)));
size_t sz = s.size();
size_t len = buf.len < sz ? buf.len : sz;
// calling memcpy with null strings is undefined behavior
// and will wreak havoc in calling code's branches.
// see https://github.com/biojppm/rapidyaml/pull/264#issuecomment-1262133637
if(len)
{
C4_ASSERT(s.data() != nullptr);
C4_ASSERT(buf.str != nullptr);
memcpy(buf.str, s.data(), len);
}
return sz; // return the number of needed chars
}

} // namespace c4

#endif // C4_STRING_VIEW_AVAILABLE

#endif // _C4_STD_STRING_VIEW_HPP_
43 changes: 33 additions & 10 deletions test/test_std_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#ifndef C4CORE_SINGLE_HEADER
#include "c4/std/string_fwd.hpp"
#include "c4/std/string.hpp"
#include "c4/std/string_view.hpp"
#endif

namespace c4 {
Expand All @@ -28,12 +29,26 @@ TEST_CASE("std_string.to_csubstr")
CHECK_EQ(ss[0], 'B');
}

TEST_CASE("std_string.compare_csubstr")
#if (C4_CPP >= 17 && defined(__cpp_lib_string_view))
TEST_CASE("std_string_view.to_csubstr")
{
std::string s0 = "000";
std::string s1 = "111";
csubstr ss0 = "000";
csubstr ss1 = "111";
std::string_view s("barnabe");
csubstr ss = to_csubstr(s);
CHECK_EQ(ss.str, s.data());
CHECK_EQ(ss.len, s.size());
}
#endif

#if (C4_CPP >= 17 && defined(__cpp_lib_string_view))
TEST_CASE_TEMPLATE("std_string.compare_csubstr", T, std::string, std::string_view)
#else
TEST_CASE_TEMPLATE("std_string.compare_csubstr", T, std::string)
#endif
{
T s0 = "000";
T s1 = "111";
csubstr ss0 = csubstr("0001").first(3);
csubstr ss1 = csubstr("1112").first(3);
CHECK_NE(s0.data(), ss0.data());
CHECK_NE(s1.data(), ss1.data());
//
Expand Down Expand Up @@ -62,10 +77,14 @@ TEST_CASE("std_string.compare_csubstr")
CHECK_LT(ss0, s1);
}

TEST_CASE("std_string.compare_substr")
#if (C4_CPP >= 17 && defined(__cpp_lib_string_view))
TEST_CASE_TEMPLATE("std_string.compare_substr", T, std::string, std::string_view)
#else
TEST_CASE_TEMPLATE("std_string.compare_substr", T, std::string)
#endif
{
std::string s0 = "000";
std::string s1 = "111";
T s0 = "000";
T s1 = "111";
char buf0[] = "000";
char buf1[] = "111";
substr ss0 = buf0;
Expand Down Expand Up @@ -98,9 +117,13 @@ TEST_CASE("std_string.compare_substr")
CHECK_LT(ss0, s1);
}

TEST_CASE("std_string.to_chars")
#if (C4_CPP >= 17 && defined(__cpp_lib_string_view))
TEST_CASE_TEMPLATE("std_string.to_chars", T, std::string, std::string_view)
#else
TEST_CASE_TEMPLATE("std_string.to_chars", T, std::string)
#endif
{
const std::string s0 = "000";
const T s0 = "000";
char buf_[100] = {};
substr buf = buf_;
CHECK_NE(buf.data(), s0.data());
Expand Down
1 change: 1 addition & 0 deletions tools/amalgamate.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ def amalgamate_c4core(filename: str,
"src/c4/base64.hpp",
am.onlyif(with_stl, am.ignfile("src/c4/std/std.hpp")), # this is an umbrella include
am.onlyif(with_stl, "src/c4/std/string.hpp"),
am.onlyif(with_stl, "src/c4/std/string_view.hpp",
am.onlyif(with_stl, "src/c4/std/vector.hpp"),
am.onlyif(with_stl, "src/c4/std/tuple.hpp"),
"src/c4/ext/rng/rng.hpp",
Expand Down

0 comments on commit 7feda45

Please sign in to comment.