String.h 16 KB

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