我见过类似的线程,但是,不知道如何将解决方案完全应用到我的案例中。我的问题是,我有一套用例可以说'A'' B' C' C',输入时我需要执行某些命令传递(2个用例是输入)是列出的任何2个用例。例如:
switch(input1)
{
case A:
break;
case B:
break;
case C:
break;
}
在每个案例中,我将不得不检查输入2, 所以,最终的代码看起来像
switch(input1)
{
case A:
{
switch(input2):
case B:
break;
case c:
break;
}
case B:
{
switch(input2):
case A:
break;
case c:
break;
}
....
}
我正在考虑使用(pair,command)的地图并删除这个开关案例,但有没有其他更好的解决方案或设计问题来解决这个问题?
答案 0 :(得分:8)
如果性能不这是一个大问题,那么函数指针的映射可能是一个解决方案。
假设标签A
,B
,C
...是小于255
的小整数值。
首先设置地图
#define KEY(a,b) ( (a<<8) | b )
std::map<int, function_pointer_type> dispatcher =
{
{ KEY(A,B), ab_handler},
{ KEY(A,C), ac_handler},
{ KEY(B,C), bc_handler},
//etc
};
使用map为每组输入调用适当的处理程序:
dispatcher[KEY(input1,input2)] (/* args */);
请注意,您必须使用每对可能的输入设置调度程序。此外,如果KEY(A,B)
和KEY(B,A)
对是相同的情况,那么您可以编写一个名为invoke
的函数来处理这种情况,以便为其余代码提供统一的用法。< / p>
void invoke(int input1, int input2, /* args */)
{
if (dispatcher.find(KEY(input1, input2)) != dispatcher.end() )
dispatcher[KEY(input1,input2)] (/* args */);
else
dispatcher[KEY(input2,input1)] (/* args */);
}
然后将其用作:
invoke(input1, input2, /* args */);
invoke(input2, input1, /* args */); //still okay!
希望有所帮助。
答案 1 :(得分:2)
在你的情况下,如何将两个开关分成两个功能
bool processInput2(char input2)
{
switch(input2)
{
case 'A':
{
// blah
}
break;
}
bool processInput1(char input1)
{
switch(input1)
{
case 'A':
processInput2(input2);
break;
}
答案 2 :(得分:1)
一种可能性是将代码拆分为每个嵌套大小写的一个函数,因此您的示例将具有6个函数:
void process_A_A() { ... }
void process_A_B() { ... }
void process_B_A() { ... }
void process_B_B() { ... }
void process_C_A() { ... }
void process_C_B() { ... }
然后,在初始化时将它们放入一个数组中,以便在运行时进行非常快速(恒定时间)的查找:
typedef std::function<void(void)> Function; // or: void (*)(void)
Function f[Input1Count][Input2Count];
f[A][A] = &process_A_A;
f[A][B] = &process_A_B;
...
要调用相应的函数,请写:
f[input1][input2]();
请注意,通过使用C ++ 11 std::function
类型,函数不必是经典函数指针;它们也可以是lambda函数或函子对象。
您还可以将某些部分保留为空或多次分配相同的功能。当你决定保留一些条目为空(所以在这种情况下不应该做任何事情),在调用之前检查函数对象:
if (f[input1][input2])
f[input1][input2]();
答案 3 :(得分:0)
您可以随时执行以下操作:
switch ( 256 * input1 + input2 ) {
case 256 * 'A' + 'B':
// ...
break;
// ...
};
但坦率地说,在这种情况下,我会发现嵌套开关更容易
理解,假设 switch
是正确的答案
你的问题。对于字符输入,它通常是,但有
其他替代方案,例如std::map<std::pair<char, char>,
Action const*>
,其中Action
是虚拟基类,以及
映射中的每个操作都是派生类的静态实例。
这具有使每个动作成为不同对象的优点
(这可能不是一个优势,取决于你在做什么
动作),如果地图是动态填充的(例如
在Action
)的构造函数中,您可以不添加操作
修改解析器的源代码(但您可能不需要
这种灵活性)。
答案 4 :(得分:0)
建议的答案与地图或指针处理功能表是可以的。但我看到两个缺点: 1)与手动嵌套交换机相比,性能略有下降。 2)案件处理方法不是完全自我描述的。我的意思是你必须两次提到每个句柄方法 - 在它的'定义和你初始化地图的地方。
我看到两个备选方案: 1)源代码生成。从某种表示自动生成嵌套开关。嗯...如果不介意为这么小的任务添加代码生成,那么创建最佳代码是非常好的选择。 2)使用预处理器黑客。不是最优雅但非常有趣的方式使它发挥作用。
首先我们为枚举声明X-Macro:
#define ELEMENTS(processor) \
processor(firstElement) \
processor(secondElement) \
processor(thirdElement)
我们可以用它来声明枚举本身:
#define ENUM_PROCESSOR(arg) arg,
enum class
{
ELEMENTS(ENUM_PROCESSOR)
};
#undef ENUM_PROCESSOR
Now we can add method that uses macros to generate nested switches:
void processElements(const Elements element1,
const Elements element2)
{
// These macros are useful to trick the preprocessor to allow recursive macro calls
// https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
#define EMPTY(...)
#define DEFER(...) __VA_ARGS__ EMPTY()
#define EXPAND(...) __VA_ARGS__
#define ELEMENTS_INT() ELEMENTS
#define PROCESS_NESTED_ENUM_VALUE(value) \
case Elements::value: \
{ \
process<Value1, Elements::value>(); \
break; \
}
#define PROCESS_ENUM_VALUE(value) \
case Elements::value: \
{ \
constexpr Elements Value1 = Elements::value; \
switch (element2) \
{ \
DEFER(ELEMENTS_INT)()(PROCESS_NESTED_ENUM_VALUE) \
}; \
\
break; \
}
switch (element1)
{
EXPAND(ELEMENTS(PROCESS_ENUM_VALUE));
};
#undef EMPTY
#undef DEFER
#undef EXPAND
#undef ELEMENT_TYPES_INT
#undef PROCESS_ENUM_VALUE
#undef PROCESS_NESTED_ENUM_VALUE
}
这里做了很多努力来“欺骗”预处理器以递归方式扩展ELEMENTS。主要观点有很好的描述here。
现在我们将处理程序声明为模板函数特化:
template <Elements Element1, Elements Element2>
void process();
template<>
void process<Elements::firstElement, Elements::firstElement>()
{
//some code 1;
}
...
答案 5 :(得分:-1)
为什么不使用分支机构?
if (input1 == A && input2 == B) {
} else if (input1==A && input2 = C) {
} ...
这是写你的意思。