How can i pass an element to be able to evaluate within a certain specialization?

86 views Asked by At

I am trying to make a CHIP-8 emulator that generates most of the instructions at compile time.

So i have a constexpr array like this:

constexpr uint16_t hardCodedROM[] = {
    0x8001, 0x8002, 0x8003, 0xA0F0, 0xB0FE
};

And I have a specialization for each item of the array like this:

template<uint16_t instruction> struct Opcode 
{
    static inline void Task(uint16_t code)
    { 
        printf("Unknown Opcode \n");
    }
};

template<> struct Opcode<0x8000>
{

    static inline auto Task(uint16_t code)
    {
        static constexpr unsigned char submask = code & 0x000F; //error here, code can not use as a constant

        if constexpr (submask == 0x0001)
        {
            printf("Opcode 0x8xx1 \n");
        }

        if constexpr (submask == 0x0002)
        {
            printf("Opcode 0x8xx2 \n");
        }

        if constexpr (submask == 0x0003)
        {
            printf("Opcode 0x8xx2 \n");
        }

        if constexpr (submask == 0x0000)
        {
            printf("Opcode 0x8xx0 \n");
        }
    }
};

template<> struct Opcode<0xA000>
{
    static inline auto Task(uint16_t code)
    {
        printf("Opcode 0xAxxx \n");
    }
};

template<> struct Opcode<0xB000>
{
    static inline auto Task(uint16_t code)
    {
        printf("Opcode 0xBxxx \n");
    }
};

And i iterate over each of the elements and invoke specialization in this way:

[&] <std::size_t...p>(std::index_sequence<p...>)
{
    (Opcode<(hardCodedROM[p] & 0xF000)>::Task(hardCodedROM[p]), ...); //How should i pass the element, to be able to evaluate depending on each submask?
}
(std::make_index_sequence<5>{});

How should i pass the element (hardCodedROM[p]), to be able to evaluate depending on each submask within a certain specialization?

1

There are 1 answers

0
Evg On BEST ANSWER

If code is always known at compile time, make it a template parameter:

template<std::uint16_t code>
static void Task() {
    constexpr unsigned char submask = code & 0x000F;
    // ...
}

and then don't forget the template disambiguator:

[&]<std::size_t...p>(std::index_sequence<p...>) {
    (Opcode<(hardCodedROM[p] & 0xF000)>::template Task<hardCodedROM[p]>(), ...);
}
(std::make_index_sequence<5>{});

You can also wrap code into a type similar to Opcode and pass it as a function parameter to avoid pretty ugly template keyword. For example:

template<std::uint16_t> struct Instruction {};

// ...
    template<std::uint16_t code>
    static void Task(Instruction<code>) { 
        constexpr unsigned char submask = code & 0x000F;
        // ...
    }
// ...

[&]<std::size_t...p>(std::index_sequence<p...>) {
    (Opcode<(hardCodedROM[p] & 0xF000)>::Task(Instruction<hardCodedROM[p]>{}), ...);
}
(std::make_index_sequence<5>{});