String.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #pragma once
  6. #include <AnKi/Util/DynamicArray.h>
  7. #include <AnKi/Util/Array.h>
  8. #include <AnKi/Util/Hash.h>
  9. #include <AnKi/Util/Forward.h>
  10. #include <cstring>
  11. #include <cstdio>
  12. #include <cctype>
  13. #include <cinttypes> // For PRId8 etc
  14. namespace anki {
  15. // Forward
  16. class F16;
  17. /// @addtogroup util_private
  18. /// @{
  19. namespace detail {
  20. template<typename TNumber>
  21. constexpr const char* toStringFormat()
  22. {
  23. return nullptr;
  24. }
  25. #define ANKI_DEPLOY_TO_STRING(type_, string_) \
  26. template<> \
  27. constexpr const char* toStringFormat<type_>() \
  28. { \
  29. return string_; \
  30. }
  31. ANKI_DEPLOY_TO_STRING(I8, "%" PRId8)
  32. ANKI_DEPLOY_TO_STRING(I16, "%" PRId16)
  33. ANKI_DEPLOY_TO_STRING(I32, "%" PRId32)
  34. ANKI_DEPLOY_TO_STRING(I64, "%" PRId64)
  35. ANKI_DEPLOY_TO_STRING(U8, "%" PRIu8)
  36. ANKI_DEPLOY_TO_STRING(U16, "%" PRIu16)
  37. ANKI_DEPLOY_TO_STRING(U32, "%" PRIu32)
  38. ANKI_DEPLOY_TO_STRING(U64, "%" PRIu64)
  39. ANKI_DEPLOY_TO_STRING(F32, "%f")
  40. ANKI_DEPLOY_TO_STRING(F64, "%f")
  41. #undef ANKI_DEPLOY_TO_STRING
  42. } // end namespace detail
  43. /// @}
  44. /// @addtogroup util_containers
  45. /// @{
  46. /// A wrapper on top of C strings. Used mainly for safety.
  47. class CString
  48. {
  49. public:
  50. using Char = char;
  51. static constexpr PtrSize NPOS = MAX_PTR_SIZE;
  52. CString() = default;
  53. CString(const Char* ptr)
  54. : m_ptr(ptr)
  55. {
  56. }
  57. /// Copy constructor.
  58. CString(const CString& b)
  59. : m_ptr(b.m_ptr)
  60. {
  61. }
  62. /// Copy.
  63. CString& operator=(const CString& b)
  64. {
  65. m_ptr = b.m_ptr;
  66. return *this;
  67. }
  68. /// Return true if the string is initialized.
  69. explicit operator Bool() const
  70. {
  71. return !isEmpty();
  72. }
  73. /// Return char at the specified position.
  74. template<typename T>
  75. const Char& operator[](T pos) const
  76. {
  77. checkInit();
  78. ANKI_ASSERT(pos >= 0 && U32(pos) <= getLength());
  79. return m_ptr[pos];
  80. }
  81. Bool operator==(const CString& b) const
  82. {
  83. if(m_ptr == nullptr || b.m_ptr == nullptr)
  84. {
  85. return m_ptr == b.m_ptr;
  86. }
  87. else
  88. {
  89. return std::strcmp(m_ptr, b.m_ptr) == 0;
  90. }
  91. }
  92. Bool operator!=(const CString& b) const
  93. {
  94. return !((*this) == b);
  95. }
  96. Bool operator<(const CString& b) const
  97. {
  98. if(m_ptr == nullptr || b.m_ptr == nullptr)
  99. {
  100. return false;
  101. }
  102. else
  103. {
  104. return std::strcmp(m_ptr, b.m_ptr) < 0;
  105. }
  106. }
  107. Bool operator<=(const CString& b) const
  108. {
  109. if(m_ptr == nullptr || b.m_ptr == nullptr)
  110. {
  111. return m_ptr == b.m_ptr;
  112. }
  113. else
  114. {
  115. return std::strcmp(m_ptr, b.m_ptr) <= 0;
  116. }
  117. }
  118. Bool operator>(const CString& b) const
  119. {
  120. if(m_ptr == nullptr || b.m_ptr == nullptr)
  121. {
  122. return false;
  123. }
  124. else
  125. {
  126. return std::strcmp(m_ptr, b.m_ptr) > 0;
  127. }
  128. }
  129. Bool operator>=(const CString& b) const
  130. {
  131. if(m_ptr == nullptr || b.m_ptr == nullptr)
  132. {
  133. return m_ptr == b.m_ptr;
  134. }
  135. else
  136. {
  137. return std::strcmp(m_ptr, b.m_ptr) >= 0;
  138. }
  139. }
  140. /// Get C-string.
  141. const Char* cstr() const
  142. {
  143. checkInit();
  144. return m_ptr;
  145. }
  146. const Char* getBegin() const
  147. {
  148. checkInit();
  149. return &m_ptr[0];
  150. }
  151. const Char* getEnd() const
  152. {
  153. checkInit();
  154. return &m_ptr[getLength()];
  155. }
  156. const Char* begin() const
  157. {
  158. return getBegin();
  159. }
  160. const Char* end() const
  161. {
  162. return getEnd();
  163. }
  164. /// Return true if the string is not initialized.
  165. Bool isEmpty() const
  166. {
  167. return m_ptr == nullptr || getLength() == 0;
  168. }
  169. /// Get the string length.
  170. U32 getLength() const
  171. {
  172. return (m_ptr == nullptr) ? 0 : U32(std::strlen(m_ptr));
  173. }
  174. PtrSize find(const CString& cstr, PtrSize position = 0) const
  175. {
  176. checkInit();
  177. ANKI_ASSERT(position < getLength());
  178. const Char* out = std::strstr(m_ptr + position, &cstr[0]);
  179. return (out == nullptr) ? NPOS : PtrSize(out - m_ptr);
  180. }
  181. /// Convert to F16.
  182. ANKI_USE_RESULT Error toNumber(F16& out) const;
  183. /// Convert to F32.
  184. ANKI_USE_RESULT Error toNumber(F32& out) const;
  185. /// Convert to F64.
  186. ANKI_USE_RESULT Error toNumber(F64& out) const;
  187. /// Convert to I8.
  188. ANKI_USE_RESULT Error toNumber(I8& out) const;
  189. /// Convert to U8.
  190. ANKI_USE_RESULT Error toNumber(U8& out) const;
  191. /// Convert to I16.
  192. ANKI_USE_RESULT Error toNumber(I16& out) const;
  193. /// Convert to U16.
  194. ANKI_USE_RESULT Error toNumber(U16& out) const;
  195. /// Convert to I32.
  196. ANKI_USE_RESULT Error toNumber(I32& out) const;
  197. /// Convert to U32.
  198. ANKI_USE_RESULT Error toNumber(U32& out) const;
  199. /// Convert to I64.
  200. ANKI_USE_RESULT Error toNumber(I64& out) const;
  201. /// Convert to U64.
  202. ANKI_USE_RESULT Error toNumber(U64& out) const;
  203. /// Convert to Bool.
  204. ANKI_USE_RESULT Error toNumber(Bool& out) const;
  205. /// Compute the hash.
  206. U64 computeHash() const
  207. {
  208. checkInit();
  209. return anki::computeHash(m_ptr, getLength());
  210. }
  211. private:
  212. const Char* m_ptr = nullptr;
  213. void checkInit() const
  214. {
  215. ANKI_ASSERT(m_ptr != nullptr);
  216. }
  217. };
  218. /// Compare function for CStrings. Can be used in HashMap.
  219. class CStringCompare
  220. {
  221. public:
  222. Bool operator()(CString a, CString b)
  223. {
  224. return a == b;
  225. }
  226. };
  227. /// The base class for strings.
  228. class String
  229. {
  230. public:
  231. using Char = char; ///< Character type
  232. using CStringType = CString;
  233. using Iterator = Char*;
  234. using ConstIterator = const Char*;
  235. using Allocator = GenericMemoryPoolAllocator<Char>;
  236. static const PtrSize NPOS = MAX_PTR_SIZE;
  237. /// Default constructor.
  238. String()
  239. {
  240. }
  241. /// Move constructor.
  242. String(String&& b)
  243. {
  244. *this = std::move(b);
  245. }
  246. String(StringAuto&& b)
  247. {
  248. *this = std::move(b);
  249. }
  250. String(Allocator alloc, CString str)
  251. {
  252. create(alloc, str);
  253. }
  254. String(const String&) = delete; // Non-copyable
  255. /// Requires manual destruction.
  256. ~String()
  257. {
  258. }
  259. String& operator=(const String&) = delete; // Non-copyable
  260. /// Move one string to this one.
  261. String& operator=(String&& b)
  262. {
  263. move(b);
  264. return *this;
  265. }
  266. /// Move a StringAuto to this one.
  267. String& operator=(StringAuto&& b);
  268. /// Return char at the specified position.
  269. template<typename TInt>
  270. const Char& operator[](TInt pos) const
  271. {
  272. checkInit();
  273. return m_data[pos];
  274. }
  275. /// Return char at the specified position as a modifiable reference.
  276. template<typename TInt>
  277. Char& operator[](TInt pos)
  278. {
  279. checkInit();
  280. return m_data[pos];
  281. }
  282. explicit operator Bool() const
  283. {
  284. return !isEmpty();
  285. }
  286. /// Return true if strings are equal
  287. Bool operator==(const String& b) const
  288. {
  289. checkInit();
  290. b.checkInit();
  291. return std::strcmp(&m_data[0], &b.m_data[0]) == 0;
  292. }
  293. /// Return true if strings are not equal
  294. Bool operator!=(const String& b) const
  295. {
  296. return !(*this == b);
  297. }
  298. /// Return true if this is less than b
  299. Bool operator<(const String& b) const
  300. {
  301. checkInit();
  302. b.checkInit();
  303. return std::strcmp(&m_data[0], &b.m_data[0]) < 0;
  304. }
  305. /// Return true if this is less or equal to b
  306. Bool operator<=(const String& b) const
  307. {
  308. checkInit();
  309. b.checkInit();
  310. return std::strcmp(&m_data[0], &b.m_data[0]) <= 0;
  311. }
  312. /// Return true if this is greater than b
  313. Bool operator>(const String& b) const
  314. {
  315. checkInit();
  316. b.checkInit();
  317. return std::strcmp(&m_data[0], &b.m_data[0]) > 0;
  318. }
  319. /// Return true if this is greater or equal to b
  320. Bool operator>=(const String& b) const
  321. {
  322. checkInit();
  323. b.checkInit();
  324. return std::strcmp(&m_data[0], &b.m_data[0]) >= 0;
  325. }
  326. /// Get a C string.
  327. const Char* cstr() const
  328. {
  329. checkInit();
  330. return &m_data[0];
  331. }
  332. operator CString() const
  333. {
  334. return toCString();
  335. }
  336. /// Initialize using a const string.
  337. void create(Allocator alloc, const CStringType& cstr);
  338. /// Initialize using a range. Copies the range of [first, last)
  339. void create(Allocator alloc, ConstIterator first, ConstIterator last);
  340. /// Initialize using a character.
  341. void create(Allocator alloc, Char c, PtrSize length);
  342. /// Copy one string to this one.
  343. void create(Allocator alloc, const String& b)
  344. {
  345. create(alloc, b.toCString());
  346. }
  347. /// Append another string to this one.
  348. String& append(Allocator alloc, const String& b)
  349. {
  350. if(!b.isEmpty())
  351. {
  352. appendInternal(alloc, &b.m_data[0], b.m_data.getSize() - 1);
  353. }
  354. return *this;
  355. }
  356. /// Append a const string to this one.
  357. String& append(Allocator alloc, const CStringType& cstr)
  358. {
  359. if(!cstr.isEmpty())
  360. {
  361. appendInternal(alloc, cstr.cstr(), cstr.getLength());
  362. }
  363. return *this;
  364. }
  365. /// Append using a range. Copies the range of [first, oneAfterLast)
  366. String& append(Allocator alloc, ConstIterator first, ConstIterator oneAfterLast)
  367. {
  368. const PtrSize len = oneAfterLast - first;
  369. appendInternal(alloc, first, len);
  370. return *this;
  371. }
  372. /// Create formated string.
  373. String& sprintf(Allocator alloc, CString fmt, ...);
  374. /// Destroy the string.
  375. void destroy(Allocator alloc)
  376. {
  377. m_data.destroy(alloc);
  378. }
  379. Iterator getBegin()
  380. {
  381. checkInit();
  382. return &m_data[0];
  383. }
  384. ConstIterator getBegin() const
  385. {
  386. checkInit();
  387. return &m_data[0];
  388. }
  389. Iterator getEnd()
  390. {
  391. checkInit();
  392. return &m_data[m_data.getSize() - 1];
  393. }
  394. ConstIterator getEnd() const
  395. {
  396. checkInit();
  397. return &m_data[m_data.getSize() - 1];
  398. }
  399. Iterator begin()
  400. {
  401. return getBegin();
  402. }
  403. ConstIterator begin() const
  404. {
  405. return getBegin();
  406. }
  407. Iterator end()
  408. {
  409. return getEnd();
  410. }
  411. ConstIterator end() const
  412. {
  413. return getEnd();
  414. }
  415. /// Return the string's length. It doesn't count the terminating character.
  416. U32 getLength() const
  417. {
  418. return (m_data.getSize() == 0) ? 0 : U32(std::strlen(&m_data[0]));
  419. }
  420. /// Return the CString.
  421. CStringType toCString() const
  422. {
  423. return (!isEmpty()) ? CStringType(&m_data[0]) : CStringType();
  424. }
  425. /// Return true if it's empty.
  426. Bool isEmpty() const
  427. {
  428. return m_data.isEmpty();
  429. }
  430. /// Find a substring of this string.
  431. /// @param[in] cstr The substring to search.
  432. /// @param position Position of the first character in the string to be considered in the search.
  433. /// @return A valid position if the string is found or NPOS if not found.
  434. PtrSize find(const CStringType& cstr, PtrSize position = 0) const
  435. {
  436. checkInit();
  437. return toCString().find(cstr, position);
  438. }
  439. /// Find a substring of this string.
  440. /// @param[in] str The substring to search.
  441. /// @param position Position of the first character in the string to be considered in the search.
  442. /// @return A valid position if the string is found or NPOS if not found.
  443. PtrSize find(const String& str, PtrSize position) const
  444. {
  445. str.checkInit();
  446. return find(str.toCString(), position);
  447. }
  448. /// Convert a number to a string.
  449. template<typename TNumber>
  450. void toString(Allocator alloc, TNumber number);
  451. /// Convert to F16.
  452. ANKI_USE_RESULT Error toNumber(F16& out) const
  453. {
  454. return toCString().toNumber(out);
  455. }
  456. /// Convert to F32.
  457. ANKI_USE_RESULT Error toNumber(F32& out) const
  458. {
  459. return toCString().toNumber(out);
  460. }
  461. /// Convert to F64.
  462. ANKI_USE_RESULT Error toNumber(F64& out) const
  463. {
  464. return toCString().toNumber(out);
  465. }
  466. /// Convert to I8.
  467. ANKI_USE_RESULT Error toNumber(I8& out) const
  468. {
  469. return toCString().toNumber(out);
  470. }
  471. /// Convert to U8.
  472. ANKI_USE_RESULT Error toNumber(U8& out) const
  473. {
  474. return toCString().toNumber(out);
  475. }
  476. /// Convert to I16.
  477. ANKI_USE_RESULT Error toNumber(I16& out) const
  478. {
  479. return toCString().toNumber(out);
  480. }
  481. /// Convert to U16.
  482. ANKI_USE_RESULT Error toNumber(U16& out) const
  483. {
  484. return toCString().toNumber(out);
  485. }
  486. /// Convert to I32.
  487. ANKI_USE_RESULT Error toNumber(I32& out) const
  488. {
  489. return toCString().toNumber(out);
  490. }
  491. /// Convert to U32.
  492. ANKI_USE_RESULT Error toNumber(U32& out) const
  493. {
  494. return toCString().toNumber(out);
  495. }
  496. /// Convert to I64.
  497. ANKI_USE_RESULT Error toNumber(I64& out) const
  498. {
  499. return toCString().toNumber(out);
  500. }
  501. /// Convert to U64.
  502. ANKI_USE_RESULT Error toNumber(U64& out) const
  503. {
  504. return toCString().toNumber(out);
  505. }
  506. /// Convert to Bool.
  507. ANKI_USE_RESULT Error toNumber(Bool& out) const
  508. {
  509. return toCString().toNumber(out);
  510. }
  511. /// Compute the hash.
  512. U64 computeHash() const
  513. {
  514. checkInit();
  515. return anki::computeHash(&m_data[0], m_data.getSize());
  516. }
  517. /// Replace all occurrences of "from" with "to".
  518. String& replaceAll(Allocator alloc, CString from, CString to);
  519. /// @brief Execute a functor for all characters of the string.
  520. template<typename TFunc>
  521. String& transform(TFunc func)
  522. {
  523. U i = 0;
  524. while(i < m_data.getSize() && m_data[i] != '\0')
  525. {
  526. func(m_data[i]);
  527. ++i;
  528. }
  529. return *this;
  530. }
  531. String& toLower()
  532. {
  533. return transform([](Char& c) {
  534. c = Char(tolower(c));
  535. });
  536. }
  537. protected:
  538. DynamicArray<Char, PtrSize> m_data;
  539. void checkInit() const
  540. {
  541. ANKI_ASSERT(m_data.getSize() > 0);
  542. }
  543. /// Append to this string.
  544. void appendInternal(Allocator& alloc, const Char* str, PtrSize strLen);
  545. void move(String& b)
  546. {
  547. ANKI_ASSERT(this != &b);
  548. m_data = std::move(b.m_data);
  549. }
  550. };
  551. template<typename TNumber>
  552. inline void String::toString(Allocator alloc, TNumber number)
  553. {
  554. destroy(alloc);
  555. Array<Char, 512> buff;
  556. const I ret = std::snprintf(&buff[0], buff.size(), detail::toStringFormat<TNumber>(), number);
  557. if(ret < 0 || ret > static_cast<I>(buff.getSize()))
  558. {
  559. ANKI_UTIL_LOGF("To small intermediate buffer");
  560. }
  561. else
  562. {
  563. create(alloc, &buff[0]);
  564. }
  565. }
  566. /// String with automatic cleanup.
  567. class StringAuto : public String
  568. {
  569. public:
  570. using Base = String;
  571. using Allocator = typename Base::Allocator;
  572. /// Create with allocator.
  573. StringAuto(Allocator alloc)
  574. : Base()
  575. , m_alloc(alloc)
  576. {
  577. }
  578. /// Copy construcor.
  579. StringAuto(const StringAuto& b)
  580. : Base()
  581. , m_alloc(b.m_alloc)
  582. {
  583. if(!b.isEmpty())
  584. {
  585. create(b.m_data.getBegin(), b.m_data.getEnd());
  586. }
  587. }
  588. /// Move constructor.
  589. StringAuto(StringAuto&& b)
  590. : Base()
  591. {
  592. move(b);
  593. }
  594. /// Create with allocator and data.
  595. StringAuto(Allocator alloc, const CStringType& cstr)
  596. : Base()
  597. , m_alloc(alloc)
  598. {
  599. create(cstr);
  600. }
  601. /// Automatic destruction.
  602. ~StringAuto()
  603. {
  604. Base::destroy(m_alloc);
  605. }
  606. /// Copy operator.
  607. StringAuto& operator=(const StringAuto& b)
  608. {
  609. destroy();
  610. m_alloc = b.m_alloc;
  611. if(!b.isEmpty())
  612. {
  613. create(b.m_data.getBegin(), b.m_data.getEnd());
  614. }
  615. return *this;
  616. }
  617. /// Copy from string.
  618. StringAuto& operator=(const CString& b)
  619. {
  620. destroy();
  621. if(!b.isEmpty())
  622. {
  623. create(b.getBegin(), b.getEnd());
  624. }
  625. return *this;
  626. }
  627. /// Move one string to this one.
  628. StringAuto& operator=(StringAuto&& b)
  629. {
  630. destroy();
  631. move(b);
  632. return *this;
  633. }
  634. GenericMemoryPoolAllocator<Char> getAllocator() const
  635. {
  636. return m_alloc;
  637. }
  638. /// Initialize using a const string.
  639. void create(const CStringType& cstr)
  640. {
  641. Base::create(m_alloc, cstr);
  642. }
  643. /// Initialize using a range. Copies the range of [first, last)
  644. void create(ConstIterator first, ConstIterator last)
  645. {
  646. Base::create(m_alloc, first, last);
  647. }
  648. /// Initialize using a character.
  649. void create(Char c, PtrSize length)
  650. {
  651. Base::create(m_alloc, c, length);
  652. }
  653. /// Copy one string to this one.
  654. void create(const String& b)
  655. {
  656. Base::create(m_alloc, b.toCString());
  657. }
  658. /// Destroy the string.
  659. void destroy()
  660. {
  661. Base::destroy(m_alloc);
  662. }
  663. /// Append another string to this one.
  664. StringAuto& append(const String& b)
  665. {
  666. Base::append(m_alloc, b);
  667. return *this;
  668. }
  669. /// Append a const string to this one.
  670. StringAuto& append(const CStringType& cstr)
  671. {
  672. Base::append(m_alloc, cstr);
  673. return *this;
  674. }
  675. /// Create formated string.
  676. template<typename... TArgs>
  677. StringAuto& sprintf(CString fmt, TArgs... args)
  678. {
  679. Base::sprintf(m_alloc, fmt, args...);
  680. return *this;
  681. }
  682. /// Convert a number to a string.
  683. template<typename TNumber>
  684. void toString(TNumber number)
  685. {
  686. Base::toString(m_alloc, number);
  687. }
  688. /// Replace all occurrences of "from" with "to".
  689. StringAuto& replaceAll(CString from, CString to)
  690. {
  691. Base::replaceAll(m_alloc, from, to);
  692. return *this;
  693. }
  694. private:
  695. GenericMemoryPoolAllocator<Char> m_alloc;
  696. void move(StringAuto& b)
  697. {
  698. Base::move(b);
  699. m_alloc = std::move(b.m_alloc);
  700. }
  701. };
  702. #define ANKI_STRING_COMPARE_OPERATOR(TypeA, TypeB, op) \
  703. inline Bool operator op(TypeA a, TypeB b) \
  704. { \
  705. return CString(a) op CString(b); \
  706. }
  707. #define ANKI_STRING_COMPARE_OPS(TypeA, TypeB) \
  708. ANKI_STRING_COMPARE_OPERATOR(TypeA, TypeB, ==) \
  709. ANKI_STRING_COMPARE_OPERATOR(TypeA, TypeB, !=) \
  710. ANKI_STRING_COMPARE_OPERATOR(TypeA, TypeB, <) \
  711. ANKI_STRING_COMPARE_OPERATOR(TypeA, TypeB, <=) \
  712. ANKI_STRING_COMPARE_OPERATOR(TypeA, TypeB, >) \
  713. ANKI_STRING_COMPARE_OPERATOR(TypeA, TypeB, >=)
  714. ANKI_STRING_COMPARE_OPS(const char*, CString)
  715. ANKI_STRING_COMPARE_OPS(const char*, const String&)
  716. ANKI_STRING_COMPARE_OPS(const char*, const StringAuto&)
  717. ANKI_STRING_COMPARE_OPS(CString, const char*)
  718. ANKI_STRING_COMPARE_OPS(CString, const String&)
  719. ANKI_STRING_COMPARE_OPS(CString, const StringAuto&)
  720. ANKI_STRING_COMPARE_OPS(const String&, const char*)
  721. ANKI_STRING_COMPARE_OPS(const String&, CString)
  722. ANKI_STRING_COMPARE_OPS(const String&, const StringAuto&)
  723. ANKI_STRING_COMPARE_OPS(const StringAuto&, const char*)
  724. ANKI_STRING_COMPARE_OPS(const StringAuto&, CString)
  725. ANKI_STRING_COMPARE_OPS(const StringAuto&, const String&)
  726. ANKI_STRING_COMPARE_OPS(const StringAuto&, const StringAuto&)
  727. #undef ANKI_STRING_COMPARE_OPERATOR
  728. #undef ANKI_STRING_COMPARE_OPS
  729. /// @}
  730. } // end namespace anki