1

I want to initialize an object with an initializer list. The problem is, an initializer list is able to contain unpredictable number of elements, but I need to initialize only for variables. The user may send any number of list elements, and I need only four of them.

I wrote the following code, but it looks like very long and inefficient to me. Is there any better way of doing this?

Pixel::Pixel(std::initializer_list<uint8_t> Channels)
{
    switch (Channels.size())
    {
        case 0:
            R = 0;
            G = 0;
            B = 0;
            A = 0;
            break;
        case 1:
            R = *(Channels.begin() + 0);
            G = 0;
            B = 0;
            A = 0;
            break;
        case 2:
            R = *(Channels.begin() + 0);
            G = *(Channels.begin() + 1);
            B = 0;
            A = 0;
            break;
        case 3:
            R = *(Channels.begin() + 0);
            G = *(Channels.begin() + 1);
            B = *(Channels.begin() + 2);
            A = 0;
            break;
        default:
            R = *(Channels.begin() + 0);
            G = *(Channels.begin() + 1);
            B = *(Channels.begin() + 2);
            A = *(Channels.begin() + 3);
    }
}

(Note: I know this can be done with passing the R, G, B, A values with four separate arguments. But my main purpose is to learn how to do this with the initializer list feature.)

5
  • 2
    You could get exactly what you want with a std::array<>. Commented Dec 16, 2014 at 10:03
  • If you always have exactly 4 elements, why not take a const std::array<uint8_t, 4>&? Commented Dec 16, 2014 at 10:04
  • 2
    What's wrong with good 'ol Pixel::Pixel(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 0)? It's simple and runs on anything on or above C++03 and doesn't force the client code to create an array. Commented Dec 16, 2014 at 10:10
  • @legends2k That constructor already exists. I want to implement it with the initializer list in order to learn it. I have never used it before and I'm trying to gain some experience. Commented Dec 16, 2014 at 10:12
  • 2
    @hkBattousai Well, your problem with the initializer list has demonstrated why it's a poor choice in this case. You already have the optimal solution. Commented Dec 16, 2014 at 10:14

2 Answers 2

4

the best I could come up with when using std::initialiser_list

struct Pixel
{
    Pixel(std::initializer_list<uint8_t> rgba)
    : _rgba { rgba }
    {
        switch(_rgba.size()) {

            case 0: _rgba.push_back(0);
            case 1: _rgba.push_back(0);
            case 2: _rgba.push_back(0);
            case 3: _rgba.push_back(0);
            case 4: break;
            default:
                throw std::invalid_argument { "" };
        }

    }

    std::vector<uint8_t> _rgba;
};

... but ...

Probably the correct way to solve this problem is this:

struct Pixel
{
    Pixel(uint8_t r = 0, uint8_t g = 0, uint8_t b = 0, uint8_t a = 0)
    : R(r)
    , G(g)
    , B(b)
    , A(a)
    {}

    uint8_t R,G,B,A;
};

because

  1. It will fail to compile if you provide invalid arguments
  2. It's optimally efficient
  3. It's the least surprising solution for anyone maintaining your code
  4. It automatically supports the initializer_list syntax at the call site

examples:

int main()
{
    Pixel p1 { 10, 20, 5, 255 };
    Pixel p2 { 10, 20, 5 };
    Pixel p3 { 10, 20 };
    Pixel p4 { 10 };
    Pixel p5 { };

    Pixel pv1 ( 10, 20, 5, 255 );
    Pixel pv2 ( 10, 20, 5 );
    Pixel pv3 ( 10, 20 );
    Pixel pv4 ( 10 );
    Pixel pv5;

    return 0;
}
Sign up to request clarification or add additional context in comments.

2 Comments

I didn't know the rule number 4. I can't find it on Google. Can you please give me a link?
since c++11 value-initialisation takes place when a named variable is constructed with braces: see point 4 in the explanation: en.cppreference.com/w/cpp/language/value_initialization
4

You may rewrite you switch as follow:

Pixel::Pixel(std::initializer_list<uint8_t> Channels) : R(0), G(0), B(0), A(0)
{
    switch (Channels.size())
    {
        default: // Too many args
        case 4: A = *(Channels.begin() + 3); // No break: Follow next line
        case 3: B = *(Channels.begin() + 2); // No break: Follow next line
        case 2: G = *(Channels.begin() + 1); // No break: Follow next line
        case 1: R = *(Channels.begin() + 0);
        case 0: break;
    }
}

1 Comment

Splendid solution. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.