C - Заполняет общую структуру c внутри функции без malloc - PullRequest
3 голосов
/ 05 мая 2020

Я пытаюсь создать общую c функцию, которая может заполнять структуру без какого-либо динамического c выделения памяти .

Следующий код является наивным примером того, что Я пытаюсь сделать. Этот код не будет компилироваться как incomplete type 'void' is not assignable.

Обратите внимание, что это игрушечный пример, чтобы осветить мои проблемы. Я действительно не хочу преобразовывать цвета; Я просто хочу подчеркнуть, что структуры будут различаться по типам и размеру данных.

#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;
}

Для некоторого дополнительного контекста я использую C11 во встроенной системной цели.

Что такое лучший [1] способ сделать это? Макро? Union?

С уважением,
Габриэль

[1] Я бы определил «лучший» как хороший компромисс между удобочитаемостью и безопасностью.

Ответы [ 4 ]

7 голосов
/ 05 мая 2020

Причина ошибки в том, что невозможно сохранить через указатель void: компилятор не знает, что сохранить. Вы можете преобразовать указатель как *(rgb_t *)out = temp; или *(cmyk_t *)out = temp;

В качестве альтернативы вы можете определить temp как указатель на соответствующий тип структуры и инициализировать его непосредственно из out, без преобразования, которое не является необходимо в 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;
    }
}

Обратите внимание, что приведение не требуется в 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;
}
4 голосов
/ 06 мая 2020

«Лучший» способ - не смешивать несвязанные структуры в одной функции или в одной области памяти, если на то пошло. Это просто беспорядочный дизайн.

Если вам нужно сохранить единый API для двух разных форм данных, то одной из идей может быть безопасный функционально-подобный макрос. Вы можете подделать такой макрос, чтобы иметь синтаксис, аналогичный передаче данных по указателю

void convert_hex_to_color(long hex, type* data)

, но затем используйте C11 _Generic, чтобы фактически определить правильный тип для использования, а не использование опасных указателей на пустоту. Поскольку вы не можете передавать параметры макросам «по ссылке», вам придется прокрасться в назначении переменных. Пример:

#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;



void convert_hex_to_color(long hex, void* data);
  /* 
     Pretty prototype just for code documentation purposes. 
     Never actually defined or called - the actual macro will "mock" this function. 
  */

#define convert_hex_to_color(hex, output) ( *(output) = _Generic(*(output), \
  rgb_t:  (rgb_t){ .r=1, .g=2, .b=3 }, \
  cmyk_t: (cmyk_t){ .c=1.0, .m=2.0, .y=3.0, .k=4.0 } ) )


int main(void) {
    // Given
    long hex = 348576;
    rgb_t  myrgb  = { 0 };
    cmyk_t mycmyk = { 0 };

    convert_hex_to_color(hex, &myrgb);
    convert_hex_to_color(hex, &mycmyk);

    printf("RGB  = %i,%i,%i\r\n", myrgb.r, myrgb.g, myrgb.b);
    printf("CMYK = %f,%f,%f,%f\r\n", mycmyk.c, mycmyk.m, mycmyk.y, mycmyk.k);
    return 0;
}

Вывод:

RGB  = 1,2,3
CMYK = 1.000000,2.000000,3.000000,4.000000

Просто имейте в виду, что поддержка _Generic для квалификаторов типов (const et c) была нестабильной в C11 - одни компиляторы C11 обрабатывают const rgb_t иначе, чем rgb_t, другие обрабатывают их так же. Это было одно из «исправлений ошибок» в C17, поэтому используйте C17, если доступно.

4 голосов
/ 05 мая 2020
rgb_t temp = {0};

Таким образом, в стеке объявляется переменная типа rgb_t. Пока все хорошо, , хотя вам это не нужно 0 .

*out = temp;

Вот ваша проблема: в C вы можете копировать только память того же типа. Когда-либо. Это не имеет ничего общего с malloc, как предполагает ваш заголовок, это просто базовая спецификация языка c. Конечно, некоторые типы обеспечивают неявное приведение типов, но void* не является одним из них.

Итак, если вы копируете структуру (rgb_t справа), адрес назначения должен быть однотипные . Так что измените строку на это:

*(rgb_t *)out = temp;
3 голосов
/ 05 мая 2020

Вызов кадра: похоже, что вы хотите выполнить другую операцию в зависимости от типа, который вы передаете в эту функцию. Вместо того, чтобы использовать перечисление, чтобы сообщить ему, какой тип вы передаете, и ветвление на основе этого перечисления, используйте C11 _Generic для обработки этого, и вам даже не нужно явно указывать, какой тип находится в каждый звонок:

#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;

inline void convert_hex_to_color_rgb(long hex, rgb_t *const out) {
    (void) hex; // or whatever you're planning to do with 'hex'
    out->r = 1;
    out->g = 2;
    out->b = 3;
}

inline void convert_hex_to_color_cmyk(long hex, cmyk_t *const out) {
    (void) hex; // or whatever you're planning to do with 'hex'
    out->c = 1.0;
    out->m = 2.0;
    out->y = 3.0;
    out->k = 4.0;
}

#define convert_hex_to_color(hex, out) _Generic((out), \
        rgb_t *: convert_hex_to_color_rgb((hex), (rgb_t *)(out)), \
        cmyk_t *: convert_hex_to_color_cmyk((hex), (cmyk_t *)(out)) \
)

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

    // Then
    printf("RGB = %i,%i,%i\r\n", mydata.r, mydata.g, mydata.b);
    printf("CMYK = %f,%f,%f,%f\r\n", mydatac.c, mydatac.m, mydatac.y, mydatac.k);
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...