我的程序从用户那里获得了两个 bool 变量,它们的值此后将不会改变。每个 bool 变量都启用一部分代码。像这样的东西:

#include <iostream>

void callback_function(bool task_1, bool task_2, bool task_3) {
  if (task_1) {
    std::cout << "Running task 1" << std::endl;
  }
  if (task_2) {
    std::cout << "Running task 2" << std::endl;
  }
  if (task_3) {
    std::cout << "Running task 3" << std::endl;
  }
}

int main() {
  bool task_1 = true;
  bool task_2 = false;
  bool task_3 = true;

  while (true) {
    callback_function(task_1, task_2, task_3);
  }

  return 0;
}

现在我的问题是,由于每次程序调用callback_function()时 bool 变量都是固定的,有没有办法避免回调函数中的if语句?

这是避免运行时检查的一种方法(为 bool 变量的所有排列实现回调函数---仅在以下两种情况下显示):
#include <functional>
#include <iostream>

void callback_function_for_tasks_1_2_3() {
  std::cout << "Running task 1" << std::endl;
  std::cout << "Running task 2" << std::endl;
  std::cout << "Running task 3" << std::endl;
}

void callback_function_for_tasks_1_3() {
  std::cout << "Running task 1" << std::endl;
  std::cout << "Running task 3" << std::endl;
}

int main() {
  bool task_1 = true;
  bool task_2 = false;
  bool task_3 = true;

  std::function<void()> callback_function;
  if (task_1 && task_2 && task_3) {
    callback_function = callback_function_for_tasks_1_2_3;
  } else if (task_1 && !task_2 && task_3) {
    callback_function = callback_function_for_tasks_1_3;
  }

  while (true) {
    callback_function();
  }

  return 0;
}

问题是,如果有2^n bool 变量,我必须实现n不同的回调函数。有没有更好的方法可以做到这一点?

最佳答案

确保在编译时评估if语句

C++ 17引入了if constexpr,它可以做到这一点:

template<bool task_1, bool task_2, bool task_3>
void callback_function() {
  if constexpr (task_1) {
    std::cout << "Running task 1" << std::endl;
  }
  if constexpr (task_2) {
    std::cout << "Running task 2" << std::endl;
  }
  if constexpr (task_3) {
    std::cout << "Running task 3" << std::endl;
  }
}

如果启用了优化,则不需要if constexpr即使您使用常规的if而不是if constexpr,因为现在已经对 bool 进行了模板化,所以编译器将能够完全消除if语句,而只需运行任务即可。 If you look at the assembly produced here,即使在-O1上,您也会看到,任何callback函数中都没有if语句。

现在,我们可以直接将callback_function用作函数指针,避免使用function<void()>:
int main() {
  using callback_t = void(*)();
  callback_t func = callback_function<true, false, true>;

  // Do stuff with func
}

我们还可以通过将bool分配给constexpr变量来命名它们:
int main() {
  using callback_t = void(*)();
  constexpr bool do_task1 = true;
  constexpr bool do_task2 = false;
  constexpr bool do_task3 = true;
  callback_t func = callback_function<do_task1, do_task2, do_task3>;

  // Do stuff with func
}

自动创建所有可能的回调函数的查找表

您提到了在运行时在不同的回调函数之间进行选择。我们可以使用查找表轻松完成此操作,并且可以使用模板自动创建所有可能的回调函数的查找表。

第一步是从特定索引获取回调函数:
// void(*)() is ugly to type, so I alias it
using callback_t = void(*)();

// Unpacks the bits
template<size_t index>
constexpr auto getCallbackFromIndex() -> callback_t
{
    constexpr bool do_task1 = (index & 4) != 0;
    constexpr bool do_task2 = (index & 2) != 0;
    constexpr bool do_task3 = (index & 1) != 0;
    return callback_function<do_task1, do_task2, do_task3>;
}

一旦做到这一点,我们就可以编写一个函数来根据一堆索引创建一个查找表。我们的查询表将只是一个std::array
// Create a std::array based on a list of flags
// See https://en.cppreference.com/w/cpp/utility/integer_sequence
// For more information
template<size_t... Indexes>
constexpr auto getVersionLookup(std::index_sequence<Indexes...>)
    -> std::array<callback_t, sizeof...(Indexes)>
{
    return {getCallbackFromIndex<Indexes>()...};
}

// Makes a lookup table containing all 8 possible callback functions
constexpr auto callbackLookupTable =
    getVersionLookup(std::make_index_sequence<8>());

在这里,callbackLookupTable包含所有8种可能的回调函数,其中callbackLookupTable[i]扩展了i的位以获取回调。例如,如果i == 6,则i的位是二进制的110,因此
callbackLookupTable[6]callback_function<true, true, false>
在运行时使用查找表

使用查找表非常简单。我们可以通过移位从一串bool中获取索引:
callback_t getCallbackBasedOnTasks(bool task1, bool task2, bool task3) {
    // Get the index based on bit shifting
    int index = ((int)task1 << 2) + ((int)task2 << 1) + ((int)task3);
    // return the correct callback
    return callbackLookupTable[index];
}

演示如何读入任务的示例

我们现在可以在运行时获取bool,只需调用getCallbackBasedOnTasks即可获取正确的回调
int main() {
    bool t1, t2, t3;
    // Read in bools
    std::cin >> t1 >> t2 >> t3;
    // Get the callback
    callback_t func = getCallbackBasedOnTasks(t1, t2, t3);
    // Invoke the callback
    func();
}

关于c++ - 如何避免对编译后无法访问的正在运行的代码部分进行运行时检查?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55438306/

10-16 19:28