ein*_*ica 13 c++ stdstring string-literals switch-statement c++17
我们每个人(可能)都有童年的写作梦想:
switch(my_std_string) {
case "foo": do_stuff(); break;
case "bar": do_other_stuff(); break;
default: just_give_up();
}
Run Code Online (Sandbox Code Playgroud)
但这是不可能的,正如古代(2009年)对这个问题的答案所解释的那样:
从那时起,我们已经看到了C++ 11的出现,它让我们走得更远:
switch (my_hash::hash(my_std_string)) {
case "foo"_hash: do_stuff(); break;
case "bar"_hash: do_other_stuff(); break;
default: just_give_up();
}
Run Code Online (Sandbox Code Playgroud)
如在描述答案来编译时间字符串哈希 -这是没有那么糟糕,但它实际上并没有做正是我们想要的-有碰撞的机会.
我的问题是:从那时起语言的发展(我认为主要是C++ 14)是否影响了编写一个字符串case语句的方式?或简化实现上述目的的螺母和螺栓?
具体而言,与结束C++ 17标准之中指日可待 -我很感兴趣,鉴于我们可以假设标准将包含答案.
注意:这不是关于使用switch语句的优点的讨论,也不是关于meta的线程的讨论.我问的是一个内容丰富的问题,所以请在此基础上回答/ up/downvote.
这很容易写
switcher(expr)->*
caser(case0)->*[&]{
}->*
caser(case1)->*[&]{
};
Run Code Online (Sandbox Code Playgroud)
构建一个静态大小的case0遍历哈希表,caseN动态填充它,测试冲突==,通过查找expr,并运行相应的lambda.
即使caser(case3)->*caser(case4)->*lambda和->*fallthrough可能的支持.
我没有看到迫切需要.
我认为在C++ 17中编写它也没有任何优势.
我的建议是可以用C++ 14,但if constexpr和std::string_view这是一个有点esier写.
首先 - 我们需要constexpr字符串 - 就像这样:
template <char... c>
using ConstString = std::integer_sequence<char, c...>;
template <char ...c>
constexpr auto operator ""_cstr ()
{
return ConstString<c...>{};
}
Run Code Online (Sandbox Code Playgroud)
operator ==使用无模板构造tuple和tuple现在constexpr 的事实也更容易编写operator ==:
template <char... c1, char ...c2>
constexpr bool operator == (ConstString<c1...>, ConstString<c2...>)
{
if constexpr (sizeof...(c1) == sizeof...(c2)) // c++17 only
{
return tuple{c1...} == tuple{c2...}; // c++17 only
}
else
{
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
接下来 - 定义switch-case代码:
template <typename Callable, typename Key>
class StringSwitchCase;
template <typename Callable, char ...c>
struct StringSwitchCase<Callable, ConstString<c...>>
{
constexpr bool operator == (const std::string_view& str) // c++17 only
{
constexpr char val[] = {c..., '\0'};
return val == str;
}
Callable call;
static constexpr ConstString<c...> key{};
};
template <typename Callable, char ...c>
constexpr auto makeStringSwitchCase(CString<c...>, Callable call)
{
return StringSwitchCase<Callable, ConstString<c...>>{call};
}
Run Code Online (Sandbox Code Playgroud)
还需要默认情况:
template <typename Callable>
struct StringSwitchDefaultCase
{
constexpr bool operator == (const std::string_view&)
{
return true;
}
Callable call;
};
template <typename Callable>
constexpr auto makeStringSwitchDefaultCase(Callable call)
{
return StringSwitchDefaultCase<Callable>{call};
}
Run Code Online (Sandbox Code Playgroud)
所以,StringSwitch实际上,它是if () {} else if () {} ... else {}构造:
template <typename ...Cases>
class StringSwitch
{
public:
StringSwitch(Cases&&... cases) : cases(std::forward<Cases>(cases)...) {}
constexpr auto call(const std::string_view& str)
{
return call<0u>(str);
}
private:
template <std::size_t idx>
constexpr auto call(const std::string_view& str)
{
if constexpr (idx < sizeof...(Cases))
{
if (std::get<idx>(cases) == str)
{
return std::get<idx>(cases).call();
}
return call<idx + 1>(str);
}
else
{
return;
}
}
std::tuple<Cases...> cases;
};
Run Code Online (Sandbox Code Playgroud)
可能的用法:
StringSwitch cstrSwitch(
makeStringSwitchCase(234_cstr,
[] {
cout << "234\n";
}),
makeStringSwitchCase(ConstString<'a', 'b', 'c'>{}, // only C++ standard committee know why I cannot write "abc"_cstr
[] {
cout << "abc\n";
}),
makeStringSwitchDefaultCase([] {
cout << "Default\n";
}));
cstrSwitch.call("abc"s);
Run Code Online (Sandbox Code Playgroud)
工作演示.
基于这篇文章,我设法以更简单的方式做ConstString .工作演示2.
增加的部分如下:
#include <boost/preprocessor/repetition/repeat.hpp>
#include <boost/preprocessor/comma_if.hpp>
#define ELEMENT_OR_NULL(z, n, text) BOOST_PP_COMMA_IF(n) (n < sizeof(text)) ? text[n] : 0
#define CONST_STRING(value) typename ExpandConstString<ConstString<BOOST_PP_REPEAT(20, ELEMENT_OR_NULL, #value)>, \
ConstString<>, sizeof(#value) - 1>::type
template <typename S, typename R, int N>
struct ExpandConstString;
template <char S1, char ...S, char ...R, int N>
struct ExpandConstString<ConstString<S1, S...>, ConstString<R...>, N> :
ExpandConstString<ConstString<S...>, ConstString<R..., S1>, N - 1>
{};
template <char S1, char ...S, char ...R>
struct ExpandConstString<ConstString<S1, S...>, ConstString<R...>, 0>
{
using type = ConstString<R...>;
};
Run Code Online (Sandbox Code Playgroud)
通过更改第一个参数(20),BOOST_PP_REPEAT(20, ELEMENT_OR_NULL, #value)我们可以控制最大可能的大小ConstString- 并且用法如下:
int main() {
StringSwitch cstrSwitch(
makeStringSwitchCase(CONST_STRING(234){},
[] {
cout << "234\n";
}),
makeStringSwitchCase(CONST_STRING(abc){},
[] {
cout << "abc\n";
}),
makeStringSwitchDefaultCase([] {
cout << "Default\n";
}));
cstrSwitch.call("abc"s);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6412 次 |
| 最近记录: |