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;
}
5
  • Also, take a look at this question. Commented Feb 4, 2019 at 13:38
  • union U is not int(*fnPtr)(int). union U has a member of that type. Commented Feb 4, 2019 at 13:40
  • Sorry, the typedef was an error, was typing from memory :/ I've removed it Commented Feb 4, 2019 at 13:43
  • 2
    @Z4ckZ3r0 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. Commented Feb 4, 2019 at 13:59
  • 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. Commented Feb 5, 2019 at 3:00

3 Answers 3

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);
Sign up to request clarification or add additional context in comments.

4 Comments

Just a tiny typo: typedef wasn't removed in the second snippet.
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?
@Z4ckZ3r0 No, there is no implicit conversion between struct and union types and other types
@Groo Thanks, I missed that. Fixed.
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.

Comments

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.

Comments

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.