我正在尝试学习一些更现代的C ++实践,例如模板,因此我决定创建一个幼稚且简单的命令行参数解析器,该解析器通常在编译时起作用,并且我已经遇到了constexpr
的问题,本质上,我想做的就是在编译时检查重复的条目(在运行时这样做很简单)。
首先,我有一个拥有单一配置的结构:
struct Arg_Opt_Tuple {
std::string_view mc{}; // multichar ie "help"
char sc{}; // singlechar ie 'h'
bool is_flag{};
};
现在让我们说我想创建一个函数(或者最终是对象的构造函数),该函数返回固定大小的std :: array,但是在编译时还要检查是否存在重复项或空值,我的目标是它以类似于以下的方式调用:
constexpr auto ARG_COUNT = 4U;
constexpr auto opts = checked_arr<ARG_COUNT>(
Arg_Opt_Tuple{"hello", 'h', false},
Arg_Opt_Tuple{"world", 'g', true},
Arg_Opt_Tuple{"goodbye", 'h', false}, // <- static_assert('h' == 'h')
Arg_Opt_Tuple{"hello", 'r', false} // <- static_assert(sv.compare("hello") == 0)
);
我的第一个尝试是使用std :: initializer_list,但是遇到了一些问题,经过一番谷歌搜索后得出的结论是,在这里与constexpr结合使用并不是正确的选择。我目前的尝试涉及一个可变参数模板:
template <std::size_t N, typename... T>
constexpr std::array<Arg_Opt_Tuple, N> checked_arr(T... list) {
static_assert(N == sizeof...(T));
return {list...};
}
这可以工作,但是完全不需要初始化数组,我真的希望它能够进行一些编译时检查。对于运行时重复或错误的值很容易,您可以循环遍历并比较或执行std :: find或不执行任何操作,但是这些似乎在编译时都不起作用,即(我知道这很丑陋,但您明白了):
for (std::size_t src_i = 0; src_i < ARG_COUNT; ++src_i) {
for (std::size_t check_i = 0; check_i < ARG_COUNT; ++check_i) {
// skip checking self
if (check_i == src_i) {
continue;
}
// doesnt work obviously
static_assert(opts[src_i].sc != opts[check_i].sc);
}
}
那么这将有多难实现?这是不好的设计吗?任何指针都会很可爱。
答案 0 :(得分:6)
对于运行时重复或错误的值很容易,您可以循环浏览并比较或执行std :: find或不执行任何操作,但是这些似乎在编译时都不起作用
普通循环确实有效:
template <typename T> constexpr bool has_duplicates(const T *array, std::size_t size)
{
for (std::size_t i = 0; i < size-1; i++)
for (std::size_t j = i+1; j < size; j++)
if (array[i] == array[j])
return 1;
return 0;
}
constexpr int foo[] = {1, 2, 3, 4};
static_assert(!has_duplicates(foo, 4));
如果要在函数中插入static_assert
,则需要将数组作为模板参数传递:
template <auto &array> constexpr void assert_has_no_duplicates()
{
constexpr std::size_t size = std::extent_v<std::remove_reference_t<decltype(array)>>;
static_assert(!has_duplicates(array, size));
}
constexpr int foo[] = {1, 2, 3, 4};
int main()
{
assert_has_no_duplicates<foo>();
}
或者,如果您更喜欢std::array
:
template <auto &array> constexpr void assert_has_no_duplicates()
{
static_assert(!has_duplicates(array.data(), array.size()));
}
constexpr std::array<int,4> foo = {1, 2, 3, 4};
int main()
{
assert_has_no_duplicates<foo>();
}
答案 1 :(得分:0)
与您要求的不完全相同,但是...如果您检查checked_arr()
中的重复项,并且发现异常,则抛出异常,则在执行checked_arr()
运行时会出现异常,而在执行时出现编译错误您可以在编译时执行。
我的意思是...你可以写
template <std::size_t N0 = 0u, typename ... Ts,
std::size_t N = (N0 > sizeof...(Ts)) ? N0 : sizeof...(Ts)>
constexpr auto checked_arr (Ts ... args)
{
std::array<Arg_Opt_Tuple, N> arr {args...};
for ( auto i = 0u ; i < sizeof...(Ts) ; ++i )
for ( auto j = 0u; j < sizeof...(Ts) ; ++j )
if ( (i != j) && (arr[i].sc == arr[j].sc) )
throw std::runtime_error("equal sc");
return arr;
}
(主题:观察N0
和N
的技巧:因此,只有当N0
大于sizeof...(Ts)
时,才必须显式显示constexpr auto opts = checked_arr(
Arg_Opt_Tuple{"hello", 'h', false},
Arg_Opt_Tuple{"world", 'g', true},
Arg_Opt_Tuple{"goodbye", 'h', false},
Arg_Opt_Tuple{"hello", 'r', false}
);
)
如果您致电
prog.cc:26:42: error: expression '<throw-expression>' is not a constant expression
26 | throw std::runtime_error("equal sc");
| ^
出现编译错误;在g ++中
opts
以下是完整的C ++ 17示例(如果在#include <array>
#include <string>
#include <exception>
struct Arg_Opt_Tuple {
std::string_view mc{}; // multichar ie "help"
char sc{}; // singlechar ie 'h'
bool is_flag{};
};
template <std::size_t N0 = 0u, typename ... Ts,
std::size_t N = (N0 > sizeof...(Ts)) ? N0 : sizeof...(Ts)>
constexpr auto checked_arr (Ts ... args)
{
std::array<Arg_Opt_Tuple, N> arr {args...};
for ( auto i = 0u ; i < sizeof...(Ts) ; ++i )
for ( auto j = 0u; j < sizeof...(Ts) ; ++j )
if ( (i != j) && (arr[i].sc == arr[j].sc) )
throw std::runtime_error("equal sc");
return arr;
}
int main ()
{
constexpr auto opts = checked_arr(
Arg_Opt_Tuple{"hello", 'h', false},
Arg_Opt_Tuple{"world", 'g', true},
Arg_Opt_Tuple{"goodbye", 'i', false},
Arg_Opt_Tuple{"hello", 'r', false}
);
}
中放置冲突,则不会编译)
constexpr
但是我建议简单地将数组初始化为constexpr std::array opts {
Arg_Opt_Tuple{"hello", 'h', false},
Arg_Opt_Tuple{"world", 'g', true},
Arg_Opt_Tuple{"goodbye", 'i', false},
Arg_Opt_Tuple{"hello", 'r', false}
};
变量
constexpr
并检查它是否在static_assert()
内部调用static_assert( checkOpts(opts) );
checOpts()
其中template <std::size_t N>
constexpr bool checkOpts (std::array<Arg_Opt_Tuple, N> const & arr)
{
for ( auto i = 0u ; i < N ; ++i )
for ( auto j = 0u; j < N ; ++j )
if ( (i != j) && (arr[i].sc == arr[j].sc) )
return false;
return true;
}
是
Transparent