用于创建 integral_constants 的任意元组的通用实用程序
Generic utility to create aribtrary tuples of integral_constants
利用 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
函数,它 returns 在字符串中找到的第一个插入符号的位置,从位置 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::integral_constants
的 std::tuple
创建 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>...>;
如何使用 caretPos
或其他方式生成此处所需的 Ns...
序列?
这里是 an example 使用 Boost.Hana(宣传 C++14)。它产生一个整数常量元组,这正是我们所要求的。
#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);
}
大部分工作都是为了解决每个值都是不同类型这一事实。 Hana 将值编码为类型的一部分,这意味着可以在常量表达式中使用参数。例如,一个 Hana 字符串是 String<'a', 'b', 'c'>
类型的 String
。这意味着当使用参数比较字符时,该字符被称为类型的一部分。这与 Scott 的字符串不同,后者在 constexpr
上充满并且不能将参数提升为函数内的常量表达式。
这是 the best I can do 和 Scott 的字符串:
#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);
}
对于普通的 constexpr
和未编码为类型的值,我们的限制要多一些。我们根本无法形成整数常量的元组,因为参数中的字符串内容在常量表达式中是无法使用的。相反,我制作了一个小的 constexpr 向量,我们可以像运行时一样使用它来推送我们找到的位置,但即便如此,我们也不能在编译时进行任何动态分配,所以它有一个需要的最大大小成为模板参数。使用 C++11,您也可以递归地而不是迭代地编写它,但我不确定您将如何实现 push_back
。您需要复制数组并更改一个值。据我所知,你必须通过数组的初始化列表来完成它,并且基本上需要一个索引参数包,其大小基于一个不是常量表达式的变量,这是可能的(虽然我不知道 C++11),但是 really complicated.
有趣的问题。
避免使用 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,
"!");
}
利用 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
函数,它 returns 在字符串中找到的第一个插入符号的位置,从位置 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::integral_constants
的 std::tuple
创建 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>...>;
如何使用 caretPos
或其他方式生成此处所需的 Ns...
序列?
这里是 an example 使用 Boost.Hana(宣传 C++14)。它产生一个整数常量元组,这正是我们所要求的。
#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);
}
大部分工作都是为了解决每个值都是不同类型这一事实。 Hana 将值编码为类型的一部分,这意味着可以在常量表达式中使用参数。例如,一个 Hana 字符串是 String<'a', 'b', 'c'>
类型的 String
。这意味着当使用参数比较字符时,该字符被称为类型的一部分。这与 Scott 的字符串不同,后者在 constexpr
上充满并且不能将参数提升为函数内的常量表达式。
这是 the best I can do 和 Scott 的字符串:
#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);
}
对于普通的 constexpr
和未编码为类型的值,我们的限制要多一些。我们根本无法形成整数常量的元组,因为参数中的字符串内容在常量表达式中是无法使用的。相反,我制作了一个小的 constexpr 向量,我们可以像运行时一样使用它来推送我们找到的位置,但即便如此,我们也不能在编译时进行任何动态分配,所以它有一个需要的最大大小成为模板参数。使用 C++11,您也可以递归地而不是迭代地编写它,但我不确定您将如何实现 push_back
。您需要复制数组并更改一个值。据我所知,你必须通过数组的初始化列表来完成它,并且基本上需要一个索引参数包,其大小基于一个不是常量表达式的变量,这是可能的(虽然我不知道 C++11),但是 really complicated.
有趣的问题。
避免使用 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,
"!");
}