Search code examples
cgeneric-programmingvoid-pointersc11

C - Populate a generic struct inside a function without malloc


I'm trying to build a generic function that can populate a struct without any dynamic memory allocation.

The following code is a naive example of what I'm trying to do. This code will not compile as incomplete type 'void' is not assignable.

Please note that this is a toy example to highlight my problems. I don't really want to convert colours; I just want to highlight that the structures will be different in data types and size.

#include <stdio.h>

typedef struct {
    int r;
    int g;
    int b;
} rgb_t;

typedef struct {
    float c;
    float m;
    float y;
    float k;
} cmyk_t;

typedef enum { RGB, CMYK } color_t;

void convert_hex_to_color(long hex, color_t colorType, void* const out) {
    if (colorType == RGB) {
        rgb_t temp = { 0 };
        // Insert some conversion math here....
        temp.r = 1;
        temp.g = 2;
        temp.b = 3;
        *out = temp; //< [!]
    } else
    if (colorType == CMYK) {
        cmyk_t temp = { 0 };
        // Insert some conversion math here....
        temp.c = 1.0;
        temp.m = 2.0;
        temp.y = 3.0;
        temp.k = 4.0;
        *out = temp; //< [!]
    }
}

int main(void) {
    // Given
    long hex = 348576;
    rgb_t mydata = { 0 };
    convert_hex_to_color(hex, RGB, (void*)(&mydata));

    // Then
    printf("RGB = %i,%i,%i\r\n", mydata.r, mydata.g, mydata.b);
    return 0;
}

For some additional context, I'm using C11 on an embedded system target.

What is the best[1] way to do this? Macro? Union?

Regards,
Gabriel

[1] I would define "best" as a good compromise between readability and safety.


Solution

  • The reason for the error is it is invalid to store via a void pointer: the compiler does not know what to store. You could cast the pointer as *(rgb_t *)out = temp; or *(cmyk_t *)out = temp;

    Alternately, you could define temp as a pointer to the appropriate structure type and initialize it directly from out, without the cast that is not needed in C:

    void convert_hex_to_color(long hex, color_t colorType, void *out) {
        if (colorType == RGB) {
            rgb_t *temp = out;
            // Insert some conversion math here....
            temp->r = 1;
            temp->g = 2;
            temp->b = 3;
        } else
        if (colorType == CMYK) {
            cmyk_t *temp = out;
            // Insert some conversion math here....
            temp->c = 1.0;
            temp->m = 2.0;
            temp->y = 3.0;
            temp->k = 4.0;
        }
    }
    

    Note that the cast is not needed in C:

    int main(void) {
        // Given
        long hex = 348576;
        rgb_t mydata = { 0 };
        convert_hex_to_color(hex, RGB, &mydata);
    
        // Then
        printf("RGB = %i,%i,%i\r\n", mydata.r, mydata.g, mydata.b);
        return 0;
    }