StringView constexpr. (#351)

This commit is contained in:
Branimir Karadžić
2025-11-22 18:41:54 -08:00
committed by GitHub
parent 0e221f9844
commit 808aa150f8
8 changed files with 281 additions and 138 deletions

View File

@@ -171,6 +171,13 @@ namespace bx
return __builtin_bit_cast(Ty, _from);
}
template<typename Ty, typename FromT>
inline constexpr bool narrowCastTest(Ty* _out, const FromT& _from)
{
*_out = static_cast<Ty>(_from);
return static_cast<FromT>(*_out) == _from;
}
template<typename Ty, typename FromT>
inline Ty narrowCast(const FromT& _from, Location _location)
{

View File

@@ -44,8 +44,8 @@ namespace bx
}
template<int32_t SizeT>
inline constexpr StringLiteral::StringLiteral(const char (&str)[SizeT])
: m_ptr(str)
inline constexpr StringLiteral::StringLiteral(const char (&_str)[SizeT])
: m_ptr(_str)
, m_len(SizeT - 1)
{
BX_ASSERT('\0' == m_ptr[SizeT - 1], "Must be 0 terminated.");
@@ -61,20 +61,21 @@ namespace bx
return m_ptr;
}
inline void StringLiteral::clear()
inline constexpr void StringLiteral::clear()
{
m_ptr = "";
m_len = 0;
}
inline bool StringLiteral::isEmpty() const
inline constexpr bool StringLiteral::isEmpty() const
{
return 0 == m_len;
}
inline StringView::StringView()
inline constexpr StringView::StringView()
{
clear();
m_ptr = "";
m_len = 0;
}
inline constexpr StringView::StringView(const StringLiteral& _str)
@@ -84,49 +85,49 @@ namespace bx
{
}
inline StringView::StringView(const StringView& _rhs)
inline constexpr StringView::StringView(const StringView& _rhs)
{
set(_rhs);
}
inline StringView::StringView(const StringView& _rhs, int32_t _start, int32_t _len)
inline constexpr StringView::StringView(const StringView& _rhs, int32_t _start, int32_t _len)
{
set(_rhs, _start, _len);
}
inline StringView& StringView::operator=(const char* _rhs)
inline constexpr StringView& StringView::operator=(const char* _rhs)
{
set(_rhs);
return *this;
}
inline StringView& StringView::operator=(const StringView& _rhs)
inline constexpr StringView& StringView::operator=(const StringView& _rhs)
{
set(_rhs);
return *this;
}
inline StringView::StringView(const char* _ptr)
inline constexpr StringView::StringView(const char* _ptr)
{
set(_ptr, INT32_MAX);
}
inline StringView::StringView(const char* _ptr, int32_t _len)
inline constexpr StringView::StringView(const char* _ptr, int32_t _len)
{
set(_ptr, _len);
}
inline StringView::StringView(const char* _ptr, const char* _term)
inline constexpr StringView::StringView(const char* _ptr, const char* _term)
{
set(_ptr, _term);
}
inline void StringView::set(const char* _ptr)
inline constexpr void StringView::set(const char* _ptr)
{
set(_ptr, INT32_MAX);
}
inline void StringView::set(const char* _ptr, int32_t _len)
inline constexpr void StringView::set(const char* _ptr, int32_t _len)
{
clear();
@@ -138,68 +139,99 @@ namespace bx
}
}
inline void StringView::set(const char* _ptr, const char* _term)
inline constexpr void StringView::set(const char* _ptr, const char* _term)
{
set(_ptr, int32_t(_term-_ptr) );
}
inline void StringView::set(const StringView& _str)
inline constexpr void StringView::set(const StringView& _str)
{
set(_str, 0, INT32_MAX);
}
inline void StringView::set(const StringView& _str, int32_t _start, int32_t _len)
inline constexpr void StringView::set(const StringView& _str, int32_t _start, int32_t _len)
{
const int32_t start = min(_start, _str.m_len);
const int32_t len = clamp(_str.m_len - start, 0, min(_len, _str.m_len) );
set(_str.m_ptr + start, len);
}
inline void StringView::clear()
inline constexpr void StringView::clear()
{
m_ptr = "";
m_len = 0;
m_0terminated = true;
}
inline const char* StringView::getPtr() const
inline constexpr const char* StringView::getPtr() const
{
return m_ptr;
}
inline const char* StringView::getTerm() const
inline constexpr const char* StringView::getTerm() const
{
return m_ptr + m_len;
}
inline bool StringView::isEmpty() const
inline constexpr bool StringView::isEmpty() const
{
return 0 == m_len;
}
inline int32_t StringView::getLength() const
inline constexpr int32_t StringView::getLength() const
{
return m_len;
}
inline bool StringView::is0Terminated() const
inline constexpr bool StringView::is0Terminated() const
{
return m_0terminated;
}
inline bool operator==(const StringView& _lhs, const StringView& _rhs)
inline constexpr bool operator==(const StringView& _lhs, const StringView& _rhs)
{
return 0 == strCmp(_lhs, _rhs);
const int32_t len = _lhs.getLength();
if (len != _rhs.getLength() )
{
return false;
}
if (0 == len)
{
return true;
}
const char* lhs = _lhs.getPtr();
const char* rhs = _rhs.getPtr();
if constexpr (!isConstantEvaluated() )
{
// note: comparison of addresses of literals has unspecified value
if (lhs == rhs)
{
return true;
}
}
for (int32_t ii = 0, num = len-1
; ii < num && *lhs == *rhs
; ++ii, ++lhs, ++rhs
)
{
}
return *lhs == *rhs;
}
inline bool overlap(const StringView& _a, const StringView& _b)
inline constexpr bool overlap(const StringView& _a, const StringView& _b)
{
return _a.getTerm() > _b.getPtr()
&& _b.getTerm() > _a.getPtr()
;
}
inline bool contain(const StringView& _a, const StringView& _b)
inline constexpr bool contain(const StringView& _a, const StringView& _b)
{
return _a.getPtr() <= _b.getPtr()
&& _a.getTerm() >= _b.getTerm()
@@ -207,76 +239,76 @@ namespace bx
}
template<uint16_t MaxCapacityT>
inline FixedStringT<MaxCapacityT>::FixedStringT()
inline constexpr FixedStringT<MaxCapacityT>::FixedStringT()
: m_len(0)
{
}
template<uint16_t MaxCapacityT>
inline FixedStringT<MaxCapacityT>::FixedStringT(const char* _str)
inline constexpr FixedStringT<MaxCapacityT>::FixedStringT(const char* _str)
: FixedStringT<MaxCapacityT>()
{
set(_str);
}
template<uint16_t MaxCapacityT>
inline FixedStringT<MaxCapacityT>::FixedStringT(const StringView& _str)
inline constexpr FixedStringT<MaxCapacityT>::FixedStringT(const StringView& _str)
: FixedStringT<MaxCapacityT>()
{
set(_str);
}
template<uint16_t MaxCapacityT>
inline FixedStringT<MaxCapacityT>::~FixedStringT()
inline constexpr FixedStringT<MaxCapacityT>::~FixedStringT()
{
}
template<uint16_t MaxCapacityT>
inline void FixedStringT<MaxCapacityT>::set(const char* _str)
inline constexpr void FixedStringT<MaxCapacityT>::set(const char* _str)
{
set(StringView(_str) );
}
template<uint16_t MaxCapacityT>
inline void FixedStringT<MaxCapacityT>::set(const StringView& _str)
inline constexpr void FixedStringT<MaxCapacityT>::set(const StringView& _str)
{
int32_t copied = strCopy(m_storage, MaxCapacityT, _str);
m_len = copied;
}
template<uint16_t MaxCapacityT>
inline void FixedStringT<MaxCapacityT>::append(const StringView& _str)
inline constexpr void FixedStringT<MaxCapacityT>::append(const StringView& _str)
{
m_len += strCopy(&m_storage[m_len], MaxCapacityT-m_len, _str);
}
template<uint16_t MaxCapacityT>
inline void FixedStringT<MaxCapacityT>::clear()
inline constexpr void FixedStringT<MaxCapacityT>::clear()
{
m_len = 0;
m_storage[0] = '\0';
}
template<uint16_t MaxCapacityT>
inline bool FixedStringT<MaxCapacityT>::isEmpty() const
inline constexpr bool FixedStringT<MaxCapacityT>::isEmpty() const
{
return 0 == m_len;
}
template<uint16_t MaxCapacityT>
inline int32_t FixedStringT<MaxCapacityT>::getLength() const
inline constexpr int32_t FixedStringT<MaxCapacityT>::getLength() const
{
return m_len;
}
template<uint16_t MaxCapacityT>
inline const char* FixedStringT<MaxCapacityT>::getCPtr() const
inline constexpr const char* FixedStringT<MaxCapacityT>::getCPtr() const
{
return m_storage;
}
template<uint16_t MaxCapacityT>
inline FixedStringT<MaxCapacityT>::operator StringView() const
inline constexpr FixedStringT<MaxCapacityT>::operator StringView() const
{
return StringView(m_storage, m_len);
}
@@ -438,11 +470,23 @@ namespace bx
return m_line;
}
inline int32_t strLen(const StringView& _str, int32_t _max)
inline constexpr int32_t strLen(const StringView& _str, int32_t _max)
{
return min(_str.getLength(), _max);
}
inline constexpr int32_t strLen(const char* _str, int32_t _max)
{
if (NULL == _str)
{
return 0;
}
const char* ptr = _str;
for (; 0 < _max && *ptr != '\0'; ++ptr, --_max) {};
return int32_t(ptr - _str);
}
inline bool hasPrefix(const StringView& _str, const StringView& _prefix)
{
const int32_t len = _prefix.getLength();
@@ -479,4 +523,76 @@ namespace bx
return _str;
}
inline bool fromString(int8_t* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
inline bool fromString(uint8_t* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
inline bool fromString(int16_t* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
inline bool fromString(uint16_t* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
inline bool fromString(int32_t* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
inline bool fromString(uint32_t* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
inline bool fromString(long* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
inline bool fromString(unsigned long* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
inline bool fromString(unsigned long long* _out, const StringView& _str)
{
long long tmp;
fromString(&tmp, _str);
return narrowCastTest(_out, tmp);
}
} // namespace bx

View File

@@ -452,9 +452,9 @@
#if defined(__cplusplus)
static_assert(__cplusplus >= BX_LANGUAGE_CPP17, "\n\n"
static_assert(__cplusplus >= BX_LANGUAGE_CPP20, "\n\n"
"\t** IMPORTANT! **\n\n"
"\tC++17 standard support is required to build.\n"
"\tC++20 standard support is required to build.\n"
"\t\n");
// https://releases.llvm.org/

View File

@@ -282,6 +282,14 @@ namespace bx
/// Write C string.
int32_t write(WriterI* _writer, const char* _str, Error* _err);
///
template<typename Ty>
inline int32_t write(WriterI* _writer, const Ty& _value, Error* _err);
///
template<>
int32_t write(WriterI* _writer, const StringView& _str, Error* _err);
/// Write formatted string.
int32_t write(WriterI* _writer, const StringView& _format, va_list _argList, Error* _err);

View File

@@ -32,7 +32,7 @@ namespace bx
/// Construct string literal from C-style string literal.
///
template<int32_t SizeT>
constexpr StringLiteral(const char (&str)[SizeT]);
constexpr StringLiteral(const char (&_str)[SizeT]);
/// Returns string length.
///
@@ -43,11 +43,11 @@ namespace bx
constexpr const char* getCPtr() const;
///
void clear();
constexpr void clear();
/// Returns `true` if string is empty.
///
bool isEmpty() const;
constexpr bool isEmpty() const;
private:
const char* m_ptr;
@@ -61,75 +61,75 @@ namespace bx
public:
/// Construct default/empty string view.
///
StringView();
constexpr StringView();
/// Construct string view from string literal.
///
constexpr StringView(const StringLiteral& _str);
///
StringView(const StringView& _rhs);
constexpr StringView(const StringView& _rhs);
///
StringView(const StringView& _rhs, int32_t _start, int32_t _len);
constexpr StringView(const StringView& _rhs, int32_t _start, int32_t _len);
///
StringView& operator=(const char* _rhs);
constexpr StringView& operator=(const char* _rhs);
///
StringView& operator=(const StringView& _rhs);
constexpr StringView& operator=(const StringView& _rhs);
///
StringView(const char* _ptr);
constexpr StringView(const char* _ptr);
///
StringView(const char* _ptr, int32_t _len);
constexpr StringView(const char* _ptr, int32_t _len);
///
StringView(const char* _ptr, const char* _term);
constexpr StringView(const char* _ptr, const char* _term);
///
void set(const char* _ptr);
constexpr void set(const char* _ptr);
///
void set(const char* _ptr, int32_t _len);
constexpr void set(const char* _ptr, int32_t _len);
///
void set(const char* _ptr, const char* _term);
constexpr void set(const char* _ptr, const char* _term);
///
void set(const StringView& _str);
constexpr void set(const StringView& _str);
///
void set(const StringView& _str, int32_t _start, int32_t _len);
constexpr void set(const StringView& _str, int32_t _start, int32_t _len);
///
void clear();
constexpr void clear();
/// Returns pointer to non-terminated string.
///
/// @attention Use of this pointer in standard C/C++ functions is not safe. You must use it
/// in conjunction with `getTerm()` or getLength()`.
///
const char* getPtr() const;
constexpr const char* getPtr() const;
/// Returns pointer past last character in string view.
///
/// @attention Dereferencing this pointer is not safe.
///
const char* getTerm() const;
constexpr const char* getTerm() const;
/// Returns `true` if string is empty.
///
bool isEmpty() const;
constexpr bool isEmpty() const;
/// Returns string length.
///
int32_t getLength() const;
constexpr int32_t getLength() const;
/// Returns `true` if string is zero terminated.
///
bool is0Terminated() const;
constexpr bool is0Terminated() const;
protected:
const char* m_ptr;
@@ -138,13 +138,13 @@ namespace bx
};
/// Compare two string views.
bool operator==(const StringView& _lhs, const StringView& _rhs);
constexpr bool operator==(const StringView& _lhs, const StringView& _rhs);
/// Returns true if two string views overlap.
bool overlap(const StringView& _a, const StringView& _b);
constexpr bool overlap(const StringView& _a, const StringView& _b);
/// Returns true if string view `_a` contains string view `_b`.
bool contain(const StringView& _a, const StringView& _b);
constexpr bool contain(const StringView& _a, const StringView& _b);
/// Fixed capacity string.
///
@@ -153,44 +153,44 @@ namespace bx
{
public:
///
FixedStringT();
constexpr FixedStringT();
///
FixedStringT(const char* _str);
constexpr FixedStringT(const char* _str);
///
FixedStringT(const StringView& _str);
constexpr FixedStringT(const StringView& _str);
///
~FixedStringT();
constexpr ~FixedStringT();
///
void set(const char* _str);
constexpr void set(const char* _str);
///
void set(const StringView& _str);
constexpr void set(const StringView& _str);
///
void append(const StringView& _str);
constexpr void append(const StringView& _str);
///
void clear();
constexpr void clear();
/// Returns `true` if string is empty.
///
bool isEmpty() const;
constexpr bool isEmpty() const;
/// Returns string length.
///
int32_t getLength() const;
constexpr int32_t getLength() const;
/// Returns zero-terminated C string pointer.
///
const char* getCPtr() const;
constexpr const char* getCPtr() const;
/// Implicitly converts FixedStringT to StringView.
///
operator StringView() const;
constexpr operator StringView() const;
private:
char m_storage[MaxCapacityT];
@@ -342,10 +342,10 @@ namespace bx
int32_t strCmpV(const StringView& _lhs, const StringView& _rhs, int32_t _max = INT32_MAX);
/// Get string length.
int32_t strLen(const char* _str, int32_t _max = INT32_MAX);
constexpr int32_t strLen(const char* _str, int32_t _max = INT32_MAX);
/// Get string length.
int32_t strLen(const StringView& _str, int32_t _max = INT32_MAX);
constexpr int32_t strLen(const StringView& _str, int32_t _max = INT32_MAX);
/// Copy _num characters from string _src to _dst buffer of maximum _dstSize capacity
/// including zero terminator. Copy will be terminated with '\0'.
@@ -480,12 +480,36 @@ namespace bx
/// Converts string to double value.
bool fromString(double* _out, const StringView& _str);
/// Converts string to 8-bit integer value.
bool fromString(int8_t* _out, const StringView& _str);
/// Converts string to 8-bit unsigned integer value.
bool fromString(uint8_t* _out, const StringView& _str);
/// Converts string to 8-bit integer value.
bool fromString(int16_t* _out, const StringView& _str);
/// Converts string to 8-bit unsigned integer value.
bool fromString(uint16_t* _out, const StringView& _str);
/// Converts string to 32-bit integer value.
bool fromString(int32_t* _out, const StringView& _str);
/// Converts string to 32-bit unsigned integer value.
bool fromString(uint32_t* _out, const StringView& _str);
/// Converts string to
bool fromString(long* _out, const StringView& _str);
/// Converts string to
bool fromString(unsigned long* _out, const StringView& _str);
/// Converts string to 64-bit long long value.
bool fromString(long long* _out, const StringView& _str);
/// Converts string to 64-bit unsigned long long value.
bool fromString(unsigned long long* _out, const StringView& _str);
///
class LineReader
{

View File

@@ -1093,7 +1093,7 @@ namespace bx
return true;
}
bool fromString(int32_t* _out, const StringView& _str)
bool fromString(long long* _out, const StringView& _str)
{
StringView str = strLTrimSpace(_str);
@@ -1113,7 +1113,7 @@ namespace bx
break;
}
int32_t result = 0;
long long result = 0;
for (ch = *ptr++; isNumeric(ch) && ptr <= term; ch = *ptr++)
{
@@ -1125,10 +1125,4 @@ namespace bx
return true;
}
bool fromString(uint32_t* _out, const StringView& _str)
{
fromString( (int32_t*)_out, _str);
return true;
}
} // namespace bx

View File

@@ -165,7 +165,7 @@ namespace bx
typedef char (*CharFn)(char _ch);
inline char toNoop(char _ch)
inline constexpr char toNoop(char _ch)
{
return _ch;
}
@@ -290,18 +290,6 @@ namespace bx
);
}
int32_t strLen(const char* _str, int32_t _max)
{
if (NULL == _str)
{
return 0;
}
const char* ptr = _str;
for (; 0 < _max && *ptr != '\0'; ++ptr, --_max) {};
return int32_t(ptr - _str);
}
inline int32_t strCopy(char* _dst, int32_t _dstSize, const char* _src, int32_t _num)
{
BX_ASSERT(NULL != _dst, "_dst can't be NULL!");

View File

@@ -13,41 +13,6 @@
bx::AllocatorI* g_allocator;
TEST_CASE("StringLiteral", "[string]")
{
constexpr bx::StringLiteral tmp[] = { "1389", "abvgd", "mac", "pod" };
REQUIRE(bx::isSorted(tmp, BX_COUNTOF(tmp) ) );
STATIC_REQUIRE(4 == tmp[0].getLength() );
REQUIRE(4 == bx::strLen(tmp[0]) );
REQUIRE(0 == bx::strCmp("1389", tmp[0]) );
STATIC_REQUIRE(5 == tmp[1].getLength() );
REQUIRE(5 == bx::strLen(tmp[1]) );
REQUIRE(0 == bx::strCmp("abvgd", tmp[1]) );
STATIC_REQUIRE(3 == tmp[2].getLength() );
REQUIRE(3 == bx::strLen(tmp[2]) );
REQUIRE(0 == bx::strCmp("mac", tmp[2]) );
STATIC_REQUIRE(3 == tmp[3].getLength() );
REQUIRE(3 == bx::strLen(tmp[3]) );
REQUIRE(0 == bx::strCmp("pod", tmp[3]) );
constexpr bx::StringLiteral copy(tmp[0]);
STATIC_REQUIRE(4 == copy.getLength() );
REQUIRE(4 == bx::strLen(copy) );
REQUIRE(0 == bx::strCmp("1389", copy) );
constexpr bx::StringView sv(tmp[1]);
REQUIRE(5 == sv.getLength() );
REQUIRE(5 == bx::strLen(sv) );
REQUIRE("abvgd" == sv);
}
TEST_CASE("stringPrintfTy", "[string]")
{
std::string test;
@@ -492,6 +457,47 @@ TEST_CASE("fromString int32_t", "[string]")
REQUIRE(testFromString(-21, "-021") );
}
TEST_CASE("StringLiteral", "[string]")
{
constexpr bx::StringLiteral tmp[] = { "1389", "abvgd", "mac", "pod" };
REQUIRE(bx::isSorted(tmp, BX_COUNTOF(tmp) ) );
STATIC_REQUIRE(4 == tmp[0].getLength() );
STATIC_REQUIRE(4 == bx::strLen(tmp[0]) );
REQUIRE(0 == bx::strCmp("1389", tmp[0]) );
STATIC_REQUIRE(5 == tmp[1].getLength() );
STATIC_REQUIRE(5 == bx::strLen(tmp[1]) );
REQUIRE(0 == bx::strCmp("abvgd", tmp[1]) );
STATIC_REQUIRE(3 == tmp[2].getLength() );
STATIC_REQUIRE(3 == bx::strLen(tmp[2]) );
REQUIRE(0 == bx::strCmp("mac", tmp[2]) );
STATIC_REQUIRE(3 == tmp[3].getLength() );
STATIC_REQUIRE(3 == bx::strLen(tmp[3]) );
REQUIRE(0 == bx::strCmp("pod", tmp[3]) );
constexpr bx::StringLiteral copy(tmp[0]);
STATIC_REQUIRE(4 == copy.getLength() );
REQUIRE(4 == bx::strLen(copy) );
REQUIRE(0 == bx::strCmp("1389", copy) );
constexpr bx::StringView sv(tmp[1]);
STATIC_REQUIRE(5 == sv.getLength() );
STATIC_REQUIRE(5 == bx::strLen(sv) );
STATIC_REQUIRE("abvgd" == sv);
}
TEST_CASE("StringView constexpr", "[string]")
{
constexpr bx::StringView sv("1389");
STATIC_REQUIRE(sv == "1389");
STATIC_REQUIRE(4 == bx::strLen(sv) );
}
TEST_CASE("StringView", "[string]")
{
bx::StringView sv("test");