C/C++:切换非整数
很多时候我需要根据一个非POD常量元素的值来选择做什么,像这样:
Often I need to choose what to do according to the value of a non-POD constant element, something like this:
switch( str ) {
case "foo": ...
case "bar": ...
default: ...
}
遗憾的是 switch
只能与整数一起使用:错误:开关量不是整数
.
Sadly switch
can only be used with integers: error: switch quantity not an integer
.
实现这样的事情最简单的方法是拥有一个 if
s 序列:
The most trivial way to implement such thing is then to have is a sequence of if
s:
if( str == "foo" ) ...
else if( str == "bar" ) ...
else ...
但是这个解决方案看起来很脏,应该花费 O(n),其中 n 是案例的数量,而在最坏的情况下,使用二分搜索,这段代码可能花费 O(log n).
But this solution looks dirty and should cost O(n), where n is the number of cases, while that piece of code could cost O(log n) at the worst case with a binary search.
使用一些数据结构(如 Maps)可以获得一个表示字符串的整数( O(log n) ),然后使用 O(1) switch
,或者可以通过以正确的方式嵌套 if
来实现静态二进制排序,但这些 hacks 仍然需要大量编码,使一切变得更加复杂和难以维护.
Using some data structs (like Maps) it could be possible to obtain an integer representing the string ( O(log n) ), and then use an O(1) switch
, or one could implement a static binary sort by nesting if
s in the right way, but still these hacks would require a lot of coding, making everything more complex and harder to maintain.
最好的方法是什么?(快速、干净、简单,就像 switch
语句一样)
What's the best way to do this? (fast, clean and simple, as the switch
statement is)
推荐答案
使用一些讨厌的宏和模板魔法,可以在编译时以漂亮的语法获得展开的二进制搜索――但 MATCHES(case")必须是排序:fastmatch.h
Using some nasty macro and template magic it's possible to get an unrolled binary search at compiletime with pretty syntax -- but the MATCHES ("case") have to be sorted: fastmatch.h
NEWMATCH
MATCH("asd")
some c++ code
MATCH("bqr")
... the buffer for the match is in _buf
MATCH("zzz")
... user.YOURSTUFF
/*ELSE
optional
*/
ENDMATCH(xy_match)
这将(大致)生成一个函数bool xy_match(char *&_buf,T &user)
,所以它必须在外层.称之为例如与:
This will generate (roughly) a function bool xy_match(char *&_buf,T &user)
, so it must be at the outer level. Call it e.g. with:
xy_match("bqr",youruserdata);
而 break
是隐式的,你不能掉以轻心.它也没有大量记录,抱歉.但是你会发现,还有更多的使用可能性,看看吧.注意:仅使用 g++ 测试.
And the break
s are implicit, you cannot fall-thru. It's also not heavily documented, sorry. But you'll find, that there are some more usage-possibilities, have a look. NOTE: Only tested with g++.
Lambdas 和初始化列表让事情变得更漂亮(不涉及宏!):
Lambdas and initializer list make things much prettier (no macros involved!):
#include <utility>
#include <algorithm>
#include <initializer_list>
template <typename KeyType,typename FunPtrType,typename Comp>
void Switch(const KeyType &value,std::initializer_list<std::pair<const KeyType,FunPtrType>> sws,Comp comp) {
typedef std::pair<const KeyType &,FunPtrType> KVT;
auto cmp=[&comp](const KVT &a,const KVT &b){ return comp(a.first,b.first); };
auto val=KVT(value,FunPtrType());
auto r=std::lower_bound(sws.begin(),sws.end(),val,cmp);
if ( (r!=sws.end())&&(!cmp(val,*r)) ) {
r->second();
} // else: not found
}
#include <string.h>
#include <stdio.h>
int main()
{
Switch<const char *,void (*)()>("ger",{ // sorted:
{"asdf",[]{ printf("0
"); }},
{"bde",[]{ printf("1
"); }},
{"ger",[]{ printf("2
"); }}
},[](const char *a,const char *b){ return strcmp(a,b)<0;});
return 0;
}
就是这样.可以在此处找到更完整的实现:switch.hpp.
That's the idea. A more complete implementation can be found here: switch.hpp.
我对这个问题的最新处理使用高级 c++11 元编程来在编译时生成 search-trie.与以前的方法不同,这将处理 unsorted案例分支/字符串就好了;它们只能是字符串文字.G++ 也允许它们使用 constexpr,但不允许 clang(从 HEAD 3.9.0/trunk 274233 开始).
My newest take on this problem uses advanced c++11 metaprogramming to generate a search-trie at compile time. Unlike the previous approaches, this will handle unsorted case-branches/strings just fine; they only have to be string-literals. G++ also allows constexpr for them, but not clang (as of HEAD 3.9.0 / trunk 274233).
在每个 trie 节点中,使用一个 switch 语句来利用编译器的高级代码生成器.
In each trie node a switch-statement is utilized to harness the compiler's advanced code generator.
完整的实现可在 github 上获得:smilingthax/cttrie.
The full implementation is available at github: smilingthax/cttrie.
相关文章