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. {
  16. // Forward
  17. class F16;
  18. /// @addtogroup util_private
  19. /// @{
  20. namespace detail
  21. {
  22. template<typename TNumber>
  23. constexpr const char* toStringFormat()
  24. {
  25. return nullptr;
  26. }
  27. #define ANKI_DEPLOY_TO_STRING(type_, string_) \
  28. template<> \
  29. constexpr const char* toStringFormat<type_>() \
  30. { \
  31. return string_; \
  32. }
  33. ANKI_DEPLOY_TO_STRING(I8, "%" PRId8)
  34. ANKI_DEPLOY_TO_STRING(I16, "%" PRId16)
  35. ANKI_DEPLOY_TO_STRING(I32, "%" PRId32)
  36. ANKI_DEPLOY_TO_STRING(I64, "%" PRId64)
  37. ANKI_DEPLOY_TO_STRING(U8, "%" PRIu8)
  38. ANKI_DEPLOY_TO_STRING(U16, "%" PRIu16)
  39. ANKI_DEPLOY_TO_STRING(U32, "%" PRIu32)
  40. ANKI_DEPLOY_TO_STRING(U64, "%" PRIu64)
  41. ANKI_DEPLOY_TO_STRING(F32, "%f")
  42. ANKI_DEPLOY_TO_STRING(F64, "%f")
  43. #undef ANKI_DEPLOY_TO_STRING
  44. } // end namespace detail
  45. /// @}
  46. /// @addtogroup util_containers
  47. /// @{
  48. /// A wrapper on top of C strings. Used mainly for safety.
  49. class CString
  50. {
  51. public:
  52. using Char = char;
  53. static constexpr PtrSize NPOS = MAX_PTR_SIZE;
  54. CString() = default;
  55. CString(const Char* ptr)
  56. : m_ptr(ptr)
  57. {
  58. }
  59. /// Copy constructor.
  60. CString(const CString& b)
  61. : m_ptr(b.m_ptr)
  62. {
  63. }
  64. /// Copy.
  65. CString& operator=(const CString& b)
  66. {
  67. m_ptr = b.m_ptr;
  68. return *this;
  69. }
  70. /// Return true if the string is initialized.
  71. explicit operator Bool() const
  72. {
  73. return !isEmpty();
  74. }
  75. /// Return char at the specified position.
  76. template<typename T>
  77. const Char& operator[](T pos) const
  78. {
  79. checkInit();
  80. ANKI_ASSERT(pos >= 0 && U32(pos) <= getLength());
  81. return m_ptr[pos];
  82. }
  83. Bool operator==(const CString& b) const
  84. {
  85. if(m_ptr == nullptr || b.m_ptr == nullptr)
  86. {
  87. return m_ptr == b.m_ptr;
  88. }
  89. else
  90. {
  91. return std::strcmp(m_ptr, b.m_ptr) == 0;
  92. }
  93. }
  94. Bool operator!=(const CString& b) const
  95. {
  96. return !((*this) == b);
  97. }
  98. Bool operator<(const CString& b) const
  99. {
  100. if(m_ptr == nullptr || b.m_ptr == nullptr)
  101. {
  102. return false;
  103. }
  104. else
  105. {
  106. return std::strcmp(m_ptr, b.m_ptr) < 0;
  107. }
  108. }
  109. Bool operator<=(const CString& b) const
  110. {
  111. if(m_ptr == nullptr || b.m_ptr == nullptr)
  112. {
  113. return m_ptr == b.m_ptr;
  114. }
  115. else
  116. {
  117. return std::strcmp(m_ptr, b.m_ptr) <= 0;
  118. }
  119. }
  120. Bool operator>(const CString& b) const
  121. {
  122. if(m_ptr == nullptr || b.m_ptr == nullptr)
  123. {
  124. return false;
  125. }
  126. else
  127. {
  128. return std::strcmp(m_ptr, b.m_ptr) > 0;
  129. }
  130. }
  131. Bool operator>=(const CString& b) const
  132. {
  133. if(m_ptr == nullptr || b.m_ptr == nullptr)
  134. {
  135. return m_ptr == b.m_ptr;
  136. }
  137. else
  138. {
  139. return std::strcmp(m_ptr, b.m_ptr) >= 0;
  140. }
  141. }
  142. /// Get C-string.
  143. const Char* cstr() const
  144. {
  145. checkInit();
  146. return m_ptr;
  147. }
  148. const Char* getBegin() const
  149. {
  150. checkInit();
  151. return &m_ptr[0];
  152. }
  153. const Char* getEnd() const
  154. {
  155. checkInit();
  156. return &m_ptr[getLength()];
  157. }
  158. const Char* begin() const
  159. {
  160. return getBegin();
  161. }
  162. const Char* end() const
  163. {
  164. return getEnd();
  165. }
  166. /// Return true if the string is not initialized.
  167. Bool isEmpty() const
  168. {
  169. return m_ptr == nullptr || getLength() == 0;
  170. }
  171. /// Get the string length.
  172. U32 getLength() const
  173. {
  174. return (m_ptr == nullptr) ? 0 : U32(std::strlen(m_ptr));
  175. }
  176. PtrSize find(const CString& cstr, PtrSize position = 0) const
  177. {
  178. checkInit();
  179. ANKI_ASSERT(position < getLength());
  180. const Char* out = std::strstr(m_ptr + position, &cstr[0]);
  181. return (out == nullptr) ? NPOS : PtrSize(out - m_ptr);
  182. }
  183. /// Convert to F16.
  184. ANKI_USE_RESULT Error toNumber(F16& out) const;
  185. /// Convert to F32.
  186. ANKI_USE_RESULT Error toNumber(F32& out) const;
  187. /// Convert to F64.
  188. ANKI_USE_RESULT Error toNumber(F64& out) const;
  189. /// Convert to I8.
  190. ANKI_USE_RESULT Error toNumber(I8& out) const;
  191. /// Convert to U8.
  192. ANKI_USE_RESULT Error toNumber(U8& out) const;
  193. /// Convert to I16.
  194. ANKI_USE_RESULT Error toNumber(I16& out) const;
  195. /// Convert to U16.
  196. ANKI_USE_RESULT Error toNumber(U16& out) const;
  197. /// Convert to I32.
  198. ANKI_USE_RESULT Error toNumber(I32& out) const;
  199. /// Convert to U32.
  200. ANKI_USE_RESULT Error toNumber(U32& out) const;
  201. /// Convert to I64.
  202. ANKI_USE_RESULT Error toNumber(I64& out) const;
  203. /// Convert to U64.
  204. ANKI_USE_RESULT Error toNumber(U64& out) const;
  205. /// Convert to Bool.
  206. ANKI_USE_RESULT Error toNumber(Bool& out) const;
  207. /// Compute the hash.
  208. U64 computeHash() const
  209. {
  210. checkInit();
  211. return anki::computeHash(m_ptr, getLength());
  212. }
  213. private:
  214. const Char* m_ptr = nullptr;
  215. void checkInit() const
  216. {
  217. ANKI_ASSERT(m_ptr != nullptr);
  218. }
  219. };
  220. /// Compare function for CStrings. Can be used in HashMap.
  221. class CStringCompare
  222. {
  223. public:
  224. Bool operator()(CString a, CString b)
  225. {
  226. return a == b;
  227. }
  228. };
  229. /// The base class for strings.
  230. class String
  231. {
  232. public:
  233. using Char = char; ///< Character type
  234. using CStringType = CString;
  235. using Iterator = Char*;
  236. using ConstIterator = const Char*;
  237. using Allocator = GenericMemoryPoolAllocator<Char>;
  238. static const PtrSize NPOS = MAX_PTR_SIZE;
  239. /// Default constructor.
  240. String()
  241. {
  242. }
  243. /// Move constructor.
  244. String(String&& b)
  245. {
  246. *this = std::move(b);
  247. }
  248. String(StringAuto&& b)
  249. {
  250. *this = std::move(b);
  251. }
  252. String(Allocator alloc, CString str)
  253. {
  254. create(alloc, str);
  255. }
  256. String(const String&) = delete; // Non-copyable
  257. /// Requires manual destruction.
  258. ~String()
  259. {
  260. }
  261. String& operator=(const String&) = delete; // Non-copyable
  262. /// Move one string to this one.
  263. String& operator=(String&& b)
  264. {
  265. move(b);
  266. return *this;
  267. }
  268. /// Move a StringAuto to this one.
  269. String& operator=(StringAuto&& b);
  270. /// Return char at the specified position.
  271. template<typename TInt>
  272. const Char& operator[](TInt pos) const
  273. {
  274. checkInit();
  275. return m_data[pos];
  276. }
  277. /// Return char at the specified position as a modifiable reference.
  278. template<typename TInt>
  279. Char& operator[](TInt pos)
  280. {
  281. checkInit();
  282. return m_data[pos];
  283. }
  284. explicit operator Bool() const
  285. {
  286. return !isEmpty();
  287. }
  288. /// Return true if strings are equal
  289. Bool operator==(const String& b) const
  290. {
  291. checkInit();
  292. b.checkInit();
  293. return std::strcmp(&m_data[0], &b.m_data[0]) == 0;
  294. }
  295. /// Return true if strings are not equal
  296. Bool operator!=(const String& b) const
  297. {
  298. return !(*this == b);
  299. }
  300. /// Return true if this is less than b
  301. Bool operator<(const String& b) const
  302. {
  303. checkInit();
  304. b.checkInit();
  305. return std::strcmp(&m_data[0], &b.m_data[0]) < 0;
  306. }
  307. /// Return true if this is less or equal to b
  308. Bool operator<=(const String& b) const
  309. {
  310. checkInit();
  311. b.checkInit();
  312. return std::strcmp(&m_data[0], &b.m_data[0]) <= 0;
  313. }
  314. /// Return true if this is greater than b
  315. Bool operator>(const String& b) const
  316. {
  317. checkInit();
  318. b.checkInit();
  319. return std::strcmp(&m_data[0], &b.m_data[0]) > 0;
  320. }
  321. /// Return true if this is greater or equal to b
  322. Bool operator>=(const String& b) const
  323. {
  324. checkInit();
  325. b.checkInit();
  326. return std::strcmp(&m_data[0], &b.m_data[0]) >= 0;
  327. }
  328. /// Get a C string.
  329. const Char* cstr() const
  330. {
  331. checkInit();
  332. return &m_data[0];
  333. }
  334. operator CString() const
  335. {
  336. return toCString();
  337. }
  338. /// Initialize using a const string.
  339. void create(Allocator alloc, const CStringType& cstr);
  340. /// Initialize using a range. Copies the range of [first, last)
  341. void create(Allocator alloc, ConstIterator first, ConstIterator last);
  342. /// Initialize using a character.
  343. void create(Allocator alloc, Char c, PtrSize length);
  344. /// Copy one string to this one.
  345. void create(Allocator alloc, const String& b)
  346. {
  347. create(alloc, b.toCString());
  348. }
  349. /// Append another string to this one.
  350. String& append(Allocator alloc, const String& b)
  351. {
  352. if(!b.isEmpty())
  353. {
  354. appendInternal(alloc, &b.m_data[0], b.m_data.getSize() - 1);
  355. }
  356. return *this;
  357. }
  358. /// Append a const string to this one.
  359. String& append(Allocator alloc, const CStringType& cstr)
  360. {
  361. if(!cstr.isEmpty())
  362. {
  363. appendInternal(alloc, cstr.cstr(), cstr.getLength());
  364. }
  365. return *this;
  366. }
  367. /// Append using a range. Copies the range of [first, oneAfterLast)
  368. String& append(Allocator alloc, ConstIterator first, ConstIterator oneAfterLast)
  369. {
  370. const PtrSize len = oneAfterLast - first;
  371. appendInternal(alloc, first, len);
  372. return *this;
  373. }
  374. /// Create formated string.
  375. String& sprintf(Allocator alloc, CString fmt, ...);
  376. /// Destroy the string.
  377. void destroy(Allocator alloc)
  378. {
  379. m_data.destroy(alloc);
  380. }
  381. Iterator getBegin()
  382. {
  383. checkInit();
  384. return &m_data[0];
  385. }
  386. ConstIterator getBegin() const
  387. {
  388. checkInit();
  389. return &m_data[0];
  390. }
  391. Iterator getEnd()
  392. {
  393. checkInit();
  394. return &m_data[m_data.getSize() - 1];
  395. }
  396. ConstIterator getEnd() const
  397. {
  398. checkInit();
  399. return &m_data[m_data.getSize() - 1];
  400. }
  401. Iterator begin()
  402. {
  403. return getBegin();
  404. }
  405. ConstIterator begin() const
  406. {
  407. return getBegin();
  408. }
  409. Iterator end()
  410. {
  411. return getEnd();
  412. }
  413. ConstIterator end() const
  414. {
  415. return getEnd();
  416. }
  417. /// Return the string's length. It doesn't count the terminating character.
  418. U32 getLength() const
  419. {
  420. return (m_data.getSize() == 0) ? 0 : U32(std::strlen(&m_data[0]));
  421. }
  422. /// Return the CString.
  423. CStringType toCString() const
  424. {
  425. return (!isEmpty()) ? CStringType(&m_data[0]) : CStringType();
  426. }
  427. /// Return true if it's empty.
  428. Bool isEmpty() const
  429. {
  430. return m_data.isEmpty();
  431. }
  432. /// Find a substring of this string.
  433. /// @param[in] cstr The substring to search.
  434. /// @param position Position of the first character in the string to be considered in the search.
  435. /// @return A valid position if the string is found or NPOS if not found.
  436. PtrSize find(const CStringType& cstr, PtrSize position = 0) const
  437. {
  438. checkInit();
  439. return toCString().find(cstr, position);
  440. }
  441. /// Find a substring of this string.
  442. /// @param[in] str The substring to search.
  443. /// @param position Position of the first character in the string to be considered in the search.
  444. /// @return A valid position if the string is found or NPOS if not found.
  445. PtrSize find(const String& str, PtrSize position) const
  446. {
  447. str.checkInit();
  448. return find(str.toCString(), position);
  449. }
  450. /// Convert a number to a string.
  451. template<typename TNumber>
  452. void toString(Allocator alloc, TNumber number);
  453. /// Convert to F16.
  454. ANKI_USE_RESULT Error toNumber(F16& out) const
  455. {
  456. return toCString().toNumber(out);
  457. }
  458. /// Convert to F32.
  459. ANKI_USE_RESULT Error toNumber(F32& out) const
  460. {
  461. return toCString().toNumber(out);
  462. }
  463. /// Convert to F64.
  464. ANKI_USE_RESULT Error toNumber(F64& out) const
  465. {
  466. return toCString().toNumber(out);
  467. }
  468. /// Convert to I8.
  469. ANKI_USE_RESULT Error toNumber(I8& out) const
  470. {
  471. return toCString().toNumber(out);
  472. }
  473. /// Convert to U8.
  474. ANKI_USE_RESULT Error toNumber(U8& out) const
  475. {
  476. return toCString().toNumber(out);
  477. }
  478. /// Convert to I16.
  479. ANKI_USE_RESULT Error toNumber(I16& out) const
  480. {
  481. return toCString().toNumber(out);
  482. }
  483. /// Convert to U16.
  484. ANKI_USE_RESULT Error toNumber(U16& out) const
  485. {
  486. return toCString().toNumber(out);
  487. }
  488. /// Convert to I32.
  489. ANKI_USE_RESULT Error toNumber(I32& out) const
  490. {
  491. return toCString().toNumber(out);
  492. }
  493. /// Convert to U32.
  494. ANKI_USE_RESULT Error toNumber(U32& out) const
  495. {
  496. return toCString().toNumber(out);
  497. }
  498. /// Convert to I64.
  499. ANKI_USE_RESULT Error toNumber(I64& out) const
  500. {
  501. return toCString().toNumber(out);
  502. }
  503. /// Convert to U64.
  504. ANKI_USE_RESULT Error toNumber(U64& out) const
  505. {
  506. return toCString().toNumber(out);
  507. }
  508. /// Convert to Bool.
  509. ANKI_USE_RESULT Error toNumber(Bool& out) const
  510. {
  511. return toCString().toNumber(out);
  512. }
  513. /// Compute the hash.
  514. U64 computeHash() const
  515. {
  516. checkInit();
  517. return anki::computeHash(&m_data[0], m_data.getSize());
  518. }
  519. /// Replace all occurrences of "from" with "to".
  520. String& replaceAll(Allocator alloc, CString from, CString to);
  521. /// @brief Execute a functor for all characters of the string.
  522. template<typename TFunc>
  523. String& transform(TFunc func)
  524. {
  525. U i = 0;
  526. while(i < m_data.getSize() && m_data[i] != '\0')
  527. {
  528. func(m_data[i]);
  529. ++i;
  530. }
  531. return *this;
  532. }
  533. String& toLower()
  534. {
  535. return transform([](Char& c) { c = Char(tolower(c)); });
  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