3

我见过类似的线程,但是,不确定如何将解决方案准确地应用于我的案例。我的问题是我有一组用例让我们说'A','B','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) 的映射并删除此开关盒,但是是否有任何替代更好的解决方案或设计问题来解决此问题?

4

7 回答 7

9

如果性能不是一个大问题,那么函数指针映射可能是一种解决方案。

假设标签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
    };
    
  • 使用映射为每组输入调用适当的处理程序:

     dispatcher[KEY(input1,input2)] (/* args */);
    

请注意,您必须使用每对可能的输入来设置调度程序。此外,如果对KEY(A,B)KEY(B,A)是相同的情况,那么您可以编写一个调用invoke来处理这种情况的函数,以便为其余代码提供统一的用法。

 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!

希望有帮助。

于 2013-02-12T08:12:30.650 回答
2

在您的情况下,如何将两个开关分成两个功能

bool processInput2(char input2)
{
  switch(input2)
  {
   case 'A':
   {  
      // blah
   }
    break;
}

bool processInput1(char input1)
{
  switch(input1)
  {
   case 'A':
      processInput2(input2);
      break;
}
于 2013-02-12T08:05:52.530 回答
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++11std::function类型,函数不必是经典函数指针;它们也可以是 lambda 函数或仿函数对象。

您还可以将某些部分保留为空或多次分配相同的功能。当您决定将某些条目保留为空时(因此在这种情况下不应该做任何事情),请在调用之前检查函数对象:

if (f[input1][input2])
    f[input1][input2]();
于 2013-02-12T08:21:09.657 回答
0

你总是可以做类似的事情:

switch ( 256 * input1 + input2 ) {
case 256 * 'A' + 'B':
    //  ...
    break;
//  ...
};

但坦率地说,在这种情况下,我会发现嵌套开关更容易理解,假设switch是您问题的正确答案。对于字符输入,通常是这样,但还有其他选择,例如 a std::map<std::pair<char, char>, Action const*>,其中Action是虚拟基类,并且映射中的每个动作都是派生类的静态实例。这具有使每个动作成为不同对象的优点(这可能不是优势,取决于您在动作中执行的操作),并且如果地图是动态填充的(例如在 的构造函数中Action),您可以添加动作而不修改解析器的源代码(但您可能不需要这种灵活性)。

于 2013-02-12T08:45:31.867 回答
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。主要思想在这里得到了很好的描述。

现在我们将处理程序声明为模板函数特化:

template <Elements Element1, Elements Element2>
void process();

template<>
void process<Elements::firstElement, Elements::firstElement>()
{
    //some code 1;
}

...
于 2016-02-09T16:31:12.483 回答
-1

为什么不使用 if 分支?

if (input1 == A && input2 == B) {
} else if (input1==A && input2 = C) {
} ...

这是一种写你的意思。

于 2013-02-12T08:09:19.990 回答
-1

不是有一个数组或一个映射,参数列表可能会做(扔掉返回值,如果你不想要它)?

constexpr auto dispatch(auto const i, auto&& ...f)
  noexcept(noexcept((f(), ...)))
  requires(std::is_enum_v<std::remove_const_t<decltype(i)>>)
{
  using int_t = std::underlying_type_t<std::remove_cvref_t<decltype(i)>>;

  using tuple_t = std::tuple<decltype(f)...>;
  using R = decltype(std::declval<std::tuple_element_t<0, tuple_t>>()());

  return [&]<auto ...I>(std::integer_sequence<int_t, I...>)
    noexcept(noexcept((f(), ...)))
  {
    if constexpr(std::is_void_v<R>)
    {
      ((I == int_t(i) ? (f(), 0) : 0), ...);
    }
    else
    {
      R r{};

      ((I == int_t(i) ? (r = f(), 0) : 0), ...);

      return r;
    }
  }(std::make_integer_sequence<int_t, sizeof...(f)>());
}
于 2021-11-22T17:13:48.293 回答