2

my code:

#include <iostream>

using namespace std;

struct widget
{
    char brand[20];
    int type;
    union id
    {
        long id_num;
        char id_char[20];
    }id_val;
};

int main()
{
    widget prize = 
    {"Rolls", 0, "A2X"};

    return 0;
}

The problem is with initialization "A2X" when initializing a union in a structure. Compiler doesn't know I want to choose second option with array of chars when I am passing "A2X", it's requiring long type. When I put

char id_char[20]

before

long id_num

everything is ok. But I want to know how to enforce compiler to accept "A2X" with char as the second option in union. Thank for your help.

9
  • 1
    Possible duplicate of What's the proper format for a union initializer list? Commented Jul 23, 2017 at 21:51
  • Why not just define a constructor of the form widget::widget(char *, long, char *)? Commented Jul 23, 2017 at 21:51
  • I am currently reading Cpp primer plus and it is example from this book, so I wonder how to solve it without such a things, that weren't covered before this example. I'm looking for simpliest solution. Commented Jul 23, 2017 at 21:54
  • I don't understand what are you trying to accomplish here. Union defines the same value for all it's members and here you have completely different types that should have the same value. Commented Jul 23, 2017 at 21:58
  • Unions are not simple Commented Jul 23, 2017 at 22:03

2 Answers 2

2

But I want to know how to enforce compiler to accept "A2X" with char as the second option in union.

You can use a constructor:

id(char const *id_char) {
    std::strcpy(this->id_char, id_char);
}

Alternatively you could use a widget constructor.

A drawback is that the compiler probably won't be able to warn you if you use a too large input string for initialization. The shown trivial constructor can be expanded with strlen to check overflow at runtime. I suggest throwing an exception if you choose to check.

Sign up to request clarification or add additional context in comments.

2 Comments

Note that in case of quoted literal initializer, a template constructor can be used and check the length at compile time. If passing in a variable, you can't check length until runtime, but then it may be better to limit the input size via truncation rather than crashing the program.
@BenVoigt good point about the template constructor, that's an option of merit. Truncation at runtime is indeed an option as well, but I don't agree it being a better one. Note that I didn't suggest crashing the program, but simply throw an exception. That won't terminate the program unless you forget to catch and handle the case. The checkless approach is also useful if you deal with time constrained systems. There is no need to check at runtime as long as the user of the class / union follows the documented preconditions. I would add an assert to a real word implementation, however.
0

This works with -std=c++11:

#include <cstring>
#include <stdexcept>

struct widget
{
    char brand[20];
    int type;
    union id
    {
        long id_num;
        char id_char[20];
    }id_val;
    widget(char const*Str, int Type, char const *Id);
};

widget::widget(char const*Str, int Type, char const *Id) 
{
    if (strlen(Str)+1 > sizeof brand)
        throw std::length_error{"brand too large"};
    memcpy(brand,Str,strlen(Str)+1);
    type = Type;
    if (strlen(Id)+1 > sizeof id_val.id_char)
        throw std::length_error{"id too large"};
    memcpy(id_val.id_char,Id,strlen(Id)+1);

}

int main()
{
    widget prize = {"Rolls", 0, "A2X"};

    return 0;
}

2 Comments

strncpy is a poor choice here as it will leave the string being non-null-terminated if the source was too long
@M.M Point taken. Checked memcpy it is.

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.