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.

RocketBall
  • 45
  • 6
  • 1
    Possible duplicate of [What's the proper format for a union initializer list?](https://stackoverflow.com/questions/18411039/whats-the-proper-format-for-a-union-initializer-list) – Michael Albers Jul 23 '17 at 21:51
  • Why not just define a constructor of the form widget::widget(char *, long, char *)? – DTSCode Jul 23 '17 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. – RocketBall Jul 23 '17 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. – Tracer Jul 23 '17 at 21:58
  • Unions are not simple – M.M Jul 23 '17 at 22:03
  • @Tracer I don't understand the source of you confusion. Of course the types are different. Having multiple union members of same type would be quite atypical. And union members don't *"have the same value"*. Only one member has a value at any point of execution. – eerorika Jul 23 '17 at 22:10
  • @Tracer I thought thath union holds only one type of value at a time. Like, when I'm passing a string of chars, only char[] is "active", and when i pass a simple number, only long is "active"? I thought that when i will pass "A2X' compiler will think: "Ok, it is string of chars, so I will pass that value to id_char[20] and leave id_num without anything. But it looks like compiler want to assign "A2X" to id_num, despite it is string of char. What I don't understand here? – RocketBall Jul 23 '17 at 22:16
  • If you have a union object X that contains `int n` and `char c`, initializing X::c to 'A' would automatically initialize X::n to 65 (it's ASCII code value). That is what I meant. – Tracer Jul 23 '17 at 22:24
  • @RocketBall: Without being too negative in general about that book, `union` really has no place in a beginners book. It's simple for computers, but not simple for programmers. – MSalters Jul 23 '17 at 22:24

2 Answers2

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.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    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. – Ben Voigt Jul 23 '17 at 22:17
  • @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. – eerorika Jul 23 '17 at 22:27
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;
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142