Home > Enterprise >  Preferred way to assign a class member of type std::function during runtime?
Preferred way to assign a class member of type std::function during runtime?

Time:11-05

I want to store function pointers to members functions in a std::function object. They all have the same signature. The condition, stored in the member status_, for which a member function should be called is found at runtime from a config file or CLI flag. However, status_ is set once during initialization and doesn't change after that. But the member functions are called many times, maybe around 1000.

class Product
{

public:

        Product(int status);

        void runJob(const ProductState& state) ;

        // The possible jobs
        // ----------------------------------
        void create(const ProductState& state) ;

        void repair(const ProductState& state) ;

        void deploy(const ProductState& state) ;

        void change(const ProductState& state) ;

private:

        int status_ ;
        std::function<void(const ProductState&)> jobRunner_ ;
};

Which is the preferred way to store the function?

  1. Using lambdas
// In the Product(int status) constructor
Product::Product(int status)
{
        status_ = status ;

        if (status_ == 0) {
          jobRunner_ = [this](const ProductState& state) {this->create(state);} ;
        } else if (status_ == 1) {
          jobRunner_ = [this](const ProductState& state) {this->repair(state);} ;
        } else if (status_ == 2) {
          jobRunner_ = [this](const ProductState& state) {this->deploy(state);} ;
        }
}

void Product::runJob(const ProductState& state)
{
        jobRunner_(state) ;
}

Or

  1. using std::bind and std::placeholders
// In the Product(int status) constructor
Product::Product(int status)
{
        status_ = status ;

        if (status_ == 0) {
          jobRunner_ = std::bind(&Product::create, this, std::placeholders::_1);
        } else if (status_ == 1) {
          jobRunner_ = std::bind(&Product::repair, this, std::placeholders::_1);
        } else if (status_ == 2) {
          jobRunner_ = std::bind(&Product::deploy, this, std::placeholders::_1);
        }
}

void Product::runJob(const ProductState& state)
{
        jobRunner_(state) ;
}

Another options is check the status_ everytime inside runJob() which removes the need for the jobRunner_ member but I believe it is slower as it is checked 1000 times as compared to only checking it once during initialization.

void Product::runJob(const ProductState& state)
{
        if (status_ == 0) {
          create(state);
        } else if (status_ == 1) {
          repair(state);
        } else if (status_ == 2) {
          deploy(state);
        }
}

CodePudding user response:

Don't use std::bind unless you need to. Its the ancient way to do things that now you can do much simpler with lamdba expressions.

Don't use std::function whenver you need to store a function. std::functions main purpose is to do type-erasure. One std::function can store any kind of callables, free function, lambdas, and more. If you want to store different callables that are all of the same type, you do not need std::function.

As mentioned in a comment you should consider to use a pointer to member function:

struct ProductState {};

class Product {

public:
        Product(int status) ;
        void runJob(const ProductState& state) ;
        void create(const ProductState& state) ;
        void repair(const ProductState& state) ;
        void deploy(const ProductState& state) ;
        void change(const ProductState& state) ;

private:

        int status_ ;
        using job_type = void(Product::*)(const ProductState&);
        job_type job;
};


Product::Product(int status) {
        status_ = status ;

        if (status_ == 0) {
          job = &Product::repair;
        } else if (status_ == 1) {
          job = &Product::create;
        } else if (status_ == 2) {
          job = &Product::deploy;
        } else {
          job = &Product::change;
        }
}

void Product::runJob(const ProductState& state)
{
        (this->*job)(state);
}

And if you actually do need std::function, using a lambda is fine.

  •  Tags:  
  • c
  • Related