ryantuck
ryantuck

Reputation: 6644

C++ Passing Function Pointer to Function with Templates

I've got a current system of code that works pretty well. I want to make a part of it more efficient.

I've got a class, Foo, and a ton of subclasses of Foo that do different things (in this example, I'm using Bar as the only subclass).

I define how Bar "updates" during each loop iteration of my main program by declaring a series of steps in its constructor. This is cool, because I can add multiple steps that each run for different numbers of iterations.

The problem is that the process of adding a step to a Bar is redundant, and I would like to make it less so.

Here's what my class declarations look like:

template <class T>
class Step {
    public:
        Step();
        void (T::*fnPtr)(); // function to execute when step is run within a Foo
        int count;          // number of iterations to execute on
}

class Foo {
    public:
        Foo();
        void doSomething();

        template <class T>
        void addStep(Step<T>* newStep) {
            // adds a new step to a linked list
        }
}

class Bar : public Foo {
    public:
        Bar();
        void doAnotherThing();
        void doYetAnotherThing();
        void omgAnotherThing();
}

my Bar constructor would look something like this:

Bar::Bar() {
    // ah four lines of code (at least!) every time I want to add a step!

    Step<Bar>* a = new Step<Bar>;
    a->fnPtr = &Bar::doAnotherThing;
    a->count = 10;
    addStep(a);

    Step<Bar>* b = new Step<Bar>;
    b->fnPtr = &Bar::doYetAnotherThing;
    b->count = 20;
    addStep(b);

    Step<Bar>* c = new Step<Bar>;
    c->fnPtr = &Bar::omgAnotherThing;
    c->count = 6;
    addStep(c);
}

Ideally, I'd like the Bar's step creation look something like this:

Bar::Bar() {
    // very nice! I like!

    addANewStep(&Bar::doAnotherThing,10);
    addANewStep(&Bar::doYetAnotherThing,20);
    addANewStep(&Bar::omgAnotherThing,30);
}

But I am not quite sure, in particular, how to pass the &Bar::doAnotherThing to a function.

Any suggestions?

Upvotes: 1

Views: 120

Answers (3)

Loki Astari
Loki Astari

Reputation: 264411

The easy first Step is to add a constructor to Step.

template<typename T>
class Step {
   public:
       typedef   void (T::*Action)();

        Step(Action a, int c): action(a), count(c) {}
        Action  action;         // function to execute when step is run within a Foo
        int     count;          // number of iterations to execute on
};

Now your calls are:

addANewStep(new Step(&Bar::doAnotherThing,10));
addANewStep(new Step(&Bar::doYetAnotherThing,20));
addANewStep(new Step(&Bar::omgAnotherThing,30));

Then we notice all the calls to new. With no ownership symantics associated with the resulting pointer. We could add std::unique_ptr<Step> into the mix. But this class is so simple I think the best solution is to just make the interface take a Step object:

So change I would look at addStep()

         template <class T>
         void addStep(Step<T>* newStep);

We see you are using pointers because you don't have a homogenous interface (so need the pointers). But the interface is simple so I would still pass as an object and put all the memory management internal to Foo class (I could alternatively be persuaded that std::unique_ptr<Step<T>> is an alternative).

         template <class T>
         void addStep(Step<T> const& newStep) {
             pointer_container.push_back(new Step<T>(newStep));
         }

This makes your calls look like this:

addANewStep(Step(&Bar::doAnotherThing,10));
addANewStep(Step(&Bar::doYetAnotherThing,20));
addANewStep(Step(&Bar::omgAnotherThing,30));

You now just need to make sure that pointer_container takes ownership of the pointers.

The next step I would take is look to see if you can replace Step<T> with std::function<void()>

Upvotes: 0

Constructor
Constructor

Reputation: 7473

You may implement addANewStep method (of Foo class as I understand) in the following manner:

template <class T>
void addANewStep(void (T::*fnPtr)(), int count)
{
    Step<T>* step = new Step<T>;

    step->fnPtr = fnPtr;
    step->count = count;
    addStep(step);
}

And don't forget about semicolon after class definition.

Upvotes: 1

typ1232
typ1232

Reputation: 5607

class Foo {
        std::vector<
            std::pair<std::function<void()>, int>
        > m_steps;
    public:
        Foo();
        void doSomething();

        template <class F>
        void addStep(F callback, int count) {
            // add step
            m_steps.emplace_back(callback, count);
        }
}

Calling the function:

addStep(std::bind(&Bar::doAnotherThing, this), 10);

Passing thisis neccessary so the functor knows on which instance the member function should be called.

Upvotes: 0

Related Questions