views:

61

answers:

4

Is it possible to embed a structure of varying type inside another structure in C?

Basically I want to do something like this.

struct A { int n; void *config; }

struct AConfig { int a; char *b; }
struct BConfig { int a; float b; }

const struct A table[] = {
    { 103, (void*)(struct AConfig){ 1932, "hello" } },
    { 438, (void*)(struct BConfig){ 14829, 33.4f } }
}

Is this possible in C or do I have to define the structures separately?

A: 

It would work if BConfig had a float pointer to b.

By the way, maybe you need to refactor your code to fit to C syntax.

#include <stdio.h>
#include <stdlib.h>

typedef struct { int n; void *config; } Config;

typedef struct { int a; char *b; } AConfig;
typedef struct { int a; float *b; } BConfig;

int main(void) {
        AConfig A;
        BConfig B;
        A.a= 103;
        A.b= "hello";
        B.a= 438;
        B.b=(float *) malloc (sizeof(float));
        *(B.b)= 33.4f;
        const Config table[] = {
                { A.a, (void *) A.b },
                { B.a, (void *) B.b }
        };
        printf("Hi\n");
        return 0;
}
KikoV
+3  A: 

No, it doesn't work like that. You need explicit storage for each structure:

struct A { int n; void *config; };

struct AConfig { int a; char *b; };
struct BConfig { int a; float b; };

struct AConfig ac = { 1932, "hello" };
struct BConfig bc = { 14829, 33.4f };

const struct A table[] = {
    { 103, &ac },
    { 438, &bc }
};

Edit:

Another possibility is to utilize a union and C99 (-std=c99) named initializers:

enum config_type { CT_INT, CT_FLOAT, CT_STRING };

union config_value {
    int int_value;
    float float_value;
    const char* string_value;
};

struct config {
    enum config_type ctype;
    union config_value cvalue;
};

struct config sys_config[] = {
    { CT_INT, { .int_value = 12 }}, 
    { CT_FLOAT, { .float_value = 3.14f }}, 
    { CT_STRING, { .string_value = "humppa" }}};

void print_config( const struct config* cfg ) {
    switch ( cfg->ctype ) {
        case CT_INT: 
            printf( "%d\n", cfg->cvalue.int_value ); break;      
        case CT_FLOAT:
            printf( "%f\n", cfg->cvalue.float_value ); break;
        case CT_STRING:
            printf( "%s\n", cfg->cvalue.string_value ); break;
        default:
            printf( "unknown config type\n" );
    }       
}
Nikolai N Fetissov
A: 

You may prefer a union. My union syntax is a little rusty, but something like this:

union config { char* c; float d; };
struct A {int n; int a; union config b;};

const struct A table[] = {
    {103, 1932, { .c = "hello" } },
    {14829, 438, { .d = 33.4f } }
};

You need C99 for the designated initalizer (the .c or .d in the table), and obviously some way to tell if you're accessing a char* or a float, but I assume you have that covered somewhere else.

Karl Bielefeldt
+1  A: 

You can use a union:

struct AConfig { int a; char *b; };
struct BConfig { int a; float b; };
struct A {
    int n;
    union {
        struct AConfig a;
        struct BConfig b;
    };
};

Note that a and b are in the exact same space in memory. So if you are going to use the A.a you should not use A.b and vice versa.

Since this is an anonymous union, you can reference both a and b as if they were direct fields of struct A:

struct A sa;
sa.n = 3;
sa.b.a = 4;
sa.b.b = 3.14;
R Samuel Klatchko