• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Funkcją łącząca tablice (zmienna liczba parametrów)

VPS Starter Arubacloud
0 głosów
257 wizyt
pytanie zadane 1 czerwca 2020 w C i C++ przez Hubertius Bywalec (2,970 p.)
edycja 1 czerwca 2020 przez Hubertius

Hej :)

Muszę do pewnego zadania rozpisać funkcję łączącą poszczególne tablice w jedną. Tutaj podaję fragment treści zadania związanej z tą funkcją:

Napisz i przetestuj funkcję łączącą N tablic w jedną o następującym wywołaniu:

int* result = connect(data_type_int, 2, (int []){18, -13, -10, -1}, (int []){-7, 1, 20, -2, -1});
Funkcja ma przyjmować w parametrze:

typ danych,
liczbę tablic oraz kolejne tablice; koniec danych w tablicy będzie oznaczony wartością -1.
Wartość zwracana:

W przypadku sukcesu funkcja powinna zwrócić adres zaalokowanej pamięci na tablicę wynikową,
NULL w przypadku błędnych danych wejściowych lub w przypadku niepowodzenia alokacji pamięci.
Funkcja powinna obsługiwać następujące typy danych: short, int, float, double i long. Typ danych ma być przekazany do funkcji za pomocą typu wyliczeniowego o nazwie data_type_t (umieść go w pliku nagłówkowym data_type.h.), a pola powinny mieć nazwy zgodne ze wzorcem: data_type_NAZWATYPU (np. data_typ_short).

Przyjmij następującą zależność nazwa typu data_type_t i jej wartości:

0 - short
1 - int
2 - float
3 - double
4 - long

Oto moja funckcja:

void * connect(enum data_type_t dir,int how_many, ... )
{
    if( dir < 0 || dir > 4 || how_many <= 0)
    {
        return NULL;
    }
    va_list lista;
    va_start ( lista, how_many );
    if( dir == 0 )
    {
        short * pointer;
        int i;
        int sum_of_all_elements = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista,short *);
            int j = 0;
            while( *(pointer + j) != -1 )
            {
                j++;
            }
            j--;
            sum_of_all_elements = j;
        }
        va_end(lista);
        short * wynikowa = (short * ) malloc(sizeof(short) * (sum_of_all_elements + 1) );
        if( wynikowa == NULL )
        {
            return NULL;
        }
        va_list lista2;
        va_start(lista2,how_many);
        int itr = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista2,short *);
            int j = 0;
            while( *(pointer + j) != -1  )
            {
                *(wynikowa + itr) = *(pointer + j);
                itr++;
                j++;
            }
        }
        *(wynikowa + sum_of_all_elements) = -1;
        va_end(lista2);
        return wynikowa;
    }
    else if( dir == 1 )
    {
        int * pointer;
        int i;
        int sum_of_all_elements = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista,int *);
            int j = 0;
            while( *(pointer + j) != -1 )
            {
                j++;
            }
            j--;
            sum_of_all_elements = j;
        }
        va_end(lista);
        int * wynikowa = (int * ) malloc(sizeof(int) * (sum_of_all_elements + 1) );
        if( wynikowa == NULL )
        {
            return NULL;
        }
        va_list lista2;
        va_start(lista2,how_many);
        int itr = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista2,int *);
            int j = 0;
            while( *(pointer + j) != -1  )
            {
                *(wynikowa + itr) = *(pointer + j);
                itr++;
                j++;
            }
        }
        *(wynikowa + sum_of_all_elements) = -1;
        va_end(lista2);
        return wynikowa;
    }
    else if( dir == 2 )
    {
        float * pointer;
        int i;
        int sum_of_all_elements = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista,float *);
            int j = 0;
            while( *(pointer + j) != -1 )
            {
                j++;
            }
            j--;
            sum_of_all_elements = j;
        }
        va_end(lista);
        float * wynikowa = (float * ) malloc(sizeof(float) * (sum_of_all_elements + 1) );
        if( wynikowa == NULL )
        {
            return NULL;
        }
        va_list lista2;
        va_start(lista2,how_many);
        int itr = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista2,float *);
            int j = 0;
            while( *(pointer + j) != -1  )
            {
                *(wynikowa + itr) = *(pointer + j);
                itr++;
                j++;
            }
        }
        *(wynikowa + sum_of_all_elements) = -1;
        va_end(lista2);
        return wynikowa;
    }
    else if( dir == 3 )
    {
        double * pointer;
        int i;
        int sum_of_all_elements = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista,double *);
            int j = 0;
            while( *(pointer + j) != -1 )
            {
                j++;
            }
            j--;
            sum_of_all_elements = j;
        }
        va_end(lista);
        double * wynikowa = (double * ) malloc(sizeof(double) * (sum_of_all_elements + 1) );
        if( wynikowa == NULL )
        {
            return NULL;
        }
        va_list lista2;
        va_start(lista2,how_many);
        int itr = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista2,double *);
            int j = 0;
            while( *(pointer + j) != -1  )
            {
                *(wynikowa + itr) = *(pointer + j);
                itr++;
                j++;
            }
        }
        *(wynikowa + sum_of_all_elements) = -1;
        va_end(lista2);
        return wynikowa;
    }
    else
    {
        long * pointer;
        int i;
        int sum_of_all_elements = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista,long  *);
            int j = 0;
            while( *(pointer + j) != -1 )
            {
                j++;
            }
            j--;
            sum_of_all_elements = j;
        }
        va_end(lista);
        long  * wynikowa = (long  * ) malloc(sizeof(long) * (sum_of_all_elements + 1) );
        if( wynikowa == NULL )
        {
            return NULL;
        }
        va_list lista2;
        va_start(lista2,how_many);
        int itr = 0;
        for(i = 0; i < how_many;i++)
        {
            pointer = va_arg(lista2,long *);
            int j = 0;
            while( *(pointer + j) != -1  )
            {
                *(wynikowa + itr) = *(pointer + j);
                itr++;
                j++;
            }
        }
        *(wynikowa + sum_of_all_elements) = -1;
        va_end(lista2);
        return wynikowa;
    }
    return NULL;
}

I jeszcze rozpisany w dołączonym do programu pliku nagłówkowym typ wyliczeniowy (enum):

#ifndef DATA_TYPE_H_INCLUDED
#define DATA_TYPE_H_INCLUDED

enum data_type_t  {data_type_short = 0, data_type_int = 1,data_type_float = 2, data_type_double = 3, data_type_long = 4};

#endif // DATA_TYPE_H_INCLUDED

Na chwilę obecną wyrzuca mi już całe zadanie na pierwszym teście:

### Testy jednostkowe ### 
TEST 1: Sprawdzanie poprawności działania funkcji connect dla tablic typu float (limit sterty ustawiono na 105 bajtów)⏎
Wynik: PORAŻKA: Funkcja connect() powinna zwrócić NULL⏎
       Sprawdź funkcję testującą TEST1(void) z pliku unit_test_v2.c, w linii 57⏎
*** Test przerwany ***⏎
### RLDebug :: Analiza wycieku zasobów ### 
Wszystkie bloki pamięci zostały pomyślnie zwolnione - brak wycieków.⏎
Wszystkie pliki zostały zamknięte.⏎
main: rdebug.c:1177: rldebug_show_leaked_resources: Assertion `memory_leaked == rbase.current_heap_size' failed.⏎
Program przerwany; kod błędu=134 (Sygnał SIGABRT)

Test został przerwany; Program PRZERWANY; miał zwrócić wartość 0 a zakończył się sygnałem SIGABRT

A tutaj jeszcze przedstawiam dokładnie rozpisany test:

float expected_result[] = {9.385853, -7.621314, 5.236408, 7.880167, -2.575675, 18.044909, -0.544349, -3.259939, -16.541740, -13.286819, -19.258662, 7.845092, 0.622868, -6.778655, -3.590870, 4.721692, -1.250231, 4.138973, 10.415007, -11.544492, -9.097682, 19.849394, 9.224041, 11.988193, 15.535914, 16.962027, 17.363107, 11.490503, -15.415687, -10.801188, -1.919569, -15.331628, -3.184014, 10.258204, 19.857382, -8.252181, 1.993204, -13.460391, -1.000000};
            
                    float* result = connect(data_type_float, 9, (float []){9.385853, -7.621314, 5.236408, 7.880167, -1.000000}, (float []){-2.575675, 18.044909, -0.544349, -3.259939, -1.000000}, (float []){-16.541740, -13.286819, -19.258662, 7.845092, 0.622868, -1.000000}, (float []){-6.778655, -3.590870, 4.721692, -1.250231, 4.138973, -1.000000}, (float []){10.415007, -11.544492, -9.097682, 19.849394, 9.224041, -1.000000}, (float []){11.988193, 15.535914, 16.962027, -1.000000}, (float []){17.363107, 11.490503, -15.415687, -10.801188, -1.919569, -1.000000}, (float []){-15.331628, -3.184014, 10.258204, 19.857382, -1.000000}, (float []){-8.252181, 1.993204, -13.460391, -1.000000});
            
                    test_error(result == NULL, "Funkcja connect() powinna zwrócić NULL");
            
                    onerror_terminate(); // przerwnie wszystkich testów jednostkowych (np. coś jest mocno nie tak z kodem)
            
                    if (result != NULL)
                    {
                        int ok = 1;
                
                        for (int i = 0; i < 39; ++i) 
                            if (*(result + i) != *(expected_result + i))
                                ok = 0;
                
                        if (!ok)
                        {
                            printf("Oczekiwany wynik:\n");
                
                            for (int i = 0; i < 39; ++i) 
                                printf("%f", *(expected_result + i));
                
                            printf("Otrzymany wynik:\n");
                            for (int i = 0; i < 39; ++i) 
                                printf("%f", *(result + i));
                        }
                
                        test_error(ok == 1, "Wartość zwrócona przez funkcję connect() jest nieprawidłowa");
                
                        free(result);
                    }
                    test_no_heap_leakage();
                    onerror_terminate(); // przerwnie wszystkich testów jednostkowych (np. coś jest mocno nie tak z kodem)

Tak więc coś ostro poknociłem chyba w kodzie. Czy mój sposób rozumowania wyżej tej funkcji tak jak to napisałem jest poprawny? Ponadto czy brakuje jakiś zabezpieczeń? No i dlaczego program przerywa działanie dla tego testu? Z góry dziękuję za odpowiedzi.  :)

komentarz 1 czerwca 2020 przez mokrowski Mędrzec (156,220 p.)
Przecież powtarzasz te same sekcje kodu. Jest pewne że będziesz miał w takim przypadku jawne i niejawne błędy. To łamie regułę DRY.
komentarz 1 czerwca 2020 przez Hubertius Bywalec (2,970 p.)
A jak mogę tego uniknąć? By nie musieć rozpisywać tego samego kodu do de facto 5 if-ów?
komentarz 1 czerwca 2020 przez mokrowski Mędrzec (156,220 p.)
W języku C, od powtarzalnego kodu lub różnicowanego przez typy, masz makra. Zrób poprawne makro, które otrzyma typ danych i wygeneruje pożądany kod.
komentarz 1 czerwca 2020 przez Hubertius Bywalec (2,970 p.)
@mokrowski Znasz może przykład na internecie, na podstawie którego bym się tego nauczył? Bo szukam de facto czegokolwiek (moje doświadczenie z makrami to de facto zastępowanie pewnych liczb makrem)?

1 odpowiedź

0 głosów
odpowiedź 1 czerwca 2020 przez mokrowski Mędrzec (156,220 p.)

Ok... tu masz ideowe rozwiązanie. Jak widać, ciała poszczególnych funkcji, generowane są w makrze. Jako argument, makro otrzymuje typ.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>

#define LEN_TYPE_TABLE(TYPE) \
static size_t len_table_##TYPE(const TYPE * table) {    \
    size_t size = 0;                                    \
    while (*table++ != -1) {                            \
        ++size;                                         \
    }                                                   \
    return size;                                        \
}

LEN_TYPE_TABLE(short)
LEN_TYPE_TABLE(int)
LEN_TYPE_TABLE(float)
LEN_TYPE_TABLE(double)
LEN_TYPE_TABLE(long)

static size_t sum_length(const size_t * table, size_t size) {
    size_t sum = 0;
    while (size--) {
        sum += table[size];
    }
    return sum;
}

static void calculate_length_tables_int(size_t * table, size_t how_many, const va_list * arg_list) {
    for (size_t i = 0; i < how_many; ++i) {
        table[i] = len_table_int(va_arg(*arg_list, const int *));
    }
}

static void * copy_data(void * dst, const void * src, size_t size, size_t data_size) {
    memcpy(dst, src, size * data_size);
    return (char *)(dst) + (size * data_size);
}

static void args_concat(int * dst, size_t * length_table, size_t size, const va_list * arg_list) {
    for (size_t i = 0; i < size; ++i) {
        dst = copy_data(dst, va_arg(*arg_list, const int *), length_table[i], sizeof(int));
    }
}

int * connect(size_t how_many, ...) {
    va_list arg_list;
    va_list copy_arg_list;
    va_start(arg_list, how_many);
    va_copy(copy_arg_list, arg_list);

    size_t * length_table = malloc(how_many * sizeof(*length_table));

    if (length_table == NULL) {
        goto EXIT1;
    }
    calculate_length_tables_int(length_table, how_many, &copy_arg_list);

    size_t size = sum_length(length_table, how_many);
    int * table = malloc(size * sizeof(*table));

    if (table == NULL) {
        goto EXIT2;
    }

    args_concat(table, length_table, how_many, &arg_list);

    free(length_table);

    va_end(copy_arg_list);
    va_end(arg_list);
    return table;

EXIT2:
    free(length_table);
EXIT1:
    return NULL;
}

int main(void) {
    int * table =  connect(2, (int[]){1, 2, 3, 4, 5, -1}, (int[]){10, 2, -1});
    for (int i = 0; i < 7; ++i) {
        printf("%d ", table[i]);
    }
    free(table);
    return 0;
}

Oczywiście zadania za Ciebie nie rozwiązywałem....

Poczytaj jeszcze o X-Macro.

komentarz 2 czerwca 2020 przez Hubertius Bywalec (2,970 p.)

Okej, analizując to teraz... w define tworzymy poszczególne tablice dla danych typów danych

( czyli jak w zadaniu będzie wymagane ode mnie zrobienia to dla różnych typów danych to wystarczy, że rozpiszę 5 if-ów i w każdym będzie LEN_TYPE_TABLE(TYPE) dla poszczególnego typu wyliczeniowego. Dalej będę mógł działać na jednym kodzie.

Jednak to co się dzieje w samym makrze... okej, zwracany jest już rozmiar poszczególnej tablicy. Nie mogę tego znaleźć w kodzie, ale zgaduję że "table" to static i dlatego nie trzeba przekazywać go jako parametr do makra. Jednak w funkcji connect nie zauważyłem bezpośredniego wykorzystania:

LEN_TYPE_TABLE(int)
1
komentarz 2 czerwca 2020 przez mokrowski Mędrzec (156,220 p.)

W kodzie masz 3 funkcje zależne od typu danych. Jedna z nich, jest już opakowana makrem LEN_TYPE_TABLE(int). Przyjmując typ int, generuje Ci funkcję.

Teraz jak dodasz jeszcze enum wybierający typ, to zwykłym switch/case lub if'em, uruchomisz ładnie wskazaną funkcję. Przez to kod, będzie pozbawiony powtórzeń i wbrew pozorom łatwiejszy w utrzymaniu. Wiem... makra.. ale w C nie ma innego narzędzia lub wyjścia. Jak jeszcze doczytasz co to jest X-Macro, kod stanie się jeszcze bardziej czytelny (jeszcze raz .. w C nie masz wyjścia i do makr powinieneś przywyknąć. W C++ to samo zło :-/ )

PS. Jak znajdę chwilę, dodam może jeszcze jakieś elementy by zilustrować koncepcję.

komentarz 2 czerwca 2020 przez Hubertius Bywalec (2,970 p.)
edycja 2 czerwca 2020 przez Hubertius

No dobra. Spróbowałem teraz rozpisać dwa takie makra:

#define type0 short
#define type1 int
#define type2 float
#define type3 double
#define type4 long



#define LEN_TYPE_TABLE_CREATE(TYPE,ELEMENTS)                                    \
 TYPE * function_in_define(ELEMENTS)                                                                \
 {                                                                                                                              \
        TYPE * pointer = NULL;                                                                                 \
        pointer = (TYPE *) malloc (sizeof(TYPE) * (elements + 1));                         \
        return pointer;                                                                                               \
 }                                                                                                                           \

#define LEN_TYPE_TABLE_WRITE(POINTER,ELEMENTS)                                  \
int write_to_allocated_table(POINTER,ELEMENTS)                                               \
{                                                                                                                               \
        int j = 0;                                                                                                             \
        while( j < ELEMENTS)                                                                                    \
        {                                                                                                                       \
            if(scanf("%d",POINTER + j) != 1)                                                                 \
            {                                                                                                                     \
                return 1;                                                                                                    \
            }                                                                                                                    \
            j++;                                                                                                               \
        }                                                                                                                    \

       *(POINTER + ELEMENTS) = -1

       return 0;                                                                                                           \
}                                                                                                                               \

Chciałem to rozpisać w bloczku kodowym, ale znak '\' nie chciał być tam gdzie powinien być. Generalnie te dwa #define-y byłyby na potrzebę dalszego zadania (do funkcji connect będę musiał przekazać od dwóch do czterech tablic o 5 możliwych typach danych).

Na chwilę obecną wywołując pierwszy tworzę sobie alokowaną dynamicznie tablicę (o type danych przekazanych w makrze). 

Drugi define zapisywałby mi poszczególne do niej elementy (aczkolwiek odnoszę wrażenie, że czegoś mi tutaj brakuje).

P.S  Odnoszę wrażenie, że nie wiem najważniejszego, czyli jak za pomocą makra zrobić, by nie musieć rozpisywać za każdym razem  w main 5 if-ów dla poszczególnych przypadków (tymi przypadkami będą akurat ilości tablic, które mają zostać użyte).

Chodzi mi o to, że na chwilę obecną mogę wywołać makro:

LEN_TYPE_TABLE_CREATE(short,elements);

, które to wywoła w sobie funkcję i zwróci pointer. Okej. Ale jak teraz za pomocą makra zrobić, by przypisać to co otrzymałem z makra (chodzi mi o to, aby nie robić 5 if-ów na:

short * pointer = LEN_TYPE_TABLE_CREATE(short,elements);
int * pointer = LEN_TYPE_TABLE_CREATE(int,elements);
float * pointer = LEN_TYPE_TABLE_CREATE(float,elements);
double * pointer = LEN_TYPE_TABLE_CREATE(double,elements);
long * pointer = LEN_TYPE_TABLE_CREATE(long,elements);

? Czy w ogóle mogę tak zrobić? Bo na tą chwilę dostaję taką oto informację przy próbie zapisania powyższej linijki dla short:


error: expected expression before 'short'

Co innego bez inicjalizacji i przypisywania, wtedy jest wszystko okej:

LEN_TYPE_TABLE_CREATE(short,elements);

 

1
komentarz 2 czerwca 2020 przez mokrowski Mędrzec (156,220 p.)

Po dodaniu makr do 3 funkcji, prostych i naiwnych testów, można osiągnąć coś takiego:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>

enum data_type_t {
	data_type_short = 0,
	data_type_int = 1,
	data_type_float = 2,
	data_type_double = 3,
	data_type_long = 4,
};

static void * copy_data(void * dst, const void * src, size_t size, size_t data_size) {
    memcpy(dst, src, size * data_size);
    return (char *)(dst) + (size * data_size);
}

#define LEN_TABLE_TYPE_IMPL(TYPE) \
static size_t len_table_##TYPE(const void * table) {    \
    const TYPE * table_type = (const TYPE *) table;	\
    size_t size = 0;                                    \
    while (*table_type++ != -1) {                       \
        ++size;                                         \
    }                                                   \
    return size;                                        \
}

LEN_TABLE_TYPE_IMPL(short)
LEN_TABLE_TYPE_IMPL(int)
LEN_TABLE_TYPE_IMPL(float)
LEN_TABLE_TYPE_IMPL(double)
LEN_TABLE_TYPE_IMPL(long)

#define CALCUALTE_LENGTH_TABLES_TYPE_IMPL(TYPE) \
static void calculate_length_tables_##TYPE(size_t * table, size_t how_many, const va_list * arg_list) { \
    for (size_t i = 0; i < how_many; ++i) {                                                             \
        table[i] = len_table_##TYPE(va_arg(*arg_list, const TYPE *));                                   \
    }                                                                                                   \
}

CALCUALTE_LENGTH_TABLES_TYPE_IMPL(short)
CALCUALTE_LENGTH_TABLES_TYPE_IMPL(int)
CALCUALTE_LENGTH_TABLES_TYPE_IMPL(float)
CALCUALTE_LENGTH_TABLES_TYPE_IMPL(double)
CALCUALTE_LENGTH_TABLES_TYPE_IMPL(long)

#define ARGS_CONCAT_TYPE_IMPL(TYPE) \
static void args_concat_##TYPE(void * dst, size_t * length_table, size_t size, const va_list * arg_list) { \
    TYPE * dst_type = (TYPE *) dst;									   \
    for (size_t i = 0; i < size; ++i) {									   \
        dst_type = copy_data(dst_type, va_arg(*arg_list, const int *), length_table[i], sizeof(int));	   \
    }													   \
}

ARGS_CONCAT_TYPE_IMPL(short)
ARGS_CONCAT_TYPE_IMPL(int)
ARGS_CONCAT_TYPE_IMPL(float)
ARGS_CONCAT_TYPE_IMPL(double)
ARGS_CONCAT_TYPE_IMPL(long)

static size_t sum_length(const size_t * table, size_t size) {
    size_t sum = 0;
    while (size--) {
        sum += table[size];
    }
    return sum;
}

int * connect(enum data_type_t dir, size_t how_many, ...) {
    va_list arg_list;
    va_list copy_arg_list;
    va_start(arg_list, how_many);
    va_copy(copy_arg_list, arg_list);

    size_t * length_table = malloc(how_many * sizeof(*length_table));

    if (length_table == NULL) {
        goto EXIT1;
    }

    size_t size;
    void * table;

    switch (dir) {
	    case data_type_short:
    		calculate_length_tables_short(length_table, how_many, &copy_arg_list);

    		size = sum_length(length_table, how_many);
    		table = malloc(size * sizeof(short));

    		if (table == NULL) {
        		goto EXIT2;
    		}

    		args_concat_short(table, length_table, how_many, &arg_list);
		break;

	    case data_type_int:
    		calculate_length_tables_int(length_table, how_many, &copy_arg_list);

    		size = sum_length(length_table, how_many);
    		table = malloc(size * sizeof(int));

    		if (table == NULL) {
        		goto EXIT2;
    		}

    		args_concat_int(table, length_table, how_many, &arg_list);
		break;

	    case data_type_float:
    		calculate_length_tables_float(length_table, how_many, &copy_arg_list);

    		size = sum_length(length_table, how_many);
    		table = malloc(size * sizeof(float));

    		if (table == NULL) {
        		goto EXIT2;
    		}

    		args_concat_float(table, length_table, how_many, &arg_list);
		break;

	    case data_type_double:
    		calculate_length_tables_double(length_table, how_many, &copy_arg_list);

    		size = sum_length(length_table, how_many);
    		table = malloc(size * sizeof(double));

    		if (table == NULL) {
        		goto EXIT2;
    		}

    		args_concat_double(table, length_table, how_many, &arg_list);
		break;

	    case data_type_long:
    		calculate_length_tables_long(length_table, how_many, &copy_arg_list);

    		size = sum_length(length_table, how_many);
    		table = malloc(size * sizeof(long));

    		if (table == NULL) {
        		goto EXIT2;
    		}

    		args_concat_long(table, length_table, how_many, &arg_list);
		break;

	    default:
		goto EXIT1;
    }

    free(length_table);

    va_end(copy_arg_list);
    va_end(arg_list);
    return table;

EXIT2:
    free(length_table);
EXIT1:
    return NULL;
}

int main(void) {
    /* Naiwne i niekompletne testy...
     * Jak widać koduję "na twardo" ilość elementów.
     * Nie było tego jednak w wymaganiach by pobierać tę wartość.
     * A do testów wystarczy.
     */
    // int
    int * table1 =  connect(data_type_int, 2, (int[]){1, 2, 3, 4, 5, -1}, (int[]){10, 2, -1});
    for (int i = 0; i < 7; ++i) {
        printf("%d ", table1[i]);
    }
    putchar('\n');
    free(table1);

    // float
    float * table2 = (float *)connect(data_type_float, 3, (float[]){3.4, 12.3, -1},
		    (float[]){1.1, 2.2, -1}, (float[]){2.72, 3.1, -1});
    for (int i = 0; i < 6; ++i) {
        printf("%f ", table2[i]);
    }
    putchar('\n');
    free(table2);

    // short
    short * table3 =  (short *)connect(data_type_short, 1, (short[]){12, 21, 13, -1});
    for (int i = 0; i < 3; ++i) {
        printf("%d ", table3[i]);
    }
    putchar('\n');
    free(table3);

    // long 
    long * table4 = (long *)connect(data_type_long, 2, (long[]){10001, 201345, 54665, -1},
		    (long[]){7898789, 999999, -1});
    for (int i = 0; i < 5; ++i) {
        printf("%ld ", table4[i]);
    }
    putchar('\n');
    free(table4);

    return 0;
}

Zwróć uwagę że kod w sekcjach case w zasadzie się... powtarza. Z dokładnością do nazwy typu w każdej z tych sekcji. Poza tym, uogólniłem argumenty niektórych funkcji, wiedząc że w języku C, konwersja z i do void *, przebiega transparentnie.

Jeśli kod w sekcjach case się powtarza to... (w C) makro :) To w zasadzie kończy Twoje zadanie. Można zrobić jeszcze więcej z użyciem tzw. X-Macro. Nie będzie tych powtarzających się *_IMPL a sam kod switch/case, skróci się do małych wywołań funkcji z tablicy. No ale to to już jak zrozumiesz kod poniżej:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>

enum data_type_t {
    data_type_short = 0,
    data_type_int = 1,
    data_type_float = 2,
    data_type_double = 3,
    data_type_long = 4,
};

static void * copy_data(void * dst, const void * src, size_t size, size_t data_size) {
    memcpy(dst, src, size * data_size);
    return (char *)(dst) + (size * data_size);
}

#define LEN_TABLE_TYPE_IMPL(TYPE) \
static size_t len_table_##TYPE(const void * table) {    \
    const TYPE * table_type = (const TYPE *) table;     \
    size_t size = 0;                                    \
    while (*table_type++ != -1) {                       \
        ++size;                                         \
    }                                                   \
    return size;                                        \
}

LEN_TABLE_TYPE_IMPL(short)
LEN_TABLE_TYPE_IMPL(int)
LEN_TABLE_TYPE_IMPL(float)
LEN_TABLE_TYPE_IMPL(double)
LEN_TABLE_TYPE_IMPL(long)

#define CALCUALTE_LENGTH_TABLES_TYPE_IMPL(TYPE) \
static void calculate_length_tables_##TYPE(size_t * table, size_t how_many, const va_list * arg_list) { \
    for (size_t i = 0; i < how_many; ++i) {                                                             \
        table[i] = len_table_##TYPE(va_arg(*arg_list, const TYPE *));                                   \
    }                                                                                                   \
}

CALCUALTE_LENGTH_TABLES_TYPE_IMPL(short)
CALCUALTE_LENGTH_TABLES_TYPE_IMPL(int)
CALCUALTE_LENGTH_TABLES_TYPE_IMPL(float)
CALCUALTE_LENGTH_TABLES_TYPE_IMPL(double)
CALCUALTE_LENGTH_TABLES_TYPE_IMPL(long)

#define ARGS_CONCAT_TYPE_IMPL(TYPE) \
static void args_concat_##TYPE(void * dst, size_t * length_table, size_t size, const va_list * arg_list) {  \
    TYPE * dst_type = (TYPE *) dst;					                                                	    \
    for (size_t i = 0; i < size; ++i) {                                                                     \
        dst_type = copy_data(dst_type, va_arg(*arg_list, const int *), length_table[i], sizeof(int));       \
    }                                                                                                       \
}

ARGS_CONCAT_TYPE_IMPL(short)
ARGS_CONCAT_TYPE_IMPL(int)
ARGS_CONCAT_TYPE_IMPL(float)
ARGS_CONCAT_TYPE_IMPL(double)
ARGS_CONCAT_TYPE_IMPL(long)

static size_t sum_length(const size_t * table, size_t size) {
    size_t sum = 0;
    while (size--) {
        sum += table[size];
    }
    return sum;
}

#define PROCESS_TABLE(TYPE) \
    calculate_length_tables_##TYPE(length_table, how_many, &copy_arg_list); \
                                                                            \
    size = sum_length(length_table, how_many);                              \
   	table = malloc(size * sizeof(TYPE));                                    \
                                                                            \
    if (table == NULL) {                                                    \
        goto EXIT2;                                                         \
    }                                                                       \
                                                                            \
    args_concat_##TYPE(table, length_table, how_many, &arg_list);


int * connect(enum data_type_t dir, size_t how_many, ...) {
    va_list arg_list;
    va_list copy_arg_list;
    va_start(arg_list, how_many);
    va_copy(copy_arg_list, arg_list);

    size_t * length_table = malloc(how_many * sizeof(*length_table));

    if (length_table == NULL) {
        goto EXIT1;
    }

    size_t size;
    void * table;

    switch (dir) {
    case data_type_short:
        PROCESS_TABLE(short)
        break;

    case data_type_int:
        PROCESS_TABLE(int)
        break;

    case data_type_float:
        PROCESS_TABLE(float)
        break;

    case data_type_double:
        PROCESS_TABLE(double)
        break;

    case data_type_long:
        PROCESS_TABLE(long)
        break;

    default:
        goto EXIT1;
    }

    free(length_table);

    va_end(copy_arg_list);
    va_end(arg_list);
    return table;

EXIT2:
    free(length_table);
EXIT1:
    return NULL;
}

int main(void) {
    /* Naiwne i niekompletne testy...
     * Jak widać koduję "na twardo" ilość elementów.
     * Nie było tego jednak w wymaganiach by pobierać tę wartość.
     * A do testów wystarczy.
     */
    // int
    int * table1 =  connect(data_type_int, 2, (int[]) {
        1, 2, 3, 4, 5, -1
    }, (int[]) {
        10, 2, -1
    });
    for (int i = 0; i < 7; ++i) {
        printf("%d ", table1[i]);
    }
    putchar('\n');
    free(table1);

    // float
    float * table2 = (float *)connect(data_type_float, 3, (float[]) {
        3.4, 12.3, -1
    },
    (float[]) {
        1.1, 2.2, -1
    }, (float[]) {
        2.72, 3.1, -1
    });
    for (int i = 0; i < 6; ++i) {
        printf("%f ", table2[i]);
    }
    putchar('\n');
    free(table2);

    // short
    short * table3 =  (short *)connect(data_type_short, 1, (short[]) {
        12, 21, 13, -1
    });
    for (int i = 0; i < 3; ++i) {
        printf("%d ", table3[i]);
    }
    putchar('\n');
    free(table3);

    // long
    long * table4 = (long *)connect(data_type_long, 2, (long[]) {
        10001, 201345, 54665, -1
    },
    (long[]) {
        7898789, 999999, -1
    });
    for (int i = 0; i < 5; ++i) {
        printf("%ld ", table4[i]);
    }
    putchar('\n');
    free(table4);

    return 0;
}

 

1
komentarz 3 czerwca 2020 przez mokrowski Mędrzec (156,220 p.)
edycja 3 czerwca 2020 przez mokrowski

Po dodaniu X-Macro, kod się upraszcza. Tylko poczytaj do czego służy X-Macro.

Jako przykład użycia, może być. Choć sam znalazł bym jeszcze kilka niezręczności...

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>

#define TYPES_COUNT 5U

#define TYPES_TABLE \
    X(data_type_short = 0,  short,  sizeof(short))  \
    X(data_type_int = 1,    int,    sizeof(int))    \
    X(data_type_float = 2,  float,  sizeof(float))  \
    X(data_type_double = 3, double, sizeof(double)) \
    X(data_type_long = 4,   long,   sizeof(long))


#define X(ENUM_TYPE, TYPE, SIZE_TYPE) ENUM_TYPE,
enum data_type_t { TYPES_TABLE };
#undef X

static void * copy_data(void * dst, const void * src, size_t size, size_t data_size) {
    memcpy(dst, src, size * data_size);
    return (char *)(dst) + (size * data_size);
}

#define LEN_TABLE_TYPE_IMPL(TYPE) \
static size_t len_table_##TYPE(const void * table) {    \
    const TYPE * table_type = (const TYPE *) table;     \
    size_t size = 0;                                    \
    while (*table_type++ != -1) {                       \
        ++size;                                         \
    }                                                   \
    return size;                                        \
}

#define X(ENUM_TYPE, TYPE, SIZE_TYPE) LEN_TABLE_TYPE_IMPL(TYPE)
    TYPES_TABLE
#undef X

#define CALCUALTE_LENGTH_TABLES_TYPE_IMPL(TYPE) \
static void calculate_length_tables_##TYPE(size_t * table, size_t how_many, const va_list * arg_list) { \
    for (size_t i = 0; i < how_many; ++i) {                                                             \
        table[i] = len_table_##TYPE(va_arg(*arg_list, const TYPE *));                                   \
    }                                                                                                   \
}

#define X(ENUM_TYPE, TYPE, SIZE_TYPE) CALCUALTE_LENGTH_TABLES_TYPE_IMPL(TYPE)
    TYPES_TABLE
#undef X

#define ARGS_CONCAT_TYPE_IMPL(TYPE) \
static void args_concat_##TYPE(void * dst, size_t * length_table, size_t size, const va_list * arg_list) {  \
    TYPE * dst_type = (TYPE *) dst;					                                                	    \
    for (size_t i = 0; i < size; ++i) {                                                                     \
        dst_type = copy_data(dst_type, va_arg(*arg_list, const int *), length_table[i], sizeof(int));       \
    }                                                                                                       \
}

#define X(ENUM_TYPE, TYPE, SIZE_TYPE) ARGS_CONCAT_TYPE_IMPL(TYPE)
    TYPES_TABLE
#undef X

#define X(ENUM_TYPE, TYPE, SIZE_TYPE) calculate_length_tables_##TYPE,
static void (* const table_calculate_length_tables[])(size_t *, size_t, const va_list *) = { TYPES_TABLE };
#undef X

#define X(ENUM_TYPE, TYPE, SIZE_TYPE) args_concat_##TYPE,
static void (* const table_args_concat[])(void *, size_t *, size_t, const va_list *) = { TYPES_TABLE };
#undef X

#define X(ENUM_TYPE, TYPE, SIZE_TYPE) SIZE_TYPE,
static const size_t table_size_type[] = { TYPES_TABLE };
#undef X

static size_t sum_length(const size_t * table, size_t size) {
    size_t sum = 0;
    while (size--) {
        sum += table[size];
    }
    return sum;
}

int * connect(enum data_type_t dir, size_t how_many, ...) {
    va_list arg_list;
    va_list copy_arg_list;
    va_start(arg_list, how_many);
    va_copy(copy_arg_list, arg_list);

    size_t * length_table = malloc(how_many * sizeof(*length_table));

    if (length_table == NULL || dir >= TYPES_COUNT) {
        goto EXIT1;
    }

    size_t size;
    void * table;

    table_calculate_length_tables[dir](length_table, how_many, &copy_arg_list);
    size = sum_length(length_table, how_many);
    table = malloc(size * table_size_type[dir]);

    if (table == NULL) {
        goto EXIT2;
    }

    table_args_concat[dir](table, length_table, how_many, &arg_list);

    free(length_table);

    va_end(copy_arg_list);
    va_end(arg_list);

    return table;

EXIT2:
    free(length_table);
EXIT1:
    return NULL;
}

int main(void) {
    /* Naiwne i niekompletne testy...
     * Jak widać koduję "na twardo" ilość elementów.
     * Nie było tego jednak w wymaganiach by pobierać tę wartość.
     * A do testów wystarczy.
     */
    // int
    int * table1 =  connect(data_type_int, 2, (int[]) {
        1, 2, 3, 4, 5, -1
    }, (int[]) {
        10, 2, -1
    });
    for (int i = 0; i < 7; ++i) {
        printf("%d ", table1[i]);
    }
    putchar('\n');
    free(table1);

    // float
    float * table2 = (float *)connect(data_type_float, 3, (float[]) {
        3.4, 12.3, -1
    },
    (float[]) {
        1.1, 2.2, -1
    }, (float[]) {
        2.72, 3.1, -1
    });
    for (int i = 0; i < 6; ++i) {
        printf("%f ", table2[i]);
    }
    putchar('\n');
    free(table2);

    // short
    short * table3 =  (short *)connect(data_type_short, 1, (short[]) {
        12, 21, 13, -1
    });
    for (int i = 0; i < 3; ++i) {
        printf("%d ", table3[i]);
    }
    putchar('\n');
    free(table3);

    // long
    long * table4 = (long *)connect(data_type_long, 2, (long[]) {
        10001, 201345, 54665, -1
    },
    (long[]) {
        7898789, 999999, -1
    });
    for (int i = 0; i < 5; ++i) {
        printf("%ld ", table4[i]);
    }
    putchar('\n');
    free(table4);

    return 0;
}

PS. W podsumowaniu, trochę poza samym zapytaniem. Pamiętaj że makra va_*, są zabronione w wielu projektach produkcyjnych np. dla szerokiej branży embedded. Tak więc (oprócz ćwiczenia), nie radził bym "roztaczać nad nimi zachwytu". Tak jak nad rodziną set_jump*(...). Słowo kluczowe goto w takim kontekście jak widzisz, jest jak najbardziej ok. To "proteza RAII" której nie ma C (a ma np. C++). No i widać z jakiego powodu w C++ odradza się użycie makr. Od takich operacji jak tu, w C++ są szablony. Niestety zaawansowane C == Makra :-/

Podobne pytania

+1 głos
1 odpowiedź 1,498 wizyt
0 głosów
2 odpowiedzi 114 wizyt
0 głosów
2 odpowiedzi 669 wizyt
pytanie zadane 28 października 2020 w JavaScript przez Wiciorny Ekspert (275,640 p.)

92,843 zapytań

141,784 odpowiedzi

320,859 komentarzy

62,177 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Wprowadzenie do ITsec, tom 2

Można już zamawiać tom 2 książki "Wprowadzenie do bezpieczeństwa IT" - będzie to około 650 stron wiedzy o ITsec (17 rozdziałów, 14 autorów, kolorowy druk).

Planowana premiera: 30.09.2024, zaś planowana wysyłka nastąpi w drugim tygodniu października 2024.

Warto preorderować, tym bardziej, iż mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy dodatkowe 15% zniżki! Dziękujemy zaprzyjaźnionej ekipie Sekuraka za kod dla naszej Społeczności!

...