5

I am trying to build an efficient Rock, Paper, Scissors simulator between 2 computer "players." A suggestion I saw here was to use a matrix to store the possible outcomes. I really liked this idea as it means I wouldn't have to have 9 different if() statements to account for all possible values. Whenever a match is completed I have functions to iterate the number of Wins, Losses, and Draws of each player. Thus, I thought it would be great if I could build a 2-dimensional array of function pointers so that [0][0] represents a throw of "Rock" by both parties and would result in a Draw, throwing the function addDraw() for each player. I found an example of a 1-dimensional array of function pointers which works here.

My code is throwing several errors when I try to compile. Let me know if anything else is needed.

Function to call the result:

void Player::result(vector<Player*> &vals, int x, int y)
{
    Player *p1 = vals[0];
    Player *p2 = vals[1];

    void(*results1[3][3]) =
    {
        { p1->addDraws, p1->addWins, p1->addLosses },
        { p1->addLosses, p1->addDraws, p1->addWins },
        { p1->addWins, p1->addLosses, p1->addDraws }
    };
}

Functions to add Wins, Losses, and Draws:

void Player::addWins()
{
    wins++;
}

void Player::addLosses()
{
    losses++;
}

void Player::addDraws()
{
    draws++;
}

All functions are initialized in Player.h and declared in Player.cpp (I think that's the right terms). The most common error I am receiving is "error C2440: 'initializing' : cannot convert from 'void (__thiscall Player::* )(void)' to 'void *'

Community
  • 1
  • 1
Spencer
  • 453
  • 4
  • 21
  • All functions are declared in Player.h and defined in Player.cpp. This is correct. – Venkatesh Nov 21 '14 at 06:50
  • You are declaring a 3x3 array of pointers to `void`, what you probably meant was `void(Player::* results1[3][3])();` (assuming the functions aren't `static`). If you really want to go this route while not losing your sanity you should `typedef` the member function pointer type first. You'll also need to provide an object when using the pointers. – user657267 Nov 21 '14 at 07:02

3 Answers3

2

You have an array of void pointers, not function pointers, and member functions are not function pointers.

You'll need something like

typedef void (Player::*Score)();  // typedefs and function types go together well
Score scores[3][3]) =
{
    { &Player::addDraws, &Player::addWins, &Player::addLosses },
    { &Player::addLosses, &Player::addDraws, &Player::addWins },
    { &Player::addWins, &Player::addLosses, &Player::addDraws }
};

// Calling
Player a;
(a.*scores[0][0])();

or, use free functions like this:

void addWins(Player* p)
{
    p->addWins();
}

// Similar for the others...

typedef void (*Score)();
Score scores[3][3] = {{ addWins, .... 

//
Player* player = ...
scores[0][0].addWins(player);

or, you can be modern and use std::function.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • I wish I were sophisticated enough in the use of c++ to know how to use std:function. – Spencer Nov 21 '14 at 07:32
  • Been searching the internet for solutions to a related problem, this is the simplest one by a long, long shot (and also working). I've ommited the typedef, because I found it redundant though. – Captain Trojan Oct 30 '20 at 22:17
-1
void (*results1[3][3]) = {
    { p1->addDraws, p1->addWins, p1->addLosses },
    { p1->addLosses, p1->addDraws, p1->addWins },
    { p1->addWins, p1->addLosses, p1->addDraws } };

I think this should be

void (*results1[3][3])() = {
    { p1->addDraws, p1->addWins, p1->addLosses },
    { p1->addLosses, p1->addDraws, p1->addWins },
    { p1->addWins, p1->addLosses, p1->addDraws } };

Or, you can use typedef

typedef void (*func)();
func results1[3][3] = {
        { p1->addDraws, p1->addWins, p1->addLosses },
        { p1->addLosses, p1->addDraws, p1->addWins },
        { p1->addWins, p1->addLosses, p1->addDraws } };
Min Fu
  • 789
  • 1
  • 6
  • 16
  • Thanks, Min Fu. Though that still throws: error C2440: 'initializing' : cannot convert from 'overloaded-function' to 'void (__cdecl *)(void)' – Spencer Nov 21 '14 at 07:14
-1

In C++ a method is not a function. If your counters are in a class instance then the code called will need to know the this to act on.

To solve the problem there are a few ways:

  1. Instead of using a void (*)() function use a void (*)(MyClass *) and pass the function the instance you want to update

  2. Use pointers to members. This under the hood is basically the same but the syntax is a bit weird; the type is void (MyClass::*)() and to make a call using a member pointer the syntax would use the strange ->* operator for example with (this->*m[i][j])().

  3. Use a closure. This requires C++11 and is somewhat cleaner from a syntax point of view; instead of using a matrix of pointers to function you use a matrix of std::function<void()> objects that are capturing the object instance. C++ closures cannot handle the upward funarg problem but in your case this shouldn't be an issue (seems to me that having one of the closures surviving the instance holding the counters is not going to be needed).

In the very specific case however I wouldn't use pointers to functions at all, but instead a matrix of integers with the result (0, 1, 2) and then to update the result the code is just

results[m[i][j]]++;

with no ifs and no call involved. In this idea results is an array of three integers with counters of wins for A, wins for B and ties.

6502
  • 112,025
  • 15
  • 165
  • 265