1

When working with an aggregate class, passing in an initializer list for an array member is not problem. For example:

class color_def
{
 public:
    const u8 color_passes;
    const u8 red_shift[MAX_COLOR_PASSES];
    const u8 red_bitcount[MAX_COLOR_PASSES];
    const u8 green_shift[MAX_COLOR_PASSES];
    const u8 green_bitcount[MAX_COLOR_PASSES];
    const u8 blue_shift[MAX_COLOR_PASSES];
    const u8 blue_bitcount[MAX_COLOR_PASSES];
};

const static color_def snk_neogeo_color = {
        3, {15, 14, 8}, {1, 1, 4}, {15, 13, 4}, {1, 1, 4}, {15, 12, 0}, {1, 1, 4}};

This works without any issue. However, when I add an explicit constructor, it won't work. For example, from a similar class:

class chr_def
{
 public:
    chr_def(u16 width, u16 height, u8 bitplanes, u32 planeoffset[MAX_GFX_PLANES],
                    u32 xoffset[MAX_GFX_SIZE], u32 yoffset[MAX_GFX_SIZE],
                    const u8 *(*converter)(const chr_def *chrdef, const u8 *data))
            : width(width),
                height(height),
                bitplanes(bitplanes),
                planeoffset{planeoffset},
                xoffset(xoffset),
                yoffset(yoffset),
                converter(converter),
                datasize(width * height * bitplanes){};

    const u8 *convert(const u8 *data) { return this->converter(this, data); }

    const u16 width;                 // pixel width of each element
    const u16 height;                // pixel height of each element
    const u8 bitplanes;          // number of color bitplanes
    const u32 *planeoffset; // bit offset of each bitplane
    const u32 *xoffset;          // bit offset of each horizontal pixel
    const u32 *yoffset;          // bit offset of each vertical pixel
    const u32 datasize;          // size of one chr in bits
    const u8 *(*converter)(const chr_def *, const u8 *);
};

const static chr_def nintendo_sfc = {
        8,
        8,
        4,
        {0, 8, 128, 136},
        {0, 1, 2, 3, 4, 5, 6, 7},
        {0 * 16, 1 * 16, 2 * 16, 3 * 16, 4 * 16, 5 * 16, 6 * 16, 7 * 16},
        get_chr};

The fails, complaining that the arguments don't match the constructor. The issue seems to be that it is taking the first value of the array init list as the pointer to the array:

note: candidate constructor not viable: cannot convert initializer list argument to 'u32 *' (aka 'unsigned int *')

If I pull the array init lists out and initialize them beforehand into their own variables like so:

u32 temp1[4] = {0, 8, 128, 136};
u32 temp2[8] = {0, 1, 2, 3, 4, 5, 6, 7};
u32 temp3[8] = {0 * 16, 1 * 16, 2 * 16, 3 * 16, 4 * 16, 5 * 16, 6 * 16, 7 * 16};
const static chr_def nintendo_sfc = {
        8,
        8,
        4,
        temp1,
        temp2,
        temp3,
        get_chr};

Things work fine. I'd just really prefer not to do it this way if at all possible.

So... why does this work in the aggregate form but not with my explicit constructor? Is there some way to make this work? Appreciate any insight anyone can offer.

Ryou
  • 57
  • 4
  • Are the array sizes dependent on `width/height/bitplanes`? If so, you should consider making them template parameters. – super Oct 04 '19 at 17:11
  • 1
    A pointer is a single value. It doesn't make sense to initialize it with multiple values. A pointer is initialized with a pointer (the array decayed to a pointer in the example that worked). Have you considered using `std::array` instead of dumb arrays? – PaulMcKenzie Oct 04 '19 at 17:51

2 Answers2

2

The problem you are having is that you are trying to initialize pointer members with std::initializer_list, which is simply not possible.

You can, however, use std::array (since c++ 11). Here is a simplified example:

#include <iostream>
#include <array>

#define MAX_GFX_PLANES (4U)

struct chr_def {
    // Constructor
    chr_def(size_t size, const std::array<unsigned int, MAX_GFX_PLANES>& planeoffset) :
        size(size), planeoffset(planeoffset) { };

    // Data Members
    const size_t size;
    const std::array<unsigned int, MAX_GFX_PLANES> planeoffset;
};

const static chr_def nintendo_sfc{ 8U, {0U, 1U, 2U, 3U} };

int main() {
    // std::array can be used with range-based for
    for (auto i : nintendo_sfc.planeoffset) {
        std::cout << i << " ";
    }
    return 0;
}
ph3rin
  • 4,426
  • 1
  • 18
  • 42
  • I know comments aren't really for saying thanks but... thank you! That worked flawlessly! – Ryou Oct 05 '19 at 09:29
1

It's not the explicit constructor that done troubles to you, but the pointers you are using instead the arrays:

The following variables declarations:

const u8 red_shift[MAX_COLOR_PASSES];
const u8 red_bitcount[MAX_COLOR_PASSES];
const u8 green_shift[MAX_COLOR_PASSES];
const u8 green_bitcount[MAX_COLOR_PASSES];
const u8 blue_shift[MAX_COLOR_PASSES];
const u8 blue_bitcount[MAX_COLOR_PASSES];

Become:

const u32 *planeoffset;      // bit offset of each bitplane
const u32 *xoffset;          // bit offset of each horizontal pixel
const u32 *yoffset;          // bit offset of each vertical pixel

Pointers are not the same as arrays.

You can read about the difference between them: Is an array name a pointer?

To enable those pointers, in your constructor you have to receive type that accept initializer_list, then inside the function you have to allocate new place in memory for your pointers, and then copy the values from the arrays to your new allocated pointers.

Coral Kashri
  • 3,436
  • 2
  • 10
  • 22
  • That was how I had things originally, and it gives me a different error: inc/global.hpp:48:5: error: array initializer must be an initializer list planeoffset(planeoffset), The only way it was compiling otherwise was using the variables as pointers... – Ryou Oct 04 '19 at 17:22