#include <opcodes.h>
const char *getOpcodeName(
uint8_t op
)
{
#define OPCODE(x, y) if((0x##y)==op) return "OP_" #x;
OPCODES
#undef OPCODE
return "OP_UNKNOWN";
}
Link to the code here: https://github.com/znort987/blockparser/blob/master/opcodes.cpp
Here is a link to the included opcodes.h
I understand this is just a strangely formatted function, however, I am wondering what exactly the *
at the beginning of the function name means. I assume it has something to do with pointers?
Also, how are the #undef
and #define
statements valid? There's no semicolon after either one, and one of them seems to be defined as a one-line function. What does (0x##y)
mean? What does return "OP_" #x
mean? I haven't ever come across syntax like this before.
I want to get into C++ more seriously, but it's very hard when looking at code to tell what exactly is going on. How can I learn the syntax and rules most effectively?
Run your code thru a C++ preprocessor, e.g. using g++ -Wall -C -E opcodes.cpp > opcodes.i
then look inside the generated opcodes.i
#define
is not a statement but a preprocessor directive.
The macro OPCODES
gets expanded to some big chunk, notably containing OPCODE( NOP, 61)
which would get expanded to something like
if ((0x61)==op) return "OP_" "NOP";
The two string literals are concatenated into one, "OP_NOP"
here.
GCC has a good documentation on its cpp preprocessor. Read about stringification (with the single #
like the ending #x;
of the OPCODE
macro) and about concatenation (with a double ##
like (0x##y)
of the OPCODE
macro).
Short answer: This function converts opcodes to string.
The *
IS RELATED to do with pointers! in fact, this function returns a const char *
type. This is a pointer for a char buffer (in this case) used to point to a C-String. Every C-String is actually a buffer with "readable characteres" (alphanumerical, some accents, basic symbols + some stuff) followed by a byte of value 0 (or '\0'
) to indicate the end of the string!
This function converts opcodes (assembly instructions) to a readable string. So The intention of the programmer was to transform:
- 0x01 -> OP_CODE1
- 0x02 -> OP_CODE2
- 0x03 -> OP_CODE3
The expanded version of the code is something like this:
const char *getOpcodeName( uint8_t op )
{
if((0x01)==op) return "OP_X";
if((0x02)==op) return "OP_Y";
if((0x03)==op) return "OP_Z";
...
if((0x??)==op) return "OP_?";
return "OP_UNKNOWN";
}
Insted of write houndreds of IF's ... the programmer decided to create the macro
#define OPCODE(x, y) if((0x##y)==op) return "OP_" #x;
So it's possible to write the OPCODES table easily like this:
#define OPCODES \
OPCODE( 01, "X" ) \
OPCODE( 02, "Y" ) \
OPCODE( 03, "Z" ) \
...
OPCODE( ??, "?" )
Opcodes are codes that indentify the processor instruction (assembly instructions). Example of real instructions (for some Intel processor) are:
inc eax ; opcode = 0x40
pusha ; opcode = 0x60
nop ; opcode = 0x90
So your table could be:
#define OPCODES \
OPCODE( 40, "INCEAX" ) \
OPCODE( 60, "PUSHA" ) \
OPCODE( 90, "NOP" )