简体   繁体   中英

Executing function pointer from a in std::vector iterator

I'm trying to write an application that adds function pointers to a vector based on some external event, then iterates through the list. For some reason, I can't seem to get the functions to actually execute despite the fact that they are added to the vector.

What am I missing?

main.cpp

#include "GameLogic.h"

GameLogic* gameLogic = nullptr;

int main(int argc, char* argv[])
{
    gameLogic = new GameLogic();
    gameLogic->run();
}

GameLogic.cpp

#include "GameLogic.h"

GameLogic::GameLogic()
{
    FireAction.push_back(&GameLogic::AddCredit);
    FireAction.push_back(&GameLogic::AddPlayer);
}

GameLogic::~GameLogic() {}

void GameLogic::run()
{

    for (std::vector<std::function<void(const GameLogic)>>::iterator it = FireAction.begin(); it != FireAction.end(); it++)
    {
        std::cout << "Iterator Loop" << std::endl;
        (*it);
    }
}

void GameLogic::AddCredit() { std::cout << "Added Credit" << std::endl; }
void GameLogic::AddPlayer() { std::cout << "Added Player" << std::endl; }

GameLogic.h

#pragma once
#include <functional>
#include <iostream>
#include <vector>

class GameLogic
{
public:
    GameLogic();
    ~GameLogic();
    void run();

private:
    void AddCredit();
    void AddPlayer();

    std::vector<std::function<void(const GameLogic)>> FireAction;
};

Execution Output:

Iterator Loop
Iterator Loop

Expected Output:

Iterator Loop
Added Credit
Iterator Loop
Added Player

You are dereferencing the vector iterators to access the stored std::function objects, but you are not actually calling their operator() to execute your class methods. You need to add an extra set of parenthesis for those calls, eg:

(*it)(*this);

This is made clearer if you store the std::function s to a local variable before calling them, eg:

std::function<void(const GameLogic)> &func = *it;
// better: auto &func = *it;
func(*this);

Now, that being said, your std::function objects need to take your GameLogic object by pointer or reference, instead of by value, eg:

std::vector<std::function<void(const GameLogic&)>> FireAction;
...
(*it)(*this);
// or:
// auto &func = *it;
// func(*this);

Or:

std::vector<std::function<void(const GameLogic*)>> FireAction;
...
(*it)(this);
// or:
// auto &func = *it;
// func(this);

You can avoid this requirement by storing lambdas instead of pointer-to-member's, so you don't have to pass around the GameLogic object explicitly:

std::vector<std::function<void()>> FireAction;
...
FireAction.push_back([this](){ this->AddCredit(); });
FireAction.push_back([this](){ this->AddPlayer(); });
...
(*it)();
// or:
// auto &func = *it;
// func();

Lastly, to simply the code even further, consider using auto (as seen above) and range-based for loops, eg:

void GameLogic::run()
{
    for (auto it = FireAction.begin(); it != FireAction.end(); ++it)
    {
        std::cout << "Iterator Loop" << std::endl;
        (*it)();
        // or:
        // auto &func = *it;
        // func();
    }
}

Or:

void GameLogic::run()
{
    for (auto &func : FireAction)
    {
        std::cout << "Iterator Loop" << std::endl;
        func();
    }
}

If you expect (*it) to call a function pointer, you're mistaken.

You should write: (*it)(...parameters...) instead, that will get you closer. The parameter needs to be the GameLogic object that you expect those member functions to be called upon.

Note: your std::function<void(const GameLogic)> should probably be std::function<void(const GameLogic &)> or std::function<void(GameLogic &)> unless you like making copies of things.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM