Union as an argument to a function in C
Asked Answered
G

6

5

I am wondering if it is possible to use unions as arguments to a function:

Let's say I have two structures:

struct complex_attribute{
    struct generic_attribute *sub_attributes[20];
};

struct generic_attribute{
    int current_value;
};

And a union of these two:

union union_attribute{
    struct complex_attribute *complex;
    struct generic_attribute *generic;
};

I want to create a function that takes in either a complex_attribute or a generic_attribute:

struct tagged_attribute* prepare_tagged_attribute(int code, union union_attribute *attribute)

However, when I make a call to this function

prepare_tagged_attribute(2, pointer_to_complex_structure);

I get this error:

 passing argument 2 of ‘prepare_tagged_attribute’ from incompatible pointer type

So I take it that pointer to a a complex structure is not necessarily a pointer of type union (which makes sense)... but then is it possible to use unions in this way?

Gpo answered 26/6, 2015 at 18:27 Comment(0)
S
3

Since there is no polymorphism or overloading in C, I'm not sure there's a perfect solution to this.

Your solution will work, provided that you wrap up your pointer in a union_attribute:

union union_attribute argument;
argument.complex = pointer_to_complex_structure;
prepare_tagged_attribute(2, &argument);

It's a bit annoying that you have to do this wrap, so one option would be to introduce a new function to do it for you:

union union_attribute wrap_complex(struct complex_attribute* attr) {
    union union_attribute result;
    result.complex = attr;
    return result;
}

Then, rewrite your prepare_tagged_attribute function to take in a union by value rather than by pointer:

struct tagged_attribute* prepare_tagged_attribute(int code, union union_attribute attribute)

Then, you can say something like

prepare_tagged_attribute(2, wrap_complex(pointer_to_complex_structure));

It's not perfect, for sure, but it works and type-checks.

Hope this helps!

Stonebroke answered 26/6, 2015 at 18:36 Comment(0)
R
5

If you're using GCC, and willing to use its language extensions, you can accomplish what you're trying to do with a transparent_union.

From https://gcc.gnu.org/onlinedocs/gcc-3.1/gcc/Type-Attributes.html:

Transparent unions are designed for library functions that have multiple interfaces for compatibility reasons. For example, suppose the wait function must accept either a value of type int * to comply with Posix, or a value of type union wait * to comply with the 4.1BSD interface. If wait's parameter were void *, wait would accept both kinds of arguments, but it would also accept any other pointer type and this would make argument type checking less useful. Instead, might define the interface as follows:

      typedef union
        {
          int *__ip;
          union wait *__up;
        } wait_status_ptr_t __attribute__ ((__transparent_union__));

      pid_t wait (wait_status_ptr_t);

In your example, you'd declare union_attribute like this:

typedef union {
    struct complex_attribute *complex;
    struct generic_attribute *generic;
} union_attribute __attribute__ ((__transparent_union__));

And then you can call prepare_tagged_attribute exactly how you proposed.

struct tagged_attribute* prepare_tagged_attribute(int code, union_attribute attribute)
{
   attribute.generic_attribute->current_value = 1; // e.g.
   return NULL; // e.g.
}

prepare_tagged_attribute(code, pointer_to_complex_structure);
Rohrer answered 26/6, 2015 at 18:38 Comment(0)
S
3

Since there is no polymorphism or overloading in C, I'm not sure there's a perfect solution to this.

Your solution will work, provided that you wrap up your pointer in a union_attribute:

union union_attribute argument;
argument.complex = pointer_to_complex_structure;
prepare_tagged_attribute(2, &argument);

It's a bit annoying that you have to do this wrap, so one option would be to introduce a new function to do it for you:

union union_attribute wrap_complex(struct complex_attribute* attr) {
    union union_attribute result;
    result.complex = attr;
    return result;
}

Then, rewrite your prepare_tagged_attribute function to take in a union by value rather than by pointer:

struct tagged_attribute* prepare_tagged_attribute(int code, union union_attribute attribute)

Then, you can say something like

prepare_tagged_attribute(2, wrap_complex(pointer_to_complex_structure));

It's not perfect, for sure, but it works and type-checks.

Hope this helps!

Stonebroke answered 26/6, 2015 at 18:36 Comment(0)
D
2

As you declared the function to take a union argument, you do have to actually pass a union argument, not a pointer.

Sidenote: If using gcc, have a look at the transparent_union attribute. That might be interesting for you, as it actually allows the usage above. Note, howeer, you still have to signal which type of argument you are passing (as much as for the standard way.

A possibly better way would be to bundle type and value information in a single struct like:

struct variable_argument {
    enum {
        COMPLEX_ARG, GENERIC_ARG
    } type;
    union union_attribute{
        struct complex_attribute *complex;
        struct generic_attribute *generic;
    };
};

and pass a pointer to such a struct to the function. Note I uses an anonymous struct field for the union (since C99). This way you can refer the fields like:

struct variable_argument s;

... s.complex = ...

Never forget to set s.type appropriately if you change the type of the union, however.

This struct actually relaces your pointers to one of the other structures. However, you might have them directly in the union instead of using pointers. That simplifies memory allocation/freeing.

Disinterested answered 26/6, 2015 at 18:38 Comment(0)
M
1

Don't use unions at all. Since you also pass a code indicating what kind of pointer is being used, use a pointer to void and just pass the structure pointer:

prepare_tagged_attribute(2, pointer_to_complex_structure);

Then dynamically cast your pointer based on the code:

if( code == 2 ){
    struct complex_attribute* p = attribute ;
}
Modernism answered 26/6, 2015 at 18:40 Comment(2)
While that's certainly an option, it loses type safety information. You can pass a pointer to anything into a function taking a void*, which means that you could pass an int*, a char**, etc. even if you're not supposed to. I think the OP's idea is to make it hard to get this wrong.Stonebroke
@Stonebroke I actually upvoted your answer, but wanted to show this method.Modernism
P
1

You can't (portably) cast a complex_attribute* to a union_attribute*. But, if you're using C99, you can use what's called a "compound literal" to effectively create an unnamed union, initialize a member in it, take its address, and pass it to your function, all in line. None of the other answers seem to have mentioned this - here's a working program demonstrating that approach:

#include <stdio.h>

struct generic_attribute{
    int current_value;
};

struct complex_attribute{
    struct generic_attribute *sub_attributes[20];
};

union union_attribute{
    struct complex_attribute *complex;
    struct generic_attribute *generic;
};

struct tagged_attribute* prepare_tagged_attribute(int code,
                                                  union union_attribute *attribute)
{
    if (code == 1) {
        printf("in func, generic_attribute is %p\n", attribute->generic);
    } else {
        printf("in func, complex_attribute is %p\n", attribute->complex);
    }
    return NULL;
}

int main()
{
    struct generic_attribute g;
    printf("in main, generic_attribute is %p\n", &g);
    prepare_tagged_attribute(1, &(union union_attribute){.generic=&g});

    struct complex_attribute c;
    printf("in main, complex_attribute is %p\n", &c);
    prepare_tagged_attribute(2, &(union union_attribute){.complex=&c});

    return 0;
}

See the section on Compound Literals in the standard for more information about this technique. It's been available since C99, but it isn't available in C++, and many C programmers seem not to know about it.

Padriac answered 26/6, 2015 at 18:47 Comment(6)
Read the other answers again carefully. At least one did mention it.Disinterested
Oh, you do not mean anonymous struct fields (or union fields, but compound literals. Please get the terms right.Disinterested
@Olaf I don't think so - gubblebozer's answer indirects through another function rather than using the C99 feature I'm referencing, and your answer references unnamed unions as a member of a struct, but not unnamed unions created and initialized in a function call - that's also a different feature.Padriac
@Olaf Fair enough, I couldn't remember the name of the feature, and was having poor luck googling it without remembering the name. I'll edit that in - thank you!Padriac
@Olaf To quote the standard - "A postfix expression that consists of a parenthesized type name followed by a brace- enclosed list of initializers is a compound literal. It provides an unnamed object whose value is given by the initializer list." - calling it an "unnamed union" wasn't totally off base. :-) I'll add in a link to the standard section, too.Padriac
It is a compound literal in the first place, however. It actually does not work just for unions, as your original wording might give that impression. Call me nit-picky, but I prefer a more general picture; that might help ppl to see common patterns, not just a singular solution for a specific problem. Anyway, I'm fine with your edit :-)Disinterested
P
1

I would just declare the function to take both argument types

prepare_tagged_attribute(struct complex_attribute *complex,struct generic_attribute *generic)

Process the first non-NULL argument.

Partizan answered 21/12, 2017 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.