union of value and function pointer
Asked Answered
E

3

2

I am struggling with using unions. Why am I unable to pass the function pointer to where the union would be? Any help would be greatly appreciated.

Edit: removed a typedef

#include <stdio.h>

union U {
    int(*fnPtr)(int);
    int i;
};

enum E {
    OPTION_0 = 0,
    OPTION_1 = 1
};

int multiply_by_two (int x) {
    return 2 * x;
}

int f (int x, enum E e, union U u) {
    switch (e) {
        case OPTION_0:
            /* Return the sum */
            return x + u.i;
        case OPTION_1:
            /* Return 2 * x */
            return u.fnPtr (x);
    }
}

int main (void) {
    int a;
    scanf ("%d", &a);
    int b = f (a, OPTION_1, &multiply_by_two);
    printf ("%d\n", b);
    return 0;
}
Ecumenical answered 4/2, 2019 at 13:32 Comment(5)
Also, take a look at this question.Embowed
union U is not int(*fnPtr)(int). union U has a member of that type.Dashiell
Sorry, the typedef was an error, was typing from memory :/ I've removed itEcumenical
@Ecumenical As a rule, never retype code. Just copy and paste what you have running. A small change can make a big difference between functioning and non-functioning code.Socialite
the posted code, when run through the compiller (gcc) results in: gcc -ggdb -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled2.c" : In function ‘main’: :31:29: error: incompatible type for argument 3 of ‘f’ int b = f (a, OPTION_1, &multiply_by_two); :17:5: note: expected ‘union U’ but argument is of type ‘int (*)(int)’ int f (int x, enum E e, union U u) { : In function ‘f’: :26:1: warning: control reaches end of non-void function [-Wreturn-type] } Compilation failed.Telephonic
S
4

First, this definition is not valid:

union U {
    typedef int(*fnPtr)(int);
    int i;
};

You can't have a typedef inside of a struct or union. Removing the typedef will give you a proper definition:

union U {
    int(*fnPtr)(int);
    int i;
};

The second problem is here:

int b = f (a, OPTION_1, &multiply_by_two);

The function f expects a union U, but you're passing it a int (*)(int). Those types are not compatible. Just because the union has a member of that type doesn't mean you can use that type wherever you would use the union. You need to create a union, set the proper field, then pass that to the function.

union U u;
u.fnPtr = multiply_by_two;
int b = f (a, OPTION_1, u);
Socialite answered 4/2, 2019 at 13:41 Comment(4)
Just a tiny typo: typedef wasn't removed in the second snippet.Dashiell
I see, so the union cannot implicitly determine which field is valid, and must be manually assigned. Is there a way to do this implicitly?Ecumenical
@Ecumenical No, there is no implicit conversion between struct and union types and other typesSocialite
@Groo Thanks, I missed that. Fixed.Socialite
P
2

In main function, try this:

int main()
{
  ...
  union U myUnion;
  myUnion.fnPtr = &multiply_by_two;
  int b = f (a, OPTION_1, myUnion);
  ...
}

And also, the union definition is not correct, you need to remove typedef.

Punk answered 4/2, 2019 at 13:41 Comment(0)
D
2

Just to add to other answers: this is usually called a variant data type, and it makes sense to keep the type enum in a struct, along with the union, since you will be passing it around all the time anyway.

So I would recommend placing both in a struct:

enum var_type
{
    VAR_INT = 0,
    VAR_FUNC = 1
};

struct variant
{
    // contains the type of the stored value
    enum var_type type;

    // contains the actual value
    union {
        int(*fnPtr)(int);
        int i;
    };    
};

And then you can have separate functions for creating each subtype, for simpler instantiation:

// wrap the int value in the variant struct
struct variant variant_create_int(int i)
{
    return (struct variant){ .type = VAR_INT, .i = i };
}

// wrap the functino pointer in the variant struct
struct variant variant_create_func(int(*fnPtr)(int))
{
    return (struct variant){ .type = VAR_FUNC, .fnPtr = fnPtr };
}

Meaning your main would do something like:

// create variant of type 'VAR_FUNC'
struct variant var = variant_create_func(&multiply_by_two);

and just pass the var struct forward.

Dashiell answered 4/2, 2019 at 13:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.