tbc
tbc

Reputation: 1749

C++, template or pointer to member function

I have a series of member functions that are all very similar, and I think I might be able to make my code more maintainable with a template or some other approach, but I am not sure how to do it.

Here is an example of one of my functions:

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();
  }
}

My other functions are identical except for:

Essentially I'd like to avoid having to duplicate the try..catch block and LOG(..) message in each of my CalController::XXX_cmd(...) functions.

Using boost::function and or boost::bind would be fine, I am just going in circles and can't figure out how to set this up.

Upvotes: 1

Views: 62

Answers (3)

Nick Louloudakis
Nick Louloudakis

Reputation: 6005

If using C++11, You can create a function with a more generic name, let's say exex_cmd. You can then pass a lambda function as argument and execute it inside the try/catch block - no need for a template use.

//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();
  }
}

Then, create 3 wrapper methods, invoking exec_cmd, but passing a different lambda as argument:

  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.
    })
  }

Similarly, you can do it for all your functions.

You can look here for more about C++11's lambdas.

A similar approach in plain-old C++ is to define a function pointer type and pass a function pointer to your exec_cmd, create normal functions following the type signature and pass them as arguments, or pass a member function pointer - You can look at Barry's answer for that.

Upvotes: 1

Oliver Dain
Oliver Dain

Reputation: 9953

I think you can get what you want with a simple, regular function. No need for a template:

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();
  }
}

Then you can make your other methods something like:

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);
}

A few things to note here:

  1. With C++11 lambdas and the new function classes boost isn't necessary
  2. I think the above works, but I'm not sure that saving a few lines of code is worth the extra complexity and loss of readability. It may also get hard to maintain as you want to make minor changes like slightly different log messages for each method.
  3. It's been a while since I wrote any C++ and even longer since I did any boost stuff so while I think the above is the right general idea, I'd be surprised if it actually compiled.

Upvotes: 1

Barry
Barry

Reputation: 302852

You could just write a member function to do all that stuff. No bind or template necessary since everything is a function on md that takes the same argument type. I'm going to use MD as the type of md, and I'm assuming 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();
  }
}

Which you would then call like:

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

Upvotes: 2

Related Questions