如何在多个条件下进行分支/切换
How to branch/switch on multiple conditions?
有没有一种方法可以在不编写看起来一团糟的代码的情况下对多个条件进行分支?将赞赏C++11或C++14中的突触融合糖。
#include <iostream>
enum state
{
STATE_1,
STATE_2,
STATE_3,
STATE_4,
STATE_5,
STATE_6,
STATE_7,
STATE_8,
};
state f(int a, bool b, const std::string& str)
{
// How not to:
if (a < 0)
{
if (b == false)
{
if (str != "morning")
{
return STATE_1;
}
else
{
return STATE_2;
}
}
else
{
if (str != "morning")
{
return STATE_3;
}
else
{
return STATE_4;
}
}
}
else // a >= 0
{
if (b == false)
{
if (str != "morning")
{
return STATE_5;
}
else
{
return STATE_6;
}
}
else
{
if (str != "morning")
{
return STATE_7;
}
else
{
return STATE_8;
}
}
}
}
int main()
{
std::cout << "State: " << f(1, true, "morning") << std::endl;
}
可以在编译时在POD中嵌入布尔值(条件结果)列表,并在其上嵌入switch
。
用法:main.cpp
#include <iostream> /* std::cout */
#include "mswitch.h" /* mswitch, mcase */
enum state
{
STATE_1,
STATE_2,
STATE_3,
STATE_4,
STATE_5,
STATE_6,
STATE_7,
STATE_8,
};
state f(int a, bool b, const std::string& str)
{
mswitch(a >= 0, b == true, str == "morning")
{
mcase(false, false, false): return STATE_1;
mcase(false, false, true) : return STATE_2;
mcase(false, true, false) : return STATE_3;
mcase(false, true, true) : return STATE_4;
mcase(true, false, false) : return STATE_5;
mcase(true, false, true) : return STATE_6;
mcase(true, true, false) : return STATE_7;
mcase(true, true, true) : return STATE_8;
}
return STATE_1;
}
int main()
{
std::cout << "State: " << f(1, true, "morning") << std::endl;
}
合成糖:mswitch.h
#ifndef MSWITCH_GUARD_H
#define MSWITCH_GUARD_H
#include <initializer_list>
#include <cstddef>
namespace mswitch
{
constexpr long long encode(long long value, size_t size) { return value << 6 | (0x3F & size); }
class mswitch
{
std::initializer_list<bool> _flags;
public:
mswitch(std::initializer_list<bool> const& l) : _flags(l) {}
operator long long() const
{
long long result = 0;
size_t index = 0;
for (bool b : _flags) {
result |= b << index++;
}
return encode(result, _flags.size());
}
};
template<bool head, bool... tail>
struct mcase
{
constexpr mcase() = default;
constexpr operator long long() const
{
return encode(tll(), 1+sizeof...(tail));
}
constexpr long long tll() const { return head | mcase<tail...>().tll() << 1; }
};
template<bool b>
struct mcase<b>
{
constexpr mcase() = default;
constexpr operator long long() const { return encode(tll(), 1); }
constexpr long long tll() const { return b; }
};
}
#define mswitch(head, ...) switch(mswitch::mswitch{head, __VA_ARGS__})
#define mcase(head, ...) case mswitch::mcase<head, __VA_ARGS__>()
#endif // MSWITCH_GUARD_H
使用g++ -std=c++14 -O2 -Wall -pedantic main.cpp
编译
它的工作原理
mswitch
和mcase
对象只是在布尔列表和可switch
的long long
之间建立一个双射(如果可能的话,在编译时使用constexpr
函数)。由于mcase
s是给定的编译时间常数,因此所有switch
标签本身实际上都是连续的编译时间常量。
我会为此制作一个查找表:
#include <iostream>
#include <string>
enum state {
STATE_1,
STATE_2,
STATE_3,
STATE_4,
STATE_5,
STATE_6,
STATE_7,
STATE_8,
};
state f(int a, bool b, const std::string& str) {
static const state table[2][2][2] = {
STATE_8, // 0, 0, 0
STATE_7, // 0, 0, 1
STATE_6, // 0, 1, 0
STATE_5, // 0, 1, 1
STATE_4, // 1, 0, 0
STATE_3, // 1, 0, 1
STATE_2, // 1, 1, 0
STATE_1 // 1, 1, 1
};
return table[a < 0][b == false][str != "morning"];
}
int main() {
std::cout << f(1, true, "morning") << std::endl;
}
我同意,模式匹配非常适合那里。不幸的是,内置的switch
在C++中非常有限。
编译时布尔包的实现非常简单。
#include <type_traits>
namespace detail
{
constexpr std::size_t pack_bool(std::size_t result)
{
return result;
}
template<typename T, typename... Ts>
constexpr std::size_t pack_bool(std::size_t result, T arg, Ts... args)
{
static_assert(std::is_same<bool, T>::value, "boolean expected");
return pack_bool((result << 1) | arg, args...);
}
}
template<typename T, typename... Ts>
constexpr std::size_t pack_bool(T arg, Ts... args)
{
static_assert(std::is_same<bool, T>::value, "boolean expected");
return detail::pack_bool(arg, args...);
}
现在,您可以在switch
语句中使用它
#include <iostream>
enum state
{
STATE_1,
STATE_2,
STATE_3,
STATE_4,
STATE_5,
STATE_6,
STATE_7,
STATE_8,
};
state f(int a, bool b, const std::string& str)
{
switch (pack_bool(a >= 0, b == true, str == "morning"))
{
case pack_bool(false, false, false) : return STATE_1;
case pack_bool(false, false, true) : return STATE_2;
case pack_bool(false, true, false) : return STATE_3;
case pack_bool(false, true, true) : return STATE_4;
case pack_bool(true, false, false) : return STATE_5;
case pack_bool(true, false, true) : return STATE_6;
case pack_bool(true, true, false) : return STATE_7;
case pack_bool(true, true, true) : return STATE_8;
}
return STATE_1;
}
int main()
{
std::cout << "State: " << f(1, true, "morning") << std::endl;
}
这是我的版本:
特点:
-
保留编译器对遗漏案例的检查,并提供有关遗漏案例的信息
-
案例的编译时评估意味着零运行时开销
-
没有宏来污染全局命名空间,并随机阻止仅头库工作:-)
缺点:
- 预定义一些样板枚举的要求(仅一次,在我为您做的库中)
代码:
#include <iostream>
#include <utility>
#include <sstream>
#include <string>
namespace detail{
template<size_t N> struct boolean_value;
template<size_t N> using boolean_value_t = typename boolean_value<N>::type;
template<size_t N> constexpr auto to_int(boolean_value_t<N> b) { return static_cast<int>(b); };
template<size_t N> constexpr auto to_boolean_value(int i) { return static_cast<boolean_value_t<N>>(i); };
template<> struct boolean_value<1> {
enum type { bit0, bit1 };
};
template<> struct boolean_value<2> {
enum type { bit00, bit01, bit10, bit11 };
};
template<> struct boolean_value<3> {
enum type { bit000, bit001, bit010, bit011, bit100, bit101, bit110, bit111 };
};
template<class...Args, size_t...Is>
static constexpr auto make_bitfield(std::tuple<Args...> t, std::index_sequence<Is...>)
{
#if __cplusplus > 201402L
int accum = (0 | ... | (std::get<Is>(t) ? (1 << Is) : 0));
#else
int accum = 0;
using expand = int[];
(void) expand { (std::get<Is>(t) ? accum |= (1 << Is) : 0) ... };
#endif
return to_boolean_value<sizeof...(Is)>(accum);
}
}
template<class...Args>
constexpr
auto mcase(Args&&...args)
{
return detail::make_bitfield(std::make_tuple(bool(std::forward<Args>(args))...),
std::index_sequence_for<Args...>());
}
// little function to defeat the optimiser, otherwise clang inlines the whole program!
auto get_result()
{
using namespace std;
istringstream ss("foo 2");
auto result = tuple<string, int>();
ss >> get<0>(result) >> get<1>(result);
return result;
}
int main()
{
using namespace std;
const auto result = get_result();
const auto& s1 = std::get<0>(result);
const auto& v1 = std::get<1>(result);
switch(mcase(s1 == "foo"s, v1 == 2))
{
case mcase(true, true):
cout << mcase(true, true) << endl;
break;
case mcase(false, false):
cout << mcase(false, false) << endl;
break;
}
return 0;
}
编译器输出示例:
./mswitch.cpp:114:12: warning: enumeration values 'bit01' and 'bit10' not handled in switch [-Wswitch]
switch(mcase(s1 == "foo"s, v1 == 2))
^
1 warning generated.
./mswitch.cpp:114:12: warning: enumeration values 'bit01' and 'bit10' not handled in switch [-Wswitch]
switch(mcase(s1 == "foo"s, v1 == 2))
^
1 warning generated.
运行时输出:
3
作为使用C++基本功能的替代答案,您也可以考虑使用三元运算符。
enum state
{
STATE_1,
STATE_2,
STATE_3,
STATE_4,
STATE_5,
STATE_6,
STATE_7,
STATE_8,
};
state f(int a, bool b, const std::string& str)
{
// How not to:
if (a < 0)
return b == true ? (str == "morning" ? STATE_4 : STATE_3) : (str == "morning" ? STATE_2 : STATE_1);
else // a >= 0
return b == true ? (str == "morning" ? STATE_8 : STATE_7) : (str == "morning" ? STATE_6 : STATE_5);
}
int main()
{
std::cout << "State: " << f(1, true, "morning") << std::endl;
}
当只使用基本运算符时,结果非常紧凑。
相关文章:
- 与互斥锁相比,旋转锁可以保证上下文切换
- 如何在运行中期切换GTK CSS style_context
- IPC使用多个管道和分支进程来运行Python程序
- 线程,如果else语句,都是错误的上下文切换后,会发生什么
- 如何在cpp文件之间切换窗口?在Qt中
- 如何删除peer if else分支中的冗长句子
- 如何确保在使用基于布尔值的两个方法之一调用方法时避免分支预测错误
- 如何正确地将分支添加到已存在的树中
- 如何将分支添加到已存在的TTree:ROOT
- 如何切换整数的 N 位
- 切换大小写后如何阻止变量重置?
- 如何删除 LLVM 中的不规则分支?
- C++ 程序菜单使用做同时和切换
- 如果以下行不包含决策或分支,GDB 无法单步跳过函数
- 在线程中读取无符号整数时,c++ 位是否以原子方式切换?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 函数指针与条件分支
- 为什么这个选择排序算法仍然切换一个元素,当它已经是其他元素中最小的元素时?
- 切换分支时如何保留 CMake 构建工件
- 如何在多个条件下进行分支/切换