C/C++:切换非整数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4165131/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:41:19  来源:igfitidea点击:

C/C++: switch for non-integers

c++cswitch-statement

提问by peoro

Often I need to choose what to do according to the value of a non-POD constant element, something like this:

通常我需要根据非 POD 常量元素的值来选择要做什么,如下所示:

switch( str ) {
  case "foo": ...
  case "bar": ...
  default:    ...
}

Sadly switchcan only be used with integers: error: switch quantity not an integer.

遗憾的是switch只能与整数一起使用:error: switch quantity not an integer

The most trivial way to implement such thing is then to have is a sequence of ifs:

实现这种事情的最简单的方法是拥有一个ifs序列:

if( str == "foo" )      ...
else if( str == "bar" ) ...
else                    ...

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.

但是这个解决方案看起来很脏,应该花费 O(n),其中 n 是案例的数量,而那段代码在最坏的情况下使用二分搜索可能花费 O(log n)。

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 ifs in the right way, but still these hackswould require a lot of coding, making everything more complex and harder to maintain.

使用一些数据结构(如 Maps),可以获得表示字符串的整数( O(log n) ),然后使用 O(1) switch,或者可以通过if在右侧嵌套s来实现静态二进制排序方式,但这些黑客仍然需要大量编码,使一切变得更加复杂和难以维护。

What's the best way to do this? (fast, clean and simple, as the switchstatement is)

做到这一点的最佳方法是什么?(快速,干净,简单,正如switch声明一样)

回答by smilingthax

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

使用一些讨厌的宏和模板魔术,可以在编译时使用漂亮的语法进行展开的二进制搜索——但必须对 MATCHES(“case”)进行排序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)

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:

这将生成(大致)一个函数bool xy_match(char *&_buf,T &user),所以它必须在外层。调用它例如:

xy_match("bqr",youruserdata);

And the breaks 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++.

并且breaks 是隐含的,您不能通过。它也没有详细记录,抱歉。但是你会发现,还有更多的使用可能性,看看吧。注意:仅使用 g++ 进行测试。

Update C++11:

更新 C++11:

Lambdas and initializer list make things much prettier (no macros involved!):

Lambda 和初始值设定项列表使事情变得更漂亮(不涉及宏!):

#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\n"); }},
    {"bde",[]{ printf("1\n"); }},
    {"ger",[]{ printf("2\n"); }}
  },[](const char *a,const char *b){ return strcmp(a,b)<0;});           
  return 0;
}

That's the idea. A more complete implementation can be found here: switch.hpp.

这就是想法。更完整的实现可以在这里找到:switch.hpp

Update 2016: Compile time trie

2016 年更新:编译时间尝试

My newest take on this problem uses advanced c++11 metaprogramming to generate a search-trieat compile time. Unlike the previous approaches, this will handle unsortedcase-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).

我对这个问题的最新看法使用高级 c++11 元编程在编译时生成搜索树。与之前的方法不同,这将 很好地处理未排序的case-branchs/strings;它们只需要是字符串文字。G++ 也允许它们使用 constexpr,但不允许使用 clang(从 HEAD 3.9.0/trunk 274233 开始)。

In each trie node a switch-statement is utilized to harness the compiler's advanced code generator.

在每个 trie 节点中,使用 switch 语句来利用编译器的高级代码生成器。

The full implementation is available at github: smilingthax/cttrie.

完整的实现可在 github 上找到:smilethax/cttrie

回答by Billy ONeal

In C++, you can obtain O(lg n)performance by having a std::map<std::string, functionPointerType>. (In C you could implement what was essentially the same but it would be more difficult) Pull out the right function pointer using std::map<k, v>::find, and call that pointer. Of course, that's not going to be nearly as simple as a language supported switch statement. On the other hand, if you have enough items that there's going to be a huge difference between O(n)and O(lg n), that's probably an indication that you should be going for a different design in the first place.

在 C++ 中,您可以O(lg n)通过使用std::map<std::string, functionPointerType>. (在 C 中,您可以实现本质上相同的内容,但会更困难)使用 拉出正确的函数指针std::map<k, v>::find,然后调用该指针。当然,这不会像语言支持的 switch 语句那么简单。另一方面,如果您有足够多的项目,O(n)并且 和之间会有巨大差异O(lg n),这可能表明您应该首先进行不同的设计。

Personally, I've always found the ELSEIF chain more readable anyway.

就我个人而言,无论如何我总是发现 ELSEIF 链更具可读性。

回答by bjskishore123

You can achieve it without using any map or unordered_map like below. Compare first character alone to identify which string. If more than one match, then you can fallback to if/else chain within that case statement. Number of comparisons will be greatly reduced if not many strings starting with same letter.

您可以在不使用任何地图或 unordered_map 的情况下实现它,如下所示。单独比较第一个字符以确定哪个字符串。如果有多个匹配项,那么您可以回退到该 case 语句中的 if/else 链。如果以相同字母开头的字符串不多,则比较次数将大大减少。

char *str = "foo";
switch(*str)
{
case 'f':
    //do something for foo
    cout<<"Foo";
    break;
case 'b':
    //do something for bar
    break;
case 'c':
    if(strcmp(str, "cat") == 0)
    {
        //do something for cat
    }
    else if(strcmp(str, "camel") == 0)
    {
        //do something for camel
    }
}

This looks to be optimal solution without any cost, even though its not standard.

这看起来是没有任何成本的最佳解决方案,即使它不是标准的。

回答by John Dibling

Use an if...else block. You don't really have a compelling reason not to, aside from it not being pretty to look at, and the if...elseblock is the mostr straightforward solution.

使用if...else block. 除了看起来不漂亮之外,您并没有真正令人信服的理由不这样做,并且if...else块是最直接的解决方案。

Everything else requires additional code which as say say increases complexity. And it just moves the ugliness to elsewhere. But at some level, a string compare still has to happen. Now you've just covered it up with more code.

其他一切都需要额外的代码,据说这会增加复杂性。它只是将丑陋转移到其他地方。但在某种程度上,字符串比较仍然必须发生。现在你已经用更多的代码覆盖了它。

You mightgain some performance increases by using a map or a hash map, but youcan also gain similar if not better gains by simply choosing a smart order to evaluate your if...elseblocks. And switching to a map for performance reasons is really just premature micro-optimization.

通过使用映射或哈希映射,您可能会获得一些性能提升,但您也可以通过简单地选择一个智能顺序来评估if...else块来获得类似的甚至更好的收益。出于性能原因切换到地图实际上只是过早的微优化。

回答by Fred Foo

In C, there are two common solutions. The first is to keep your keywords in a sorted array, say

在 C 中,有两种常见的解决方案。第一个是将关键字保存在一个排序的数组中,比如

typedef struct Keyword {
    const char *word;
    int         sub;
    int         type;
} Keyword;

Keyword keywords[] ={   /* keep sorted: binary searched */
    { "BEGIN", XBEGIN, XBEGIN },
    { "END",   XEND,   XEND },
    { "NF",    VARNF,  VARNF },
    { "atan2", FATAN,  BLTIN },
    ...
};

and do a binary searchon them. The previous is straight from the source code of awkby C grandmaster Brian W. Kernighan.

并对它们进行二分搜索。前一个直接来自C 大师 Brian W. Kernighan的awk源代码。

The other solution, which is O(min(m, n)) if nis the length of your input string and mthe length of the longest keyword, is to use a finite-state solution such as a Lex program.

如果n是输入字符串的长度并且m是最长关键字的长度,则另一个解决方案是O(min( m, n))是使用有限状态解决方案,例如 Lex 程序。

回答by Simone

Something like that would be too much complex?

这样的事情会不会太复杂了?

#include <iostream>
#include <map>

struct object
{
    object(int value): _value(value) {}

    bool operator< (object const& rhs) const
    {
        return _value < rhs._value;
    }

    int _value;
};

typedef void(*Func)();

void f1() {
    std::cout << "f1" << std::endl;
}

void f2() {
    std::cout << "f2" << std::endl;
}

void f3() {
    std::cout << "f3" << std::endl;
}

int main()
{
    object o1(0);
    object o2(1);
    object o3(2);

    std::map<object, Func> funcMap;
    funcMap[o1] = f1;   
    funcMap[o2] = f2;   
    funcMap[o3] = f3;

    funcMap[object(0)](); // prints "f1"
    funcMap[object(1)](); // prints "f2"
    funcMap[object(2)](); // prints "f3"
}

回答by Aaron Frantisak

This is similar in spirit to the lambda and the unordered_map solutions, but I think this is the best of both worlds, with a very natural and readable syntax:

这在精神上类似于 lambda 和 unordered_map 解决方案,但我认为这是两全其美的,具有非常自然和可读的语法:

#include "switch.h"
#include <iostream>
#include <string>

int main(int argc, const char* argv[])
{
    std::string str(argv[1]);
    Switch(str)
        .Case("apple",  []() { std::cout << "apple" << std::endl; })
        .Case("banana", []() { std::cout << "banana" << std::endl; })
        .Default(       []() { std::cout << "unknown" << std::endl; });    
    return 0;
}

switch.h:

开关.h:

#include <unordered_map>
#include <functional>
template<typename Key>
class Switcher {
public:
    typedef std::function<void()> Func;
    Switcher(Key key) : m_impl(), m_default(), m_key(key) {}
    Switcher& Case(Key key, Func func) {
        m_impl.insert(std::make_pair(key, func));
        return *this;
    }
    Switcher& Default(Func func) {
        m_default = func;
        return *this;
    }
    ~Switcher() {
        auto iFunc = m_impl.find(m_key);
        if (iFunc != m_impl.end())
            iFunc->second();
        else
            m_default();
    }
private:
    std::unordered_map<Key, Func> m_impl;
    Func m_default;
    Key m_key;
};
template<typename Key>
Switcher<Key> Switch(Key key)
{
    return Switcher<Key>(key);
}

回答by abelenky

Here is example code that works:

这是有效的示例代码:

This should work.
(but ONLY on strings that are 4-bytes or less)

这应该有效。
(但仅限于 4 字节或更少的字符串)

This treats the strings as 4-byte integers.

这将字符串视为 4 字节整数。

This is considered, ugly, not portable, "hacky", and not at all good style.But it does do what you wanted.

这被认为是丑陋的、不可移植的、“hacky”的,而且完全不是好的风格。但它确实做你想做的。

#include "Winsock2.h"
#pragma comment(lib,"ws2_32.lib")

void main()
{
  char day[20];
  printf("Enter the short name of day");

  scanf("%s", day);

  switch(htonl(*((unsigned long*)day)))
  {
    case 'sun
std::string name = "Alice";

std::string gender = "boy";
std::string role;

SWITCH(name)
  CASE("Alice")   FALL
  CASE("Carol")   gender = "girl"; FALL
  CASE("Bob")     FALL
  CASE("Dave")    role   = "participant"; BREAK
  CASE("Mallory") FALL
  CASE("Trudy")   role   = "attacker";    BREAK
  CASE("Peggy")   gender = "girl"; FALL
  CASE("Victor")  role   = "verifier";    BREAK
  DEFAULT         role   = "other";
END

// the role will be: "participant"
// the gender will be: "girl"
': printf("sunday"); break; case 'mon
Color color = StringSwitch<Color>(argv[i])
   .Case("red", Red)
   .Case("orange", Orange)
   .Case("yellow", Yellow)
   .Case("green", Green)
   .Case("blue", Blue)
   .Case("indigo", Indigo)
   .Cases("violet", "purple", Violet)
   .Default(UnknownColor);
': printf("monday"); break; case 'Tue##代码##': printf("Tuesday"); break; case 'wed##代码##': printf("wednesday"); break; case 'Thu##代码##': printf("Thursday"); break; case 'Fri##代码##': printf("friday"); break; case 'sat##代码##': printf("saturday"); break; } }

tested in MSVC2010

在 MSVC2010 中测试

回答by oklas

You can use any type c/c++ switch implementation. Your code will be like this:

您可以使用任何类型的 c/c++开关实现。您的代码将是这样的:

##代码##

It is possible to use more complicated types for example std::pairsor any structs or classes that support equality operations (or comarisions for quickmode).

例如,可以使用更复杂的类型std::pairs或任何支持相等操作(或快速模式的comarisions )的结构或类。

Features

特征

  • any type of data which support comparisions or checking equality
  • possibility to build cascading nested switch statemens.
  • possibility to break or fall through case statements
  • possibility to use non constatnt case expressions
  • possible to enable quick static/dynamic mode with tree searching (for C++11)
  • 支持比较或检查相等性的任何类型的数据
  • 构建级联嵌套 switch 语句的可能性。
  • 打破或失败案例陈述的可能性
  • 使用非常量大小写表达式的可能性
  • 可以通过树搜索启用快速静态/动态模式(对于 C++11)

Sintax differences with language switch is

语言切换的语法差异是

  • uppercase keywords
  • need parentheses for CASE statement
  • semicolon ';' at end of statements is not allowed
  • colon ':' at CASE statement is not allowed
  • need one of BREAK or FALL keyword at end of CASE statement
  • 大写关键字
  • CASE 语句需要括号
  • 分号';' 不允许在语句末尾
  • 不允许在 CASE 语句中使用冒号 ':'
  • 在 CASE 语句的末尾需要 BREAK 或 FALL 关键字之一

For C++97language used linear search. For C++11and more modern possible to use quickmode wuth tree search where returnstatement in CASE becoming not allowed. The Clanguage implementation exists where char*type and zero-terminated string comparisions is used.

对于C++97使用线性搜索的语言。对于C++11更现代的可能使用quick模式 wuth 树搜索,其中CASE中的return语句变得不允许。C存在使用char*类型和以零结尾的字符串比较的语言实现。

Read more aboutthis switch implementation.

阅读有关此开关实现的更多信息

回答by Reinstate Monica

LLVM has llvm::StringSwitchthat you'd use as follows:

LLVM 有llvm::StringSwitch你会使用如下:

##代码##

The major win here is that there are no problems due to hash collisions: no matter what, the actual strings are always compared before a case is accepted.

这里的主要胜利是没有由于哈希冲突引起的问题:无论如何,在接受案例之前总是比较实际的字符串。