News:

11 March 2016 - Forum Rules

Main Menu

C++ Member Function Pointers

Started by RedComet, August 10, 2011, 07:14:23 PM

Previous topic - Next topic

RedComet

I've been working on rewriting SEKAS in C++ and making it all object oriented so it's easy to manage and I've hit a snag. Originally I used the opcodes as an index to an array of function pointers to call the appropriate function to assemble the opcode and operands.

Now I've made all of the code generating functions private member functions of the M68K_Opcode class and I'm attempting to do something similar. I've created an array of function pointers to all of the code generating functions and then I've created another array with all of the valid opcode mnemonics. The code gets the opcode, runs through the list of mnemonics until it finds the correct one, then it's supposed to call the associated function from the function pointer array. Problem is Visual Studio keeps giving me the following:

error C2064: term does not evaluate to a function taking 0 arguments

for this line:

*__ops[i]();

I've spent the past few hours reading and re-reading things online but nothing has been particularly helpful. So, anybody care to help? Or maybe there's an easier way to implement what I want. I'm open to ideas. The whole reason I'm rewriting this thing is so it's easier to maintain and fix bugs and for some much need OOP practice.


bool M68K_Opcode::assemble()
{
const unsigned int OPCODE_COUNT = 71;
//A sort of jump table to the code generating functions
void (M68K_Opcode::*__ops[OPCODE_COUNT])(void) =
{
&M68K_Opcode::abcd, &M68K_Opcode::adda, &M68K_Opcode::addi,
&M68K_Opcode::addq, &M68K_Opcode::addx, &M68K_Opcode::add,
&M68K_Opcode::andi,     &M68K_Opcode::and, &M68K_Opcode::asl,

&M68K_Opcode::asr, &M68K_Opcode::bchg, &M68K_Opcode::bclr,
&M68K_Opcode::bra, &M68K_Opcode::bset,     &M68K_Opcode::bsr,
&M68K_Opcode::btst, &M68K_Opcode::chk, &M68K_Opcode::clr,

&M68K_Opcode::cmpa, &M68K_Opcode::cmpi, &M68K_Opcode::cmpm,
&M68K_Opcode::cmp, &M68K_Opcode::divs, &M68K_Opcode::divu,
&M68K_Opcode::eori, &M68K_Opcode::eor, &M68K_Opcode::exg,

&M68K_Opcode::ext, &M68K_Opcode::jmp, &M68K_Opcode::jsr,
&M68K_Opcode::lea, &M68K_Opcode::link, &M68K_Opcode::lsl,
&M68K_Opcode::lsr, &M68K_Opcode::movea, &M68K_Opcode::movem,

&M68K_Opcode::movep, &M68K_Opcode::moveq, &M68K_Opcode::move,
&M68K_Opcode::muls, &M68K_Opcode::mulu, &M68K_Opcode::nbcd,
&M68K_Opcode::neg, &M68K_Opcode::negx, &M68K_Opcode::nop,

&M68K_Opcode::not, &M68K_Opcode::ori, &M68K_Opcode::or,
&M68K_Opcode::pea, &M68K_Opcode::reset,   &M68K_Opcode::rol,
&M68K_Opcode::ror, &M68K_Opcode::roxl, &M68K_Opcode::roxr,

&M68K_Opcode::rte, &M68K_Opcode::rtr, &M68K_Opcode::rts,
&M68K_Opcode::sbcd, &M68K_Opcode::stop, &M68K_Opcode::suba,
&M68K_Opcode::subi, &M68K_Opcode::subq, &M68K_Opcode::subx,

&M68K_Opcode::sub, &M68K_Opcode::swap, &M68K_Opcode::tas,
&M68K_Opcode::trapv    ,&M68K_Opcode::trap, &M68K_Opcode::tst,
&M68K_Opcode::unlk, &M68K_Opcode::illegal
};

//instruction opcodes:
//these are used as indexes to call the correct function from __ops[]
char __ops_indexes[OPCODE_COUNT][8] =
{
"abcd", "adda", "addi", "addq", "addx", "add", "andi", //7
"and", "asl", "asr", "bchg", "bclr", "bra", "bset", //14
"bsr", "btst", "chk", "clr", "cmpa", "cmpi", "cmpm", //21
"cmp", "divs", "divu", "eori", "eor", "exg", "ext", //28
"jmp", "jsr", "lea", "link", "lsl", "lsr", "movea", //35
"movem","movep","moveq","move", "muls", "mulu", "nbcd", //42
"neg", "negx", "nop", "not", "ori", "or", "pea", //49
"reset","rol", "ror", "roxl", "roxr", "rte", "rtr", //56
"rts", "sbcd", "stop", "suba", "subi", "subq", "subx", //63
"sub", "swap", "tas", "trapv","trap", "tst", "unlk", //70
"illegal"
};

bool success = true; //set to false if an error occurs
string mnemonic = opcode; //this will equal opcode with the size field removed

//first check the size field
set_size();

if(opsize != NONE && opsize != INVALID)
{
//remove the size field
int pos = opcode.find('.');
//we can assume that if an error wasn't flagged by set_size()
//then a '.' is present in the opcode
mnemonic.erase(pos);
}

//next use the mnemonic to determine which opcode function to call
if(opsize != INVALID)
{
//first convert all upper-case letters to lower-case
for(unsigned int i = 0; i < mnemonic.length(); i++)
{
if(mnemonic[i] >= 'A' && mnemonic[i] <= 'Z')
{
//ASCII upper-case letters are 0x20 more than their
//lower-case equivalent
mnemonic[i] += 0x20;
}
}

//next use the mnemonic as an index and call the code generating function
for(unsigned int i = 0; i < OPCODE_COUNT; i++)
{
if(!mnemonic.compare(__ops_indexes[i]))
{
//call the appropriate function and generate binary data
*__ops[i]();
}
}
}

return success;
}


And all of the opcode functions are declared as void with no parameters:


void exg();
void lea();
void link();
void move();
Twilight Translations - More than just Dragonball Z. :P

Klarth

Quote from: RedComet on August 10, 2011, 07:14:23 PM
error C2064: term does not evaluate to a function taking 0 arguments
*__ops[i]();
I've never had the need for member pointer functions, but my guess is because you aren't referencing the class itself.  Try using it like (yourclass.*__ops)().  If you want to use it like __ops(), then you need to implement the class as a singleton and grab the class reference from each function (changed to static functions, not member functions).

RedComet

I tried using that and still get the same error. Could you give me an example of what you're talking about? I'm not sure I understand.
Twilight Translations - More than just Dragonball Z. :P

Klarth

Quote from: RedComet on August 10, 2011, 08:01:54 PM
I tried using that and still get the same error.
Oh, I see you're using your array of member pointer functions from within the same class.  I think this will work for you then:
(this->*__ops[i])();
So what I meant before.  When you call a member function, you're using __thiscall.  Which means, for example, your assemble function isn't really 0 parameters (at the x86 level).  The compiler pushes a pointer the current class as a parameter.   As such, your __ops() didn't work because the compiler needed a pointer to your M68K_Opcode class and didn't know how to do it.  Which is where "this" comes in.  You need the parenthesis around the (this->*__ops) because of operator precedence of function calls "()" over the rest.

RedComet

That did the trick and I learned something new. Thanks Klarth. I owe you one. :D
Twilight Translations - More than just Dragonball Z. :P

Kiyoshi Aman

So riddle me this, if you would.

Why are you doing bytecode --> string --> integer --> function pointer?

You have the bytecode and thus know the hex code for the opcode you want already—why not just use a sparse array, or use std::map<byte, (void) *func(void)> and initialize it statically to point the right function members?

RedComet

Quote from: Kiyoshi Aman on August 10, 2011, 09:54:36 PM
So riddle me this, if you would.

Why are you doing bytecode --> string --> integer --> function pointer?

You have the bytecode and thus know the hex code for the opcode you want already—why not just use a sparse array, or use std::map<byte, (void) *func(void)> and initialize it statically to point the right function members?

The 68K isn't like say the 6502 where every opcode has a fixed hex equivalent. Every opcode is encoded as a 2 byte word with information encoded into that instruction word. So unless I'm misunderstanding, what you're suggesting isn't feasible. I had considered using a map container for this part though. I'll probably end up changing this part of the code, but it works for now like I want it to. :P
Twilight Translations - More than just Dragonball Z. :P

Gemini

I'd suggest you to use something like in the GNU assembler which relies on a static array containing for each row the opcode, a parameter list, the hex encoding for the instruction and a few flags for other misc stuff. Here's an example of it for the MIPS assembler:
OPCODE opcodes[]=
{
// name args encoding    pinfo
{_T("nop"),     _T(""), 0x00000000, 0},
{_T("li"),      _T("t,j"),              0x24000000, WR_t}, /* addiu */
{_T("li"), _T("t,i"), 0x34000000, WR_t}, /* ori */
{_T("li"),      _T("t,I"), 0,     INSN_MACRO},
{_T("move"),    _T("d,s"), 0x00000021, WR_d|RD_s},/* addu */
{_T("move"),    _T("d,s"), 0x00000025, WR_d|RD_s},/* or */
{_T("add"),     _T("d,s,t"),         0x00000020, WR_d|RD_s|RD_t}, // ok
{_T("addi"),    _T("t,s,j"),         0x20000000, WR_t|RD_s},
{_T("addiu"),   _T("t,s,j"),         0x24000000, WR_t|RD_s},
{_T("addu"),    _T("d,s,t"),         0x00000021, WR_d|RD_s|RD_t},

Kiyoshi Aman

Quote from: RedComet on August 11, 2011, 11:36:25 AM
The 68K isn't like say the 6502 where every opcode has a fixed hex equivalent. Every opcode is encoded as a 2 byte word with information encoded into that instruction word. So unless I'm misunderstanding, what you're suggesting isn't feasible. I had considered using a map container for this part though. I'll probably end up changing this part of the code, but it works for now like I want it to. :P

In which case you'll probably want to identify the patterns and mask out the variable bits. You could still use a map, mind you—it'd just need to be adapted at runtime to new opcodes.

RedComet

Which is what I'm doing as much as possible. :P
Twilight Translations - More than just Dragonball Z. :P