2

The situation is that I programmed an assembler and I'm using a std::unordered_multimap container to store all the different instructions, where the actual mnemonic is my key into the map and the associated value is a custom structure with some additonal information about the parameters, etc.

Since I don't need to make any changes to this lookup during runtime I thought I'd declare it as static and const and put all the values manually in an initializer_list.

Altogether it looks like this:

typedef std::wstring STRING;

static const
std::unordered_multimap<STRING, ASM_INSTRUCTION> InstructionLookup = {
//  { MNEMONIC, { Opcode1, Opcode2, Param1Type, Param2Type, Param3Type, NrBytes, Bt1, Bt2, Bt3, Bt4, NoRexW, InvalidIn64Bit, InvalidIn32Bit } },
    { L"AAA",{ ot_none, ot_none, par_noparam, par_noparam, par_noparam, 1, 0x37, 0x00, 0x00, 0x00, false, true, false } },

    { L"AAD",{ ot_none, ot_none, par_noparam, par_noparam, par_noparam, 2, 0xD5, 0x0A, 0x00, 0x00, false, true, false } },
    { L"AAD",{ ot_ib, ot_none, par_imm8, par_noparam, par_noparam, 1, 0xD5, 0x00, 0x00, 0x00, false, true, false } },

    { L"AAM",{ ot_none, ot_none, par_noparam, par_noparam, par_noparam, 2, 0xD4, 0x0A, 0x00, 0x00, false, true, false } },
    ...

My problem now is that there're a lot of instructions (currently 1,225 of them) implemented.
So when I run a code-analysis with Visual Studio, it tells me that the constructor function exceeds the stack with 98,000/16,384 bytes because the constructor first puts all those entries on the stack it seems, before processing them any further.

My question now is how to initialize all that space directly on the heap, preferably without having to rewrite much of it.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
E. H
  • 53
  • 4
  • A quality of implementation issue unfortunately. Does it change anything to make the container `constexpr` ? – M.M Jun 01 '18 at 01:35
  • @M.M Unfortunately that doesn't work because the std::wstring type is not permitted in a constexpr. – E. H Jun 02 '18 at 12:51

1 Answers1

1

I think emplace is what you are looking for:

InstructionLookup.emplace(std::piecewise_construct, std::forward_as_tuple(L"sXs"), std::forward_as_tuple(ot_none, ot_none, par_noparam, par_noparam, par_noparam, 1, 0x37, 0x00, 0x00, 0x00, false, true, false));

I tried to keep your syntax as much as possible and changed the Boost.Assign implementation version from here to use perfect forwarding:

template <typename T, typename U>
class create_unmap
{
private:
    std::unordered_multimap<T, U> m_map;
public:

    template <typename ...Args>
    create_unmap(Args&&... _Val)
    {
        m_map.emplace(std::forward<Args>(_Val)...);
    }

    template <typename ...Args>
    create_unmap<T, U>& operator()(Args&&... _Val)
    {
        m_map.emplace(std::forward<Args>(_Val)...);
        return *this;
    }

    operator std::unordered_multimap<T, U>()
    {
        return std::move(m_map);
    }
};

You can declare your map using this syntax:

static const std::unordered_multimap<STRING, ASM_INSTRUCTION> InstructionLookupt = create_unmap<STRING, ASM_INSTRUCTION>
        (std::piecewise_construct, std::forward_as_tuple(L"AAA"), std::forward_as_tuple(ot_none, ot_none, par_noparam, par_noparam, par_noparam, 1, 0x37, 0x00, 0x00, 0x00, false, true, false))
        (std::piecewise_construct, std::forward_as_tuple(L"AAD"), std::forward_as_tuple(ot_none, ot_none, par_noparam, par_noparam, par_noparam, 1, 0x37, 0x00, 0x00, 0x00, false, true, false))
        (std::piecewise_construct, std::forward_as_tuple(L"AAD"), std::forward_as_tuple(ot_none, ot_none, par_noparam, par_noparam, par_noparam, 1, 0x37, 0x00, 0x00, 0x00, false, true, false));
scrapper
  • 361
  • 2
  • 6
  • Thanks for the proposition, I'll have a closer look at it once I'm home again next week. Just for understanding though, what prevents me from using Boost.Assign like you mentioned with something like "map_list_of(STRING(L"AAA"),ASM_INSTRUCTION{ot_none, ot_none, ...})(...)" ? – E. H Jun 02 '18 at 12:54
  • without the [piecewise construction constructor](http://en.cppreference.com/w/cpp/utility/pair/pair) of std::pair the compiler will still first generate all of your instructions on the stack first as std::pair and then move/copy them to the map. – scrapper Jun 03 '18 at 10:14