Вызов функции указателя внутри вектора ничего не делает - PullRequest
2 голосов
/ 02 апреля 2019

Я сейчас пытаюсь реализовать эмулятор CHIP-8.В этих эмуляторах вы читаете содержимое картриджа, который дает вам «коды операций», значения, соответствующие функции.Я не хочу писать огромный оператор switch case, чтобы позаботиться об этих кодах операций (в контексте эмулятора CHIP-8 это нормально, так как там не слишком много кодов операций, но в контексте эмулятора Gameboy, дляНапример, он может дать несколько огромных операторов switch case).

Мой подход заключается в следующем:

У меня есть структура, содержащая диапазон для кода операции, имя функции и указательк функции (void* func).

struct instruction {
    std::pair<uint16_t, uint16_t> range;
    std::string name;
    void* func;

    instruction(std::pair<uint16_t, uint16_t> range, std::string name, void* func) {
        this->range = range;
        this->name = name;
        this->func = func;
    }
};

Мой класс выглядит следующим образом (пожалуйста, не бейте меня, я сейчас все положил public, но когда все заработает, все перейдет в приват):

class Chip8 {
    public:
        Chip8();
        virtual ~Chip8();

        void set_opcodes(void);

        bool load_cartridge(std::string path);
        void output_cartridge_contents(void);
        bool verifiy_instructions(void);

        void JUMP(void);

        // 0     -> 0x1FF: interpreter (unused since the emulator is running outside of the memory)
        // 0x200 -> 0xE9F: cartridge data
        // 0xEA0 -> 0xEFF: call stack, internal use, and other variables
        // 0xF00 -> 0xFFF: display refresh
        std::vector<uint8_t> memory;
        std::vector<uint8_t> registers;

        // Index register
        uint16_t I;
        // Program counter
        uint16_t pc;

        std::vector<uint8_t> stack;
        // Stack pointer
        uint8_t sp;

        std::vector<instruction> instr;

        // Timers
        uint8_t delay_timer;
        uint8_t sound_timer;

        uint16_t opcode;

        // Screen
        bool gfx[64 * 32];

};

Давайте рассмотрим пример функции, JUMP

void Chip8::JUMP(void) {
    this->pc = (this->opcode & 0x0FFF);
}

Затем я заполняю вектор, содержащий все реализованные коды операций,

this->instr.push_back(instruction(std::pair<uint16_t, uint16_t>(0x1000,0x1FFF), std::string("JUMP"), (void*)&Chip8::JUMP))

.текущий код операции читается, затем сравнивается с диапазоном реализованных функций (например, если код операции 0x1225, будет вызвана функция JUMP).Когда найдена правильная функция, вызывается следующий код (i - это индекс правильного instruction в показанном выше instr векторе):

this->instr[i].func;

Но когда я это делаю,ничего не происходит (т.е. значение pc не изменяется, std::cout ничего не выводит на консоль и т. д.).Я пробовал несколько подходов (изменив тип func на void (*func)(void), пытаясь вызвать this->instr[i].func();), но пока не повезло.Я думаю, что я что-то здесь упускаю.У кого-нибудь есть указатели?

Спасибо

Ответы [ 2 ]

5 голосов
/ 02 апреля 2019

Поскольку Chip8::JUMP является нестатической функцией-членом класса, вам нужен объект класса для вызова функции.Это связано с тем, что нестатические функции-члены имеют неявный первый параметр типа класса, который используется для включения использования this внутри функции.

Это можно решить с помощью std::function и с помощьюлямбда или std::bind чтобы связать функцию с объектом.Это будет выглядеть как

struct instruction {
    std::pair<uint16_t, uint16_t> range;
    std::string name;
    std::function<void(void)> func;

    template<typename Func>
    instruction(std::pair<uint16_t, uint16_t> range, std::string name, Func func) : range(range), name(name), func(func) {}
};

//...

this->instr.push_back(instruction(std::pair<uint16_t, uint16_t>(0x1000,0x1FFF), std::string("JUMP"), [this]{ this->JUMP(); }))

// and call it like

this->instr[i].func();
1 голос
/ 02 апреля 2019

указатель на функцию (void* func).

void * не является указателем на функцию. Это указатель на объект.

(void*)&Chip8::JUMP

Это плохой актерский состав. Указатель на функцию-член не может быть по существу преобразован в void*. Обращение через преобразованный указатель будет иметь неопределенное поведение (даже если преобразовано обратно в правильный тип).

this->instr[i].func;

Но когда я это делаю, ничего не происходит

this->instr[i].func; не является выражением вызова функции. Это id-выражение. У Id-выражений нет побочных эффектов.

Все выражения для вызова функций имеют круглые скобки, которые содержат (возможно, пустой) список параметров.

Я пробовал несколько подходов (изменив тип func на void (*func)(void)

void (*func)(void) - это указатель функции. Но это не указатель на функцию-член. Вы не можете преобразовать &Chip8::JUMP в указатель на функцию.

пытается позвонить this->instr[i].func();

Это выражение вызова функции, так что вы приближаетесь. Это будет правильный синтаксис для указателя на функцию, но вы попытались использовать нестатическую функцию-член, поэтому указатель на функцию у вас не работает.


Вы могли бы сделать instruction шаблоном и использовать указатели на функции-члены:

template<class T>
struct instruction {
    void (T::*func)(); // a pointer to member function
    // ...
};

// insert
instr.emplace_back(/* other member initializers */, &JUMP);

// call
(this->*(instr[i].func))() 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...