利用Scott Schurr's str_const
我有一个constexpr
字符串。
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_;
};
我有另一个constexpr
函数,它从位置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);
}
我可以使用caretPos
的结果为std::tuple
std::integral_constants
创建一个typedef,其中元组的大小是字符串中找到的插入符号的数量,每个元组元素是一个整数常量,其值是字符串中插入符号的位置。
这里我手动构造这个元组:
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, "");
}
问题:
我现在想对任何具有任意数量插入符号的输入字符串进行概括。
template<size_t... Ns>
using PosTuple = std::tuple<std::integral_constant<int, Ns>...>;
如何使用Ns...
或其他方式生成此处所需的caretPos
序列?
答案 0 :(得分:1)
Here's an example using Boost.Hana (which advertises C++14). It results in a tuple of integral constants, which is directly what was asked for.
#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 getting around the fact that each value is a different type. Hana encodes the values as part of the type, which means it's possible to use a parameter in a constant expression. For example, a Hana string is of type String<'a', 'b', 'c'>
for some artificial String
. That means that when using a parameter to compare a character, that character is known as part of the type. This is different from Scott's string, which goes full on constexpr
and cannot lift a parameter to a constant expression within the function.
答案 1 :(得分:1)
Here's the best I can do with Scott's string:
#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 ordinary constexpr
and values not being encoded in types, we're a bit more limited. We simply can't form a tuple of integral constants because the string content in the parameter is unusable in constant expressions. Instead, I made a small constexpr vector that we can use just like a runtime one to push locations as we find them, but even then, we can't do any dynamic allocations at compile-time, so it has a max size that needs to be a template parameter. With C++11, you can possibly also write this recursively instead of iteratively, but I'm not sure how you'd implement push_back
. You need to copy the array and change a value. As far as I know, you'd have to do it through an initializer list for the array and would essentially need a parameter pack of indices whose size is based on a variable that isn't a constant expression, which is possible (though I don't know about C++11), but really complicated.
答案 2 :(得分:1)
有趣的问题。
避免使用StrConst
,在以下示例中,您可以看到获取std::tuple<std::integral_constant<std::size_t, Pos1>, std::integral_constant<std::size_t, Pos2>, ...>
类型的方法。
首先,使用arrayConverter()
转换char const *
中的std::array<char, N>
(lc
,在以下代码中);第二:使用tupleIndexGenerator<lc.size(), lc, '^'>::type
获取请求的类型。
因此,如果"hello^world^"
是字符串,则tupleIndexGenerator<lc.size(), lc, '^'>::type
来自std::tuple<std::integral_constant<std::size_t, 5U>, std::integral_constant<std::size_t, 11U>>
代码
#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,
"!");
}