4

I'm trying to code a Gameboy emulator and i would like to use a vector of function pointers to call the right function instead of doing a long switch statement.

For example if the program counter point to 0x00 (in memory), the first element of the vector is NOP so void NOP() is called; but i can't figure how to call the functions.

Z80.h

#include <vector>
using namespace std;

class Z80;
typedef void (Z80::*function_t)();

class Z80
{
public:
  vector<function_t> fmap;
...
...
};

Z80.cpp

Z80::Z80()
{
    fmap = { &Z80::NOP, &Z80::LDBCnn, &Z80::LDBCmA};
}

void Z80::emulateCycle() {
    opcode = memory.readByte(r.pc);
    fmap[opcode]();     <---ERROR
    r.pc++;
}

void Z80::NOP() {

}

this is the error:

IntelliSense: expression preceding parentheses of apparent call must have (pointer-to-) function type
Barry
  • 286,269
  • 29
  • 621
  • 977
Matteo
  • 171
  • 1
  • 5
  • 15
  • 2
    A pointer to a member function is not the same as a pointer to a non-member function, you have to keep that in mind, And to help with that problem I recommend you read about [`std::function`](http://en.cppreference.com/w/cpp/utility/functional/function) and [`std::bind`](http://en.cppreference.com/w/cpp/utility/functional/bind). – Some programmer dude Jan 25 '15 at 16:28
  • As for *why* a member function is not the same as a non-member function, it's because in a member function the `this` pointer is actually a hidden and implicit first argument. That means when you skip the Intellisense problem and build and run your code, you will have [*undefined behavior*](http://en.wikipedia.org/wiki/Undefined_behavior) because you do not provide that implicit `this` pointer when calling your member functions. – Some programmer dude Jan 25 '15 at 16:31

2 Answers2

5

This expression:

fmap[opcode]

gives you a pointer to a member function. You can't just call that - it needs the class instance too. But you're actually calling it from a class method itself - so this is the instance you're looking for:

(this->*fmap[opcode])();

Note that if you want to avoid that bit of syntax and you're using C++11, you can change your fmap to instead be a vector of std::function<void()> and initialize it thusly:

fmap = { std::bind(&Z80::NOP, this),    // or [this](){ this->NOP(); }
         std::bind(&Z80::LDBCnn, this), // etc.
         std::bind(&Z80::LDBCmA, this)};

That will let you actually do:

fmap[opcode]();
Barry
  • 286,269
  • 29
  • 621
  • 977
  • I can imagine this being prohibitively slow, though. – Lightness Races in Orbit Jan 25 '15 at 16:35
  • @LightnessRacesinOrbit Never timed it, so don't know. Usual caveats about always profiling your code apply. Just throwing it out there as an option for completeness' sake. [Mats](http://stackoverflow.com/a/28138662/2069064)' suggestion of a switch intuitively seems like it'd be fastest but who knows :) – Barry Jan 25 '15 at 16:39
  • I have written a few instruction simulators and similar, and either used switch, or basic function pointers - not sure how much extra overhead there is in the function and bind solution - in a good implementation of compiler, one would hope that it's minimal. – Mats Petersson Jan 25 '15 at 16:42
  • For those not clear on what is `->*`operator and why that extra parenthesis is required in `(this->*fmap[opcode])()` - please read http://stackoverflow.com/a/9188567/1180117 – kiranpradeep Jan 26 '15 at 08:26
  • @LightnessRacesinOrbit : please explain why this would be prohibitively slow? – user12066 Apr 30 '20 at 07:38
5

I'm not entirely sure that using function pointer in this case is particularly much better than for example a big switch statement.

However, the reason you can't call your member function is that you are not passing your object to the function.

You need this;

(this->*fmap[opcode])();

Another option is to use static/free function pointers, like this:

 void (*function_t)(Z80& self); 

and call it with:

 fmap[opcode](this). 

[Or use std::function and std::bind, which covers over the rather (intentionally, apparently) ugly syntax]

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227