C++、模板或指向成员函数的指针

C++, template or pointer to member function

我有一系列非常相似的成员函数,我想我可以使用模板或其他方法使我的代码更易于维护,但我不确定该怎么做。

这是我的功能之一的示例:

void CalController::bgc_cmd(const std::string& s) {
  try {
    this->cohort_ptr->md->set_bgcmodule(temutil::onoffstr2bool(s));

    LOG(note) << "CalController turned bgc module to " 
              << s <<" via cohort pointer...";

  } catch (const std::runtime_error& e) {
    LOG(warn) << e.what();
  }
}

我的其他功能完全相同,除了:

基本上我想避免在每个 CalController::XXX_cmd(...) 函数中重复 try..catch 块和 LOG(..) 消息。

使用boost::function 和/或boost::bind 就可以了,我只是在兜圈子,不知道如何设置。

您可以只编写一个成员函数来完成所有这些工作。不需要 bind 或模板,因为一切都是 md 上的函数,它采用相同的参数类型。我将使用 MD 作为 md 的类型,我假设 onoffstr2bool returns a bool:

void set_cmd(void (MD::*fn)(bool), const std::string& name, const std::string& s)
{
  try {
    (this->cohort_ptr->md->*fn)(temutil::onoffstr2bool(s));

    LOG(note) << "CalController turned " << name << " to " 
              << s <<" via cohort pointer...";

  } catch (const std::runtime_error& e) {
    LOG(warn) << e.what();
  }
}

你会这样称呼:

void CalController::bgc_cmd(const std::string& s) {
    set_cmd(&MD::set_bgcmodule, "bgc module", s);
}

我认为您可以通过简单的常规函数​​获得您想要的。不需要模板:

void CalController::do_cmd(boost::function<void (String)> fun, const std::string& s) {
  try {
    fun(temutil::onoffstr2bool(s));

    LOG(note) << "CalController turned bgc module to " 
              << s <<" via cohort pointer...";

  } catch (const std::runtime_error& e) {
    LOG(warn) << e.what();
  }
}

然后你可以让你的其他方法类似于:

void CalController::bgc_cmd(const std::string& s) {
    // here TypeOfMd is whatever the type is for this->cohort_ptr->md.
    // This binds the "this pointer" for set_bgcmodule to this->cohort_ptr->md
    do_cmd(boost::bind(&TypeOfMd::set_bgcmodule, this->chort_prt->md), s);
}

这里有几点需要注意:

  1. 使用 C++11 lambda 和新函数 类 boost 不是必需的
  2. 我认为以上方法可行,但我不确定节省几行代码是否值得额外的复杂性和可读性的损失。它也可能难以维护,因为您想对每种方法进行细微的更改,例如稍微不同的日志消息。
  3. 我已经有一段时间没有写任何 C++ 了,甚至更长时间没有做任何提升的东西,所以虽然我认为以上是正确的一般想法,但如果它真的编译了我会感到惊讶。

如果使用 C++11,您可以创建一个具有更通用名称的函数,比方说 exex_cmd。 然后您可以将 lambda 函数作为参数传递并在 try/catch 块内执行它 - 无需使用模板。

//WARNING: Untested code - the point is that you get the idea. Modify at will.
void CalController::exec_cmd(const std::function<void (void)> func) {
  try {
    //Invoke lambda.
    //The string is passed via closure implementation,
    //but you can always pass it as normal argument if you want.
    func();

    LOG(note) << "CalController turned bgc module to " 
              << s <<" via cohort pointer...";

  } catch (const std::runtime_error& e) {
    LOG(warn) << e.what();
  }
}

然后,创建 3 个包装器方法,调用 exec_cmd,但将不同的 lambda 作为参数传递:

  void CalcController::bgc_cmd(const std::string& s){
    CalcController::exec_cmd([=] () {
      //Taking closures into control, to avoid passing the string as argument.
      this->cohort_ptr->md->set_bgcmodule(temutil::onoffstr2bool(s));
    })
  }

  void CalcController::env_cmd(const std::string& s){
    CalcController::exec_cmd([=] () {
       //different function invocation here.
    })
  }

同样,您可以为所有功能执行此操作。

您可以查看 here 以了解有关 C++11 的 lambda 的更多信息。

普通 C++ 中的一种类似方法是定义一个函数指针类型并将函数指针传递给您的 exec_cmd,按照类型签名创建普通函数并将它们作为参数传递,或者传递一个成员函数指针 - 你可以看看 Barry 的回答。