Generische Datenstruktur (Template)



  • Super Danke,

    aber wie sehen nun die Methoden aus Add /remove und das generisch? Da der typ in den Methoden ja bekannt sein muss!?

    Grüße


  • Mod

    NullBockException schrieb:

    Super Danke,

    aber wie sehen nun die Methoden aus Add /remove und das generisch? Da der typ in den Methoden ja bekannt sein muss!?

    Grüße

    Ich sehe nicht ,wo du da ein Problem siehst. Oder ich verstehe deine Frage nicht. Wo wäre denn der große Unterschied zu meinem Konstruktorbeispiel, außer dass ein add wahrscheinlich noch ein Element vom Typ SEPPJ_LIST_TYPE möchte? Im Prinzip geht das Schreiben des Codes so wie bei den C++-Templates, bloß dass man stellenweise etwas kämpfen muss mit den Eigenschaften des Präprozessors (daher mein Namemangling über drei Ecken). Das was man bei Templates meistens T nennt ist hier SEPPJ_LIST_TYPE und was foo<T> ist, ist hier SEPPJ_NAME(foo). Automatische Templateparameterherleitung kann der C-Compiler natürlich nicht, daher muss man immer alles ausschreiben.

    P.S.: Du solltest dir natürlich im Klaren sein, dass die Makromethode auch etliche Nachteile hat. Zum Beispiel darf der Typ keine Leerzeichen enthalten, da brauchts vorher ein typedef. Und eine Bibliothek zu schreiben, die die Funktionen offen anbietet ist (wie bei C++-Templates auch, aus den gleichen Gründen) auch ziemlich schwierig.



  • Das Problem ist, das ich mich mit C++ Templates kaum (mehr) auskenne, nur Ansi c wenig mit makros gemacht habe.. mir fehlt da bisschen ne "denkhilfe"

    nehmen wir an ich habe eine Array, welches ich via Makro und typdef definiere:

    /* a generiv Buffer structure */
    #define QUEUE_CLASS(T, size) struct __queue_ ##T	\
    {																\
    	T buffer[size]; 	/* Buffer */							\
    	uint32_t  read;		/* points to the last write field */	\
    	uint32_t  write;	/* points to a free write field */		\
    }
    typedef QUEUE_CLASS( int, 5 )  IntArray;
    

    nun will ich zwei methoden haben a'la:

    Queue_In(ARRAY_INTANCE *foo, ARRAY_TYPE **elem);
    {
    .....
    }
    
    Queue_Out(ARRAY_INTANCE *foo, ARRAY_TYPE **elem);
    {
    .....
    }
    

    und weis nich wie ich das anstellen soll.. kannst du mir ne kleine denkhilfe geben, muss ich die ganze methode bzw. logik in einem makro machen!?



  • Nur mal rein prinzipiell: Muss es wirklich um jeden Preis C sein? Mit C++ wär das wesentlich einfacher und besser realisierbar...



  • Ja, muss es .. das ding wird auf nem Mircocontroller verwendet.... sonst würde ich kein son aufwand treiben:)

    Ich habe bereits nen "Ringpuffer" realsiiert, nur ist dieser fix getyped.. und ich will die implementierung für mehrer typen verwenden können, generisch machen , um redundanzen etc, zu vermeiden...


  • Mod

    NullBockException schrieb:

    Ja, muss es .. das ding wird auf nem Mircocontroller verwendet.... sonst würde ich kein son aufwand treiben:)

    Gibt es fur den Mikrocontroller keinen C++-Compiler oder glaubst du nur, dass es deswegen C sein muss? Denn wenn der Mikrocontroller malloc kann, dann kann er auch die STL-Container, es braucht bloß einen passenden Compiler. Und wenn er kein malloc kann, dann nützt dir auch C nichts, um eine Liste zu bauen. Es gibt auch C++-Compiler die C erzeugen.

    Einen Ringpuffer (der auch ohne maloc geht) kannst du in beiden Sprachen schreiben und es wird auch der gleiche oder äquivalenter Code erzeugt werden.


  • Mod

    NullBockException schrieb:

    Das Problem ist, das ich mich mit C++ Templates kaum (mehr) auskenne, nur Ansi c wenig mit makros gemacht habe.. mir fehlt da bisschen ne "denkhilfe"

    nehmen wir an ich habe eine Array, welches ich via Makro und typdef definiere:

    /* a generiv Buffer structure */
    #define QUEUE_CLASS(T, size) struct __queue_ ##T	\
    {																\
    	T buffer[size]; 	/* Buffer */							\
    	uint32_t  read;		/* points to the last write field */	\
    	uint32_t  write;	/* points to a free write field */		\
    }
    typedef QUEUE_CLASS( int, 5 )  IntArray;
    

    nun will ich zwei methoden haben a'la:

    Queue_In(ARRAY_INTANCE *foo, ARRAY_TYPE **elem);
    {
    .....
    }
    
    Queue_Out(ARRAY_INTANCE *foo, ARRAY_TYPE **elem);
    {
    .....
    }
    

    und weis nich wie ich das anstellen soll.. kannst du mir ne kleine denkhilfe geben, muss ich die ganze methode bzw. logik in einem makro machen!?

    Ich sehe immer noch nicht, wo du da nicht den Zusammenhang zu meinem Beispiel siehst. Das ist doch genau das, was ich gezeigt habe, wie so etwas geht.



  • ich verstehe dein beispiel nich soganz bzw. sehe da keine genrische In und Out funktionen..


  • Mod

    static void SEPPJ_NAME(add_to_list) ( SEPPJ_NAME(list) *this, SEPPJ_LIST_TYPE element ) {};
    

    Wie du siehst, sollte man noch eine weitere Abstraktionsschicht einbauen, damit der Code lesbar bleibt.



  • Erst werden hier Templates mit oberhässlichen Makros nachgebaut, dann merkt man, dass das nicht das Wahre ist und empfiehlt C++. Tolles Publikum hier.

    Die Vorgehensweise mit den Makros hat ausserden den Nachteil des C++-typischen Binarybloats - was auf Microcontrollern besonders stört.

    Wenn schon C, dann richtig, und zwar in diesem Stil:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct Buffer {
      char *data;
      char *last_elem;
      char *alloc_end;
    } Buffer;
    
    Buffer buffer_new_bytes(size_t n)
    {
      Buffer g;
      g.data = g.last_elem = malloc(n);
      g.alloc_end = g.data + n;
      return g;
    }
    
    void buffer_free_pointer(Buffer *g)
    {
      free(g->data);
    }
    
    void * buffer_queue_bytes_in(Buffer *arr, void *elem, size_t n)
    {
      void *e;
    
      if (arr->last_elem == arr->alloc_end) return 0;
    
      memcpy (e = arr->last_elem, elem, n);
      arr->last_elem += n;
    
      return e;
    }
    
    void * buffer_queue_bytes_out(Buffer *arr, size_t n)
    {
      if (arr->last_elem == arr->data) return 0;
    
      arr->last_elem -= n;
      return arr->last_elem;
    }
    
    #define buffer_new(type, n)                     \
      buffer_new_bytes(sizeof(type)*n)
    #define buffer_queue_in(arr, e)                 \
      buffer_queue_bytes_in(&(arr), &(e), sizeof(e))
    #define buffer_queue_out(arr, e)                \
      (*((e*)buffer_queue_bytes_out(&(arr), sizeof(e))))
    #define buffer_free(arr)                \
      buffer_free_pointer(&(arr))
    
    int main()
    {
      Buffer g = buffer_new(int, 5);
      int a;
    
      printf("push: %d\n", a=2); buffer_queue_in(g, a);
      printf("push: %d\n", a=3); buffer_queue_in(g, a);
      printf("push: %d\n", a=5); buffer_queue_in(g, a);
    
      printf("pop: %d\n", buffer_queue_out(g, int));
      printf("pop: %d\n", buffer_queue_out(g, int));
      printf("pop: %d\n", buffer_queue_out(g, int));
    
      buffer_free(g);
    
      return 0;
    }
    

    Ausgabe:

    push: 2
    push: 3
    push: 5
    pop: 5
    pop: 3
    pop: 2
    

  • Mod

    Es war ganz ausdrücklich eine Methode ohne indirekte Speicherung der Elemente verlangt. Vermutlich kann der Mikrocontroller gar kein malloc. Da fällt mir leider nichts besseres ein als die Makroorgie und es würde mich auch sehr wundern, wenn es, bis auf die Schreibweise, besser ginge.



  • Ja , SeppJ hat es schon richtig erkannt! 'undefmarcos' : deine version wäre eine alternative, der mircocontroller kann malloc etc. aber ich will trozdem generisch arbeiten, wie seppj schon schön kommentiert hat..
    danke euch zwei.. werde mir morgen früh nochmal das ganze schön druchdenken:)

    gute nacht;)


Anmelden zum Antworten