Search code examples
c++structreturnunions

Error when trying to initialize a union inside a struct


So I a function which returns a pointer to a struct (parse_files_result_t*). This struct contains a union:

struct parse_files_result_t{
    result_tag tag;
    union {
        struct parse_file_error_t *err_ptr;
        struct parse_file_response_t *ok_ptr;        
    } data;
};

And I wanted to use the union to return an *err_ptr or an *ok_ptr based on the calculations in the function.

So I wrote the following code:

parse_files_result_t* result = (parse_files_result_t*)malloc(sizeof (parse_files_result_t));

if (success) {        
    parse_file_response_t response = //data
    *result = parse_file_result_t{result_tag::Ok, result->data.ok_ptr = &response};
    return result;
} else {
    parse_file_error_t errorresponse = //data
    *result = parse_files_result_t{{result_tag::Err}, {result->data.err_ptr = &errorresponse}};
    return result;
}

So far so good. The else part, where a parse_file_error_t gets returned, works fine because the parse_file_error_t is the first part of the union. In the if part, I want to return only the parse_file_response_t. Because this is the second "part" of the union, I get this error:

error: cannot convert ‘parse_file_response_t*’ to ‘parse_file_error_t*’ in initialization

Even though I wrote result->data.ok_ptr, my compiler tries to put the parse_file_response_t response into the parse_file_error_t part of the union.

How can I fix this?

Thanks for your help!


Solution

  • Even though I wrote result->data.ok_ptr, my compiler tries to put the parse_file_response_t response into the parse_file_error_t part of the union.

    Well, yes. You compacted so much into a single line, you lost track of what you are telling the computer to do.

       *result = parse_files_result_t{result_tag::Ok, result->data.ok_ptr = &response};
    

    This statement does several things. (Work from the inside out to follow along. We'll start from result->data.ok_ptr = &response and work out to *result = [stuff].)

    1. Assign the address of response to result->data.ok_ptr.
    2. A (temporary) parse_files_result_t object is constructed using the initial values result_tag::Ok and the value that was assigned in step 1. These values are assigned to the tag and data.err_ptr members, hence the error.
    3. Assign the object constructed in step 2 to *result, overwriting the assignment that was done in step 1.

    Simplify your code to (almost) get what you want.

        result->tag = result_tag::Ok;
        result->data.ok_ptr = &response;
    

    This gives more lines of code, but the syntax is clearer. Also, you are creating less work for the computer since this no longer tries to create a temporary parse_files_result_t object.

    There is still a problem, though, in that result->data.ok_ptr will be a dangling pointer. Unfortunately, I think addressing that (and addressing the other issues) goes outside the scope of your question.