在switch语句中使用字符串-我们站在C ++ 17的什么位置? [英] Using strings in switch statements - where do we stand with C++17?
问题描述
我们每个人(大概)都有童年的写作梦想:
Every one of us has (probably) had the childhood dream of writing:
switch(my_std_string) {
case "foo": do_stuff(); break;
case "bar": do_other_stuff(); break;
default: just_give_up();
}
但这是不可能的,正如对此问题的回答过去的日子(2009年):
but this is not possible, as is explained in the answers to this question from the olden days (2009):
从那时起,我们就看到了C ++ 11的问世,它使我们可以走得更远:
Since then we've seen the advent of C++11, which lets us go as far as:
switch (my_hash::hash(my_std_string)) {
case "foo"_hash: do_stuff(); break;
case "bar"_hash: do_other_stuff(); break;
default: just_give_up();
}
如 answer 到编译时间字符串哈希-还算不错,尽管它实际上并没有真正完成我们想要的工作-有碰撞的可能性。
as described in an answer to Compile time string hashing - which is not so bad, although it doesn't actually do exactly what we wanted - there's a chance of collision.
我的问题是:从那时起语言的发展了吗(我想大多数是C ++ 14)是否影响了编写某种字符串大小写语句的方式?还是简化实现上述目标的要点?
My question is: Has the development of the language since then (mostly C++14 I suppose) affected the way one would write a sort-of-a string case statement? Or simplified the nuts-and-bolts for achieving the above?
具体说来,就是 C ++ 17标准为指日可待-鉴于我们可以假设标准将包含的内容,我对这个答案很感兴趣。
Specifically, with the conclusion of the C++17 standard being just around the corner - I'm interested in the answer given what we can assume the standard will contain.
注意:这不是关于使用switch语句的优点的讨论,也不是关于meta的线程。我要问的是一个有用的问题,因此请根据此答案/上/下投票。
Note: This is not a discussion on the merit of using switch statements, nor a thread on meta. I'm asking an informative question, so please answer/up/downvote based on that.
推荐答案
我的提议可以用C ++ 14,但是使用 if constexpr
和 std :: string_view
编写起来有点麻烦。
My proposal is possible with C++14, but with if constexpr
and std::string_view
it is a little esier to write.
首先-我们需要constexpr字符串-像这样:
First - we need constexpr string - like this one:
template <char... c>
using ConstString = std::integer_sequence<char, c...>;
template <char ...c>
constexpr auto operator ""_cstr ()
{
return ConstString<c...>{};
}
operator == 使用
: tuple
的无模板构造以及 tuple
现在具有constexpr <$ c的事实,也更容易编写$ c> operator ==
operator ==
is also easier to write with template-less construction of tuple
and with the fact that tuple
has now 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;
}
}
接下来的事情-定义开关箱代码:
Next thing - define switch-case code:
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};
}
还需要默认情况:
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};
}
因此, StringSwitch
-实际上,它是 if(){} else if(){} ... else {}
的构造:
So, the StringSwitch
- actually, it is if () {} else if () {} ... else {}
construction:
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;
};
可能的用法:
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);
正在工作演示。
我设法以更简单的方式来做ConstString,此帖子。
工作中 demo2 。
I manage to do ConstString in much easier way, basing on this post. Working demo2.
添加的部分如下:
#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...>;
};
通过更改第一个参数( 20
) BOOST_PP_REPEAT(20,ELEMENT_OR_NULL,#value)
我们可以控制 ConstString
的最大可能大小-用法如下
By changing first parameter (20
) in BOOST_PP_REPEAT(20, ELEMENT_OR_NULL, #value)
we can control the maximum possible size of ConstString
- and the usage is as follows:
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);
}
这篇关于在switch语句中使用字符串-我们站在C ++ 17的什么位置?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!