Come rendere la libreria C educativa di array dinamici meno dettagliata da utilizzare?

3

Mi piace davvero creare degli strumenti prima di iniziare qualche incarico, quindi per gli incarichi di programmazione C ho preparato la libreria dinamica degli array. Siamo limitati a utilizzare solo lo standard C99.

Nota che tutta la programmazione che sto descrivendo di seguito è, come da titolo della domanda, fatta per scopi educativi . Questo non è il codice di produzione, ma il codice che uso per esercitarmi nella programmazione. E AFAIK in particolare l'argomento in cui invierò i compiti a casa ha comunque nessuna regola per le librerie esterne .

Salterò intenzionalmente i dettagli di implementazione ora, la "libreria" ha il seguente formato (interfaccia, se lo desideri):

typedef struct {

  //How many elements are in array
  // READ ONLY!!! Do not change
  size_t length;
  /** all the rest is private, do not touch **/
    ... omitted for brevity ...
} Array;

/** Creates new empty array. The returned pointer must be 
 * correctly destroyed using array_destroy.
 * Inital size is allowed to be zero
 *   **/
Array* array_create(size_t elm_size, size_t initalSize);
/**
 * Allocate memory so that 'count' ENTRIES fits into the array.
 * If the array already has enough memory, nothing happens.
*/
void array_reserve(Array* a, size_t count);
/** Expands the array to required size. The value of
 * new fields is undefined. There is little reason to use
 * this instead of array_push or array_reserve */ 
void array_expand(Array* a, size_t length);
/**
 * Adds an element at the end of the array, realocating memory if necesary **/
void array_push(Array* a, const void* element);
/**
 * Sets value at offset in array. This ofset MUST be valid.
 * The 'element' must be pointer to correct ammount of memory, that is
 * the correct type */  
void array_set(Array* a, size_t offset, const void* element);

Ora sembrava bello, ma ho iniziato a usarlo e non è molto divertente. Gli array semplici sono OK, immagino ...

   int numbers[] = {5,4,3,2,1};
   Array* ar = array_create(sizeof(int), 0);
   // Oh, one thing that sucks is that you can't add literals
   // because you can't provide pointer to them
   array_push(ar, numbers+0);
   array_push(ar, numbers+1);
   ...
   array_push(ar, numbers+4);


   int number;
   // This is super cool macro
   // Remember this is gcc with -pedantic -Wall  -std=c99
   AR_FOREACH(number, ar, int) {
     printf("Number: %d\n", number);
   }

Che stampa:

Number: 5
Number: 4
Number: 3
Number: 2
Number: 1

Ma diventa davvero complicato con gli array multidimensionali. Voglio usare la mia "libreria" per le matrici. Ora, solo per rendere Array di Array s e mettere un array all'interno, devo fare questo:

   Array* array2d = array_create(sizeof(Array*), 0);
   // Matrix 3x3
   // first fill up
   for(short row = 0; row<3; ++row) {
     Array* tmp = array_create(sizeof(int), 3);
     array_push(array2d, &tmp);
     for(short col = 0; col<3; ++col) {
       array_push(tmp, numbers+row*3+col);
     }
   }
   // The double * here is just plain crazy...
   printf("Final dimensions: %d rows, %d columns.\n", array2d->length, (*(Array**)array_get(array2d, 0))->length);
   // Set some value at X Y
   const size_t x = 1;
   const size_t y = 1;
   const int value = 666;
   array_set(*(Array**)array_get(array2d, y), x, &value);

   Array* row = NULL;
   AR_FOREACH(row, array2d, Array*) {
     AR_FOREACH(number, row, int) {
         printf("%2d ", number);
     }
     printf("\n");
   }
   // Finally destroy all sub items and the array itself
   // Note that the foreach loop here helps a lot by already changing Array** to Array*
   AR_FOREACH(row, array2d, Array*) {
     array_destroy(*row);
   }

Nota che in realtà ho ricevuto diversi segfault prima che questo esempio funzionasse. Tutti gli errori sono venuti da me facendo a molte / non abbastanza operazioni di dereferenziazione. Non ci sono stati errori dalla libreria. L'intero *(Array**)array_get(array2d, y) è totalmente pazzesco.

Voglio usare questa libreria per programmare semplici operazioni a matrice, ma in primo luogo posso renderla meno dettagliata? L'unica cosa che viene in mente è scrivere manualmente tutti i metodi array2d_XXX che funzionano con due indici.

Esistono macro C che potrebbero aiutarmi?

    
posta Tomáš Zato 15.11.2016 - 22:20
fonte

1 risposta

0

In primo luogo, ritengo che i membri flessibili dell'array siano pertinenti per il tuo compito.

Quindi dovresti utilizzare molte più funzioni inline , ovvero static inline funzioni definite in alcuni file di intestazione. Sono in pratica leggibili come funzioni e veloci come le macro. A proposito, è pratica comune avere alcune intestazione che dichiarano funzioni static inline , e includendo se stessa un'altra intestazione "privata" o "implementazione" che le implementa. Quindi l'intestazione pubblica che dichiara solo tipi e funzioni è abbastanza leggibile.

Sembra che tu voglia creare una contenitore libreria per C. Vedi questa domanda . Potresti trarre ispirazione da SGLIB (codice qui ) che ha diverse macro piuttosto lunghe estese a diverse definizioni (e creando nuovi simboli usando la macro concatenazione del preprocessore. ).

Hai pensato di avere una funzione push che restituisce il nuovo array o modifica uno esistente? Per esempio. ar = array_push(ar, numbers+1); o array_push(&ar, numbers+1); ?

BTW, se si tratta di alcuni compiti a casa, potresti chiedere al tuo insegnante se sei autorizzato a utilizzare le librerie di contenitori esistenti, che IMHO è molto meglio che crearne di tuoi. Progettare e implementare ex nihilo una buona libreria contenitore riutilizzabile in C ti richiederà almeno diversi mesi (e forse anche diversi anni) di lavoro.

Infine, non sono sicuro che le matrici debbano essere implementate come matrici dinamiche di array dinamici (perché ciò non impone la proprietà che tutte le file abbiano la stessa lunghezza). Considererei invece le matrici come alcuni tipo di dati astratti e ho un'operazione matrix_get(m,i,j) per ottenere l'elemento matrice m i, j (probabilmente una certa static inline funzione che dà m->array[m->width*i+j] - forse dopo alcuni test di validità che controllano m , i e j ).

Un altro approccio possibile è un metaprogramming : scriverai qualche script (magari usando un macroprocessore generico come GPP o m4 ) o programma che emette un codice C adatto alle tue esigenze (per esempi di generatori di codice C, vedi rpcgen & bisonte ). Quindi, dato un nome di tipo element_t (forse typedef double element_t; ) il tuo generatore emetterebbe il codice per matrici di tale tipo, e il codice generato includerebbe alcune funzioni o macro di matrix_get_element(m,i,j) ben digitato. Pertanto, se fornisci double come argomento o input per il tuo generatore, genererebbe un po 'di% tipo% di% e il codice C di matrix_double_t .

BTW, SICP è un libro molto utile da leggere (è un'eccellente e liberamente disponibile introduzione alla programmazione, e darti un sacco di concetti e terminologia eccellenti) anche se non si tratta di C.

Inoltre, per scorrere all'interno dei contenitori, potresti avere qualche iteratore tipo astratto, o usare alcuni callback approccio (poiché C non ha chiusure ). Guarda (almeno per ispirazione) nei tipi di dati Glib (codice here ).

PS. Per i compiti a casa, se il tuo insegnante lo consente (e IMHO dovrebbe consentirlo), dovresti davvero utilizzare meglio alcune librerie contenitore (questo rispondi a molti di essi) e concentrati sul problema dei compiti a casa. Se non ti è consentito utilizzare materiale esistente, assicurati di cercare come ispirazione all'interno del codice di software gratuito librerie simile ai tuoi compiti e menzionarli. Quindi, guarda almeno all'interno del codice di SGLIB e di Glib prima di scrivere la tua cosa (e assicurati di citarli).

Non dimenticare di compilare con tutti gli avvertimenti e amp; informazioni di debug ( matrix_get_double(matrix_double_t*m, int i, int j) se si utilizza GCC su Linux) e utilizzare valgrind

    
risposta data 16.11.2016 - 06:59
fonte

Leggi altre domande sui tag