General utility for creating arbitrary integ_constants tuples

Using Scott Schurr str_const I have a constexpr line.

 class StrConst { public: template<size_t N> constexpr StrConst(const char (&str)[N]) : str_(str) , len_(N - 1) { static_assert(N > 1, "not a string"); } constexpr operator const char*() const { return str_; } constexpr size_t size() const { return len_; } constexpr char operator[] (size_t i) const { return i < len_ ? str_[i] : throw std::out_of_range("invalid index"); } private: const char* const str_; const size_t len_; }; 

I have another constexpr function that returns the position of the first carriage found in the line, starting at position n:

 constexpr int caretPos(const StrConst& str, size_t n = 0) { if (n == str.size()) return -1; if (str[n] == '^') return n; return caretPos(str, n+1); } 

I can use the caretPos results to create a typedef for std::tuple of std::integral_constants , where the tuple size is the number of patterns found in the row, and each element of the tuple is an integral constant whose value is the caret position in the row.

Here I manually create this tuple:

 int main() { constexpr StrConst s("hello^world^"); constexpr int pos1 = caretPos(s); constexpr int pos2 = caretPos(s, pos1+1); using P1 = std::integral_constant<int, pos1>; using P2 = std::integral_constant<int, pos2>; using PosTuple = std::tuple<P1, P2>; static_assert(std::tuple_element_t<0, PosTuple>::value == 5, ""); static_assert(std::tuple_element_t<1, PosTuple>::value == 11, ""); } 

Question:

Now I would like to generalize this to any input string with any number of controls.

 template<size_t... Ns> using PosTuple = std::tuple<std::integral_constant<int, Ns>...>; 

How can I generate the Ns... sequence required here using caretPos or some other means?

Working example

+8
c ++ template-meta-programming c ++ 14 boost-hana
source share
3 answers

Intriguing question.

Avoiding using StrConst , in the following example you can see a way to get the type std::tuple<std::integral_constant<std::size_t, Pos1>, std::integral_constant<std::size_t, Pos2>, ...> .

First of all, use arrayConverter() to convert a char const * to std::array<char, N> ( lc in the following code); second: use tupleIndexGenerator<lc.size(), lc, '^'>::type to get the requested type.

So, if "hello^world^" is a string, from tupleIndexGenerator<lc.size(), lc, '^'>::type you get std::tuple<std::integral_constant<std::size_t, 5U>, std::integral_constant<std::size_t, 11U>>

The code

 #include <iostream> #include <array> #include <tuple> template <typename, typename> struct typeConcat; template <typename T0, template <typename ...> class C, typename ... Ts> struct typeConcat<T0, C<Ts...>> { using type = C<T0, Ts...>; }; template <std::size_t N, std::array<char, N> const & A, char CH, std::size_t Pos = 0U, bool EQ = ((Pos < N) && (A.at(Pos) == CH))> struct tupleIndexGenerator; template <std::size_t N, std::array<char, N> const & A, char CH> struct tupleIndexGenerator<N, A, CH, N, false> { using type = std::tuple<>; }; template <std::size_t N, std::array<char, N> const & A, char CH, std::size_t Pos> struct tupleIndexGenerator<N, A, CH, Pos, false> { using type = typename tupleIndexGenerator<N, A, CH, Pos+1U>::type; }; template <std::size_t N, std::array<char, N> const & A, char CH, std::size_t Pos> struct tupleIndexGenerator<N, A, CH, Pos, true> { using type = typename typeConcat< std::integral_constant<std::size_t, Pos>, typename tupleIndexGenerator<N, A, CH, Pos+1U>::type>::type; }; template <typename T, size_t N, size_t ... Is> constexpr auto arrayConverter (T const (&arr)[N], std::index_sequence<Is...> const &) { return std::array<T, N> { { arr[Is]... } }; } template <typename T, size_t N> constexpr auto arrayConverter (T const (&arr)[N]) { return arrayConverter(arr, std::make_index_sequence<N>{}); } constexpr auto lc = arrayConverter("hello^world^"); int main () { static_assert(std::is_same< typename tupleIndexGenerator<lc.size(), lc, '^'>::type, std::tuple<std::integral_constant<std::size_t, 5U>, std::integral_constant<std::size_t, 11U>>>::value, "!"); } 
+1
source share

Here's an example using Boost.Hana (which advertises C ++ 14). This results in a set of integral constants that are directly requested.

 #include <cstddef> #include <utility> #include <boost/hana.hpp> namespace hana = boost::hana; using namespace boost::hana::literals; template<typename Str, int... Is> constexpr auto unfilteredCaretsImpl(Str str, std::integer_sequence<int, Is...>) { return hana::make_tuple(boost::hana::if_(str[hana::int_c<Is>] == hana::char_c<'^'>, hana::just(hana::int_c<Is>), hana::nothing)...); } template<typename Str> constexpr auto unfilteredCarets(Str str) { return unfilteredCaretsImpl(str, std::make_integer_sequence<int, hana::length(str)>{}); } template<typename Str> constexpr auto allCarets(Str str) { auto unfiltered = unfilteredCarets(str); auto filtered = hana::filter(unfiltered, [](auto opt) { return opt != hana::nothing; }); return hana::transform(filtered, [](auto opt) { return opt.value(); }); } int main() { constexpr auto carets = allCarets(BOOST_HANA_STRING("hello^world^")); static_assert(hana::length(carets) == std::size_t{2}); static_assert(carets[0_c] == 5); static_assert(carets[1_c] == 11); } 

Most of the work is related to the fact that each value is a different type. Hana encodes the values ​​as part of the type, which means that the parameter can be used in constant expression. For example, a Hana string is of type String<'a', 'b', 'c'> for some artificial String . This means that when using a parameter to compare a character, this character is known as part of the type. This is different from a Scott line that ends in constexpr and cannot raise a parameter to a constant expression inside a function.

+1
source share

Here's the best I can do with a Scott line:

 #include <cstddef> #include <stdexcept> class StrConst { public: template<size_t N> constexpr StrConst(const char (&str)[N]) : str_(str) , len_(N - 1) { static_assert(N > 1, "not a string"); } constexpr operator const char*() const { return str_; } constexpr size_t size() const { return len_; } constexpr char operator[] (size_t i) const { return i < len_ ? str_[i] : throw std::out_of_range("invalid index"); } private: const char* const str_; const size_t len_; }; template<typename T, size_t MaxSize> class VectorConst { public: constexpr VectorConst() = default; constexpr size_t size() const { return _size; } constexpr void push_back(const T& value) { _data[_size++] = value; } constexpr T& operator[](size_t i) { return _data[i]; } constexpr const T& operator[](size_t i) const { return _data[i]; } private: T _data[MaxSize]{}; size_t _size = 0; }; template<size_t Size> constexpr auto allCarets(const StrConst& str) { VectorConst<size_t, Size> result; for (size_t i = 0; i < str.size(); ++i) { if (str[i] == '^') { result.push_back(i); } } return result; } int main() { constexpr StrConst s("hello^world^"); constexpr auto carets = allCarets<s.size()>(s); static_assert(carets.size() == 2); static_assert(carets[0] == 5); static_assert(carets[1] == 11); } 

With regular constexpr and values ​​that are not encoded in types, we are a bit more limited. We simply cannot form a tuple of integral constants, because the contents of the string in the parameter are not applicable in constant expressions. Instead, I made a small constexpr vector, which we can use in the same way as runtime to push locations as they are found, but even then we cannot make any dynamic allocations at compile time, so it has a maximum size that you needed for the template parameter. With C ++ 11, you can also write this recursively, not iteratively, but I'm not sure how to implement push_back . You need to copy the array and change the value. As far as I know, you will need to do this through a list of initializations for the array and essentially require a package of index parameters whose size is based on a variable that is not a constant expression, which is possible (although I don't know about C ++ 11), but really complicated .

+1
source share

All Articles