static-string.h 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // This is a minimal compile-time string handling library (currently just handling concatenation)
  2. // which owes much to Andrzej Krzemieński, and his blog post:
  3. //
  4. // https://akrzemi1.wordpress.com/2017/06/28/compile-time-string-concatenation/
  5. //
  6. // However, the approach is not exactly the same and is perhaps a tiny bit simpler.
  7. //
  8. // Provided are two templates: static_string<N> and array_string<N>. The first is a direct wrapper
  9. // around character string literals and instances can be created using the "literal" function:
  10. //
  11. // constexpr auto str = literal("this will result in a static_string");
  12. //
  13. // The array_string<N> type is similar but can be produced as the result of concatenation of other
  14. // compile-time strings:
  15. //
  16. // constexpr auto str = literal("one") + literal("two"); // array_string containing "onetwo"
  17. // constexpr auto str3 = literal("one") + "two"; // same
  18. //
  19. // To use the string at run time, call the c_str() function to obtain a pointer to the contained
  20. // string, or simply assign or cast to a 'const char *':
  21. //
  22. // const char * cstr = str3; // now points to "onetwo"
  23. namespace cts {
  24. // A static string, designed as a wrapper around string literals:
  25. template <int N>
  26. class static_string
  27. {
  28. const char (&lit)[N+1];
  29. public:
  30. constexpr static_string(const char (&lit_p)[N+1]) : lit(lit_p) {}
  31. constexpr char operator[](int i) const { return lit[i]; }
  32. constexpr const char * c_str() const { return lit; }
  33. operator const char*() const
  34. {
  35. return c_str();
  36. }
  37. constexpr static int length = N;
  38. };
  39. template <int N>
  40. constexpr static_string<N-1> literal(const char (&lit)[N])
  41. {
  42. return static_string<N-1>(lit);
  43. }
  44. // A sequence of integers, designed to look like:
  45. // sequence<0,1,2,3,...>:
  46. template <int ...N>
  47. class sequence { };
  48. // A utility to append one element to a sequence:
  49. template <class S>
  50. class extend_sequence;
  51. template <int ...N>
  52. class extend_sequence<sequence<N...>>
  53. {
  54. public:
  55. using t = sequence<N..., sizeof...(N)>;
  56. };
  57. // A utility to construct a sequence from 0 .. N:
  58. template <int N>
  59. class make_sequence;
  60. template <>
  61. class make_sequence<0>
  62. {
  63. public:
  64. using t = sequence<>;
  65. };
  66. template <int N>
  67. class make_sequence
  68. {
  69. static_assert(N >= 0, "N must be >= 0");
  70. public:
  71. using t = typename extend_sequence<typename make_sequence<N - 1>::t>::t;
  72. };
  73. // forward declaration:
  74. // extract a single character by index from the join of two strings
  75. template <typename S1, typename S2>
  76. constexpr char joined_index(const S1 &s1, const S2 &s2, int i);
  77. // get the length of a compile-time static string
  78. template <typename T>
  79. struct static_length_t
  80. {
  81. constexpr static int len = T::length;
  82. };
  83. template <int N>
  84. struct static_length_t<char [N]>
  85. {
  86. constexpr static int len = N - 1;
  87. };
  88. template <typename T>
  89. constexpr int static_length()
  90. {
  91. return static_length_t<T>::len;
  92. }
  93. // A compile-time string
  94. template <int N>
  95. class array_string
  96. {
  97. const char arr[N+1];
  98. template <typename S1, typename S2, int... S> constexpr
  99. array_string(const S1 &s1, const S2 &s2, const sequence<S...> seq)
  100. : arr{joined_index(s1, s2, S)...}
  101. {}
  102. template <int... S> constexpr
  103. array_string(const static_string<N> &src, const sequence<S...> seq)
  104. : arr{src.c_str()[S]...}
  105. {}
  106. public:
  107. // construct array_string from static_string
  108. constexpr array_string(const static_string<N> &src)
  109. : array_string(src, typename make_sequence<N>::t {})
  110. {}
  111. template <typename S1, typename S2>
  112. constexpr array_string(const S1 &a, const S2 &b)
  113. : array_string(a, b, typename make_sequence<static_length<S1>() + static_length<S2>()>::t {})
  114. {}
  115. static constexpr int length = N;
  116. constexpr char operator[](int i) const
  117. {
  118. return arr[i];
  119. }
  120. const char *c_str() const { return arr; }
  121. operator const char*() const
  122. {
  123. return c_str();
  124. }
  125. };
  126. template <typename S1, typename S2>
  127. constexpr char joined_index(const S1 &s1, const S2 &s2, int i)
  128. {
  129. return (i < S1::length) ? s1[i] : s2[i - S1::length];
  130. }
  131. // Allow concatenating array_string and static_string with any compile-time constant string
  132. // (including character string literal):
  133. template <int N, typename S2> constexpr
  134. array_string<N+static_length<S2>()> operator+(const array_string<N> &s1, const S2 &s2)
  135. {
  136. return array_string<N+static_length<S2>()>(s1, s2);
  137. }
  138. template <int N, typename S2> constexpr
  139. array_string<N+static_length<S2>()> operator+(const static_string<N> &s1, const S2 &s2)
  140. {
  141. return array_string<N+static_length<S2>()>(s1, s2);
  142. }
  143. } // end "cts" namespace