如何实现通用 switch/case,它也适用于一般 C++ 类型并且语法相似?

2024-06-19

在 C/C++ 中,switch/case仅将整型类型与编译时常量进行比较。不可能使用它们来比较用户/库定义的类型,例如std::string与运行时值。为什么 switch 语句不能应用于字符串? https://stackoverflow.com/questions/650162/why-the-switch-statement-cannot-be-applied-on-strings

我们可以实施吗看起来像 switch/case它提供了类似的语法糖,目的是避免简单的if/else比较。


struct X { 
  std::string s;
  bool operator== (const X& other) const { return s == other.s; }
  bool operator== (const std::string& other) const { return s == other; }
};

简而言之,人们应该能够运行这个switch/case,如果有一个operator==为类型定义X. i.e.:

X x1{"Hello"}, x2{"World"};
switch(x1)
{
  // compare literal or any different type for which `==` is defined
  case "Hello": std::cout << "Compared 'Hello'\n"; break;     
  // cases/default appear in between and also can fall-through without break
  default:      std::cout << "Compared 'Default'\n"; 
  // compare compiletime or runtime created objects
  case x2:    { std::cout << "Compared 'World'\n"; break; }
}

我知道上面是不可能的。但任何看起来相似的东西都会很好。
这个问题的灵感来自于此演示的一种方式博客点:switch 语句的乐趣 http://bloglitb.blogspot.com/.


执行:

#define CONCATE_(X,Y) X##Y
#define CONCATE(X,Y) CONCATE_(X,Y)
#define UNIQUE(NAME) CONCATE(NAME, __LINE__)

#define MSVC_BUG(MACRO, ARGS) MACRO ARGS
#define NUM_ARGS_2(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, TOTAL, ...) TOTAL
#define NUM_ARGS_1(...) MSVC_BUG(NUM_ARGS_2, (__VA_ARGS__))
#define NUM_ARGS(...) NUM_ARGS_1(__VA_ARGS__, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define VA_MACRO(MACRO, ...) MSVC_BUG(CONCATE, (MACRO, NUM_ARGS(__VA_ARGS__)))(__VA_ARGS__)

#define switch_(X) for(struct { static_assert(not std::is_pointer<decltype(X)>::value, "No Pointers!"); \
              const decltype(X)& VALUE_; enum { CASES, DEFAULT, COMPARED } IS_ = CASES; } VAR_{X}; \
                       VAR_.IS_ != VAR_.COMPARED; \
                       VAR_.IS_ == VAR_.DEFAULT or (VAR_.IS_ = VAR_.COMPARED))

#define default_ {}} if(VAR_.IS_ == VAR_.COMPARED or VAR_.IS_ == VAR_.DEFAULT or \
                        ((VAR_.IS_ = VAR_.DEFAULT) and false)) \
                     { VAR_.IS_ = VAR_.COMPARED; CONCATE(default,__LINE__)

#define case_(...) VA_MACRO(case_, __VA_ARGS__)
#define case_1(X)    {}} if(VAR_.IS_ == VAR_.COMPARED or VAR_.VALUE_ == X) \
                         { VAR_.IS_ = VAR_.COMPARED; CONCATE(case,__LINE__)
#define case_2(X,OP) {}} if(VAR_.IS_ == VAR_.COMPARED or VAR_.VALUE_ OP X) \
                         { VAR_.IS_ = VAR_.COMPARED; CONCATE(case,__LINE__)

Usage:

X x1{"Hello"}, x2{"World"};
switch_(x1)
{{ // <--- MUST
  case_("Hello"):   std::cout << "Compared 'Hello'\n"; break;
  default_:         std::cout << "Compared 'Default'\n";
  case_(x2):      { std::cout << "Compared 'World'\n"; break; }
  case_("World"): { std::cout << "Duplicate 'World' again!\n"; break; } // duplicate
}}

Notes:

  • 目的{{ }}-- 修复一个场景,其中 2 个或多个语句case_出现时未包含提供的用户{}。这可能会导致某些语句始终执行,无论哪个语句case_是真的。
  • 更高的default_放置,更好的运行时性能。当没有有效案例时,将其放低可能会进行更多比较。
  • 重复的案例将被编译,但只有第一个案例将被执行。这个重复的案例问题可以通过生成运行时来修复/检查abort(),如果一个人准备好多次检查每个案例。
  • 如果一个人准备好放弃冒号的语法糖:, i.e. case(X)代替case(X):,那么CONCATE不需要宏。保留冒号通常会给编译器发出未使用标签的警告(-Wunused-label)
  • 该实用程序可以扩展到其他比较,例如<, >=, !=,或任何此类运营商;为此我们必须添加额外的参数switch_宏观;例如OP那必须放在case_宏为VAR_ OP X
  • 对于 C++03 兼容性,请使用make_pair在 - 的里面for声明后循环struct UNIQUE(Type) { enum { ... }; };
  • 数组和字符串指针可以与以下实用程序进行比较:

template<typename T>
struct Compare
{
  const T& this_;
  template<typename T_, size_t SIZE>
  bool
  operator== (const T_ (&other)[SIZE]) const
  {
    static_assert(std::is_same<decltype(this_), decltype(other)>::value, "Array size different!");
    return ::memcmp(this_, other, SIZE);
  }
};
template<>
struct Compare<const char*>
{
  const char* const this_;
  bool operator== (const char other[]) const { return (0 == ::strcmp(this_, other)); }
};
#define COMPARE(X) Compare<decltype(X)>{X}

Usage: switch_(COMPARE(var)) {{ }}.

Demo http://ideone.com/oUuGSk

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何实现通用 switch/case,它也适用于一般 C++ 类型并且语法相似? 的相关文章

随机推荐

  • 使用除 SINGLE_TABLE 之外的任何其他 Hibernate 继承策略时 JVM 崩溃

    好吧 这可能不太可能 但还是这样吧 在Java JRE 1 6 0 26 b03 中我有两个类 SuperControl及其子类SubControl 它们都需要是持久对象 我正在使用 Hibernate Annotations 来实现这一点
  • 在我的网站上显示 Google Analytics(分析)仪表板

    我设置了一个 ASP NET 网站 并使用 Google Analytics 进行页面跟踪 我唯一不喜欢的是我必须离开我的网站 到 Google Analytics 网站 才能看到该报告 有什么方法可以使用他们拥有的所有 AJAX 在我自己
  • 使用 获取用于 javascript 的 RSA 密钥?

    我的 Web 项目需要一个 RSA 密钥对 虽然有一些库 但我认为依靠浏览器 为了安全性和速度 为我生成密钥是个好主意 是否可以使用注册机或其他浏览器 API 来执行此操作 我不知道如何从注册机获取密钥 它们似乎是在提交时生成的 但我不想将
  • Google reCaptcha 永远加载

    我在我的网站上使用 Google 的 reCaptcha 2 0 它曾经运行良好 但自从我向公众开放我的网站并获得了更多用户后 recaptcha 不再适用于大多数用户 它加载得很好 但一旦用户单击 我不是机器人 复选框 它会永远加载并且从
  • 如何通过异步调用更新列表框?

    我开发了一个 Windows 窗体 C 应用程序 我只想通过分拆另一个线程来更新主窗体中列表框中的项目 而不阻塞 GUI 窗体 由于线程无法访问列表框等表单实体 因此我想到使用委托 下面的代码显示了我如何使用委托来完成该任务 但它阻止了 G
  • 下载 .NET 3.5 的实体框架

    哪个版本的实体框架 EF 我可以用在 NET 3 5我可以在哪里下载这个旧版本 对于 Net 3 5 您可以使用 EF v1 您是否尝试从以下位置下载 Microsoft NET Framework 3 5 Service Pack 1 h
  • 错误代码:InvalidIntentSamplePhraseSlot -

    我收到错误代码Error code InvalidIntentSamplePhraseSlot当我使用新的技能控制台构建模型时 完整的错误消息是 Sample utterance AddBookmarkIntent i am at page
  • iFrame 未扩展至 100% 高度

    我有这个下面的html 我希望 iFrame 能够 100 覆盖屏幕的其余部分 我在高度属性中尝试了 100 和 但不起作用 这是为什么 谢谢 div img height 35 width 84 alt Kucku src Content
  • Passport-local-mongoose:createStrategy 不是函数/authenticate 不是函数

    我正在构建这个启动项目 https github com cj wang mean start tree 424e6056e33bb16874ae808daf3780d53309296f并尝试添加用户登录护照本地猫鼬 https www n
  • 位操作,排列位

    我正在尝试创建一个循环 循环遍历所有不同的整数 其中最后 40 位中的 10 位设置为高 其余设置为低 原因是我有一个包含 40 个不同值的地图 并且我想对其中 10 个值相乘的所有不同方式求和 这只是出于好奇 所以真正感兴趣的是 bitm
  • 如何同时运行多个功能[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有以下代码 my func1 my func2 my func3 my func4 my func5 是否可以同时计算函数的数据 而
  • 在 iOS 5 中,我们可以邀请人们使用我们的应用程序或通过 Facebook 从应用程序发送好友请求吗?

    考虑X and Y是朋友Facebook他们都在各自的手机中安装了一个应用程序 但他们在这一点上并不是朋友应用程序的好友列表 那么现在 可以X 发送好友请求 Use Facebook sdk 3 1 https developers fac
  • 如果使用 jQuery 添加字段,Rails 嵌套表单属性不会保存

    我有一个带有嵌套表单的 Rails 表单 我使用 Ryan Bates 嵌套表单和 jquery 教程 并且就动态添加新字段而言 它工作得很好 但是当我提交表单时 它不会保存任何关联的属性 但是 如果在表单加载时构建部分 则它会很好地创建属
  • @OneToMany 与 @JoinTable 错误

    我试图理解 OneToMany with JoinTable 对于这样的场景 我正在使用 JPA 2 1 Hibernate 5 0 4 和 Oracle 11 XE 当我打电话时userDao save user 下面的代码 我有 jav
  • 将其从 Google Maps API v2 转换为 v3

    这适用于 Google Maps API v2 现在如何将其转换为 API v3 谢谢 Radar 1 Overlay Tiles var radar layer new GTileLayer new GCopyrightCollectio
  • 从内存地址创建python对象(使用gi.repository)

    有时我需要调用仅存在于 C 中的 gtk gobject 函数 但返回一个具有 python 包装器的对象 之前我使用过基于 ctypes 的解决方案 效果很好 现在我从 PyGtk import gtk 切换到 GObject intro
  • DynamodB:如何更新排序键?

    该表有两个键 filename 分区键 和eventTime 排序键 我要更新eventTime对于某些filename Tried put item and update item 发送相同的filename与新的eventTime但这些
  • 如何通过aws-sdk(javascript或node)获取s3存储桶大小

    我尝试使用 javascript nodejs aws sdk 查找 获取 s3 存储桶信息 但没有找到这样的 api 如何通过 aws sdk javascript 或 node api 获取 s3 存储桶大小 信息 每天一次向 Clou
  • JavaScript 中的“new”关键字是什么?

    The newJavaScript 中的关键字第一次遇到时可能会很混乱 因为人们倾向于认为 JavaScript 不是面向对象的编程语言 它是什么 它解决什么问题 什么时候合适 什么时候不合适 它做了 5 件事 它创建一个新对象 这个对象的
  • 如何实现通用 switch/case,它也适用于一般 C++ 类型并且语法相似?

    在 C C 中 switch case仅将整型类型与编译时常量进行比较 不可能使用它们来比较用户 库定义的类型 例如std string与运行时值 为什么 switch 语句不能应用于字符串 https stackoverflow com