static-string.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  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. //
  24. // Note that to be most effective, it is necessary to construct literals at file/namespace scope.
  25. // If they are constructed as locals, the compiler may reconstruct the object each time the
  26. // function is called (i.e. it will allocate stack and copy the constant string result into it).
  27. namespace cts {
  28. // A static string, designed as a wrapper around string literals:
  29. template <int N>
  30. class static_string
  31. {
  32. const char (&lit)[N+1];
  33. public:
  34. constexpr static_string(const char (&lit_p)[N+1]) : lit(lit_p) {}
  35. constexpr char operator[](int i) const { return lit[i]; }
  36. constexpr const char * c_str() const { return lit; }
  37. operator const char*() const
  38. {
  39. return c_str();
  40. }
  41. constexpr static int length = N;
  42. };
  43. template <int N>
  44. constexpr static_string<N-1> literal(const char (&lit)[N])
  45. {
  46. return static_string<N-1>(lit);
  47. }
  48. // A sequence of integers, designed to look like:
  49. // sequence<0,1,2,3,...>:
  50. template <int ...N>
  51. class sequence { };
  52. // A utility to append one element to a sequence:
  53. template <class S>
  54. class extend_sequence;
  55. template <int ...N>
  56. class extend_sequence<sequence<N...>>
  57. {
  58. public:
  59. using t = sequence<N..., sizeof...(N)>;
  60. };
  61. // A utility to construct a sequence from 0 .. N:
  62. template <int N>
  63. class make_sequence;
  64. template <>
  65. class make_sequence<0>
  66. {
  67. public:
  68. using t = sequence<>;
  69. };
  70. template <int N>
  71. class make_sequence
  72. {
  73. static_assert(N >= 0, "N must be >= 0");
  74. public:
  75. using t = typename extend_sequence<typename make_sequence<N - 1>::t>::t;
  76. };
  77. // forward declaration:
  78. // extract a single character by index from the join of two strings
  79. template <typename S1, typename S2>
  80. constexpr char joined_index(const S1 &s1, const S2 &s2, int i);
  81. // get the length of a compile-time static string
  82. template <typename T>
  83. struct static_length_t
  84. {
  85. constexpr static int len = T::length;
  86. };
  87. template <int N>
  88. struct static_length_t<char [N]>
  89. {
  90. constexpr static int len = N - 1;
  91. };
  92. template <typename T>
  93. constexpr int static_length()
  94. {
  95. return static_length_t<T>::len;
  96. }
  97. // A compile-time string
  98. template <int N>
  99. class array_string
  100. {
  101. const char arr[N+1];
  102. template <typename S1, typename S2, int... S> constexpr
  103. array_string(const S1 &s1, const S2 &s2, const sequence<S...> seq)
  104. : arr{joined_index(s1, s2, S)...}
  105. {}
  106. template <int... S> constexpr
  107. array_string(const static_string<N> &src, const sequence<S...> seq)
  108. : arr{src.c_str()[S]...}
  109. {}
  110. public:
  111. // construct array_string from static_string
  112. constexpr array_string(const static_string<N> &src)
  113. : array_string(src, typename make_sequence<N>::t {})
  114. {}
  115. template <typename S1, typename S2>
  116. constexpr array_string(const S1 &a, const S2 &b)
  117. : array_string(a, b, typename make_sequence<static_length<S1>() + static_length<S2>()>::t {})
  118. {}
  119. static constexpr int length = N;
  120. constexpr char operator[](int i) const
  121. {
  122. return arr[i];
  123. }
  124. const char *c_str() const { return arr; }
  125. operator const char*() const
  126. {
  127. return c_str();
  128. }
  129. };
  130. template <typename S1, typename S2>
  131. constexpr char joined_index(const S1 &s1, const S2 &s2, int i)
  132. {
  133. return (i < S1::length) ? s1[i] : s2[i - S1::length];
  134. }
  135. // Allow concatenating array_string and static_string with any compile-time constant string
  136. // (including character string literal):
  137. template <int N, typename S2> constexpr
  138. array_string<N+static_length<S2>()> operator+(const array_string<N> &s1, const S2 &s2)
  139. {
  140. return array_string<N+static_length<S2>()>(s1, s2);
  141. }
  142. template <int N, typename S2> constexpr
  143. array_string<N+static_length<S2>()> operator+(const static_string<N> &s1, const S2 &s2)
  144. {
  145. return array_string<N+static_length<S2>()>(s1, s2);
  146. }
  147. } // end "cts" namespace