Pointer auf Daten in einem Struct mit Index



  • So dieses Problem ist etwas komplexer und ich gebe mir mühe es einfach zu beschreiben.

    Ich habe ein Programm welches eine Art Datenbank zur Verfügung stellt.

    Die API ist recht einfach.

    int setParam( configParameter_e eParam, int iIndex, void *pValue)
    int getParam( configParameter_e eParam, int iIndex, void *pValue)

    configParameter_e ist ein enum in dem alle Variablen drinnen stehen.
    In diesem abstrahierten Beispiel habe ich auf den Type mal verzichtet da es zu dem Problem nichts beiträgt. Ich gehe hier mal davon aus das der User den Typen kennt.

    Das Problem ist das die DB mit Structs aufgebaut sein soll und einige Parameter über Indexe angesprochen werden.

    Dabei soll ein Pointer auf die "Speicherstelle" mit übergeben werden.
    Das macht bei einem Struct mit Index etwas Probleme

    sDatabase.sDbGeneral.pcServer1
    und
    sDatabase.sDbGeneral.uiPort
    machen keine Probleme

    sDatabase.sDbProvider[i].pcComment
    ist da etwas komplizierter.

    Um das aus zu rechnen müsste ich eigentlich die Adresse von
    sDatabase.sDbProvider + i * offsetof(pcComment) rechnen.
    Das geht zwar aber ist übler Code.

    #include <stdio.h>
    #include <string.h>
    
    #define MAX_LEN_DB_VERSION 10
    #define MAX_ACCOUNTS 4
    #define MAX_DEVICES 2
    #define MAX_LEN_GENERAL_SERVER_1 10
    #define MAX_LEN_PROVIDER_COMMENT 10
    
    typedef struct _dbGeneral_s
    {
      char pcServer1[MAX_LEN_GENERAL_SERVER_1 + 1];
      unsigned int uiPort;
    } dbGeneral_s;
    
    typedef struct _dbProvider_s
    {
      char pcComment[MAX_LEN_PROVIDER_COMMENT + 1];
      unsigned int uiRegister;
    } dbProvider_s;
    
    typedef struct _database_s
    {
      dbGeneral_s sDbGeneral;
      dbProvider_s sDbProvider[MAX_ACCOUNTS];
    } database_s;
    
    typedef enum _configParameter_e
    {
      DB_VERSION = 0,
      GENERAL_SERVER_1,
      GENERAL_PORT,
      PROVIDER_COMMENT,
      LAST_PARAM
    } configParameter_e;
    
    typedef struct _paramDataMngr_s
    {
      configParameter_e param;
      void *pData;
      int (*fp_get)(int iIndex, void *pData, void *pValue);
      int (*fp_set)(int iIndex, void *pData, void *pValue);
    } paramDataMngr_s;
    
    /*static database node*/
    database_s sDatabase;
    
    int setInt(int iIndex, void *pData, void *pValue)
    {
      int i= *((int*)pValue);
      *((int*)pData)= i;
    }
    
    int setString(int iIndex, void *pData, void *pValue)
    {
      strcpy(pData, pValue);
    }
    
    int getInt(int iIndex, void *pData, void *pValue)
    {
      int i= *((int*)pData);
      *((int*)pValue)= i;
    }
    
    int getString(int iIndex, void *pData, void *pValue)
    {
      strcpy(pValue, pData); 
    }
    
    paramDataMngr_s sParamDataMngr[] = {
      {GENERAL_SERVER_1, &(sDatabase.sDbGeneral.pcServer1), &getString, &setString},
      {GENERAL_PORT, &(sDatabase.sDbGeneral.uiPort), &getInt, &setInt},
      {LAST_PARAM, NULL, NULL, NULL}
    };
    
    int setParam( configParameter_e eParam, int iIndex, void *pValue)
    {
      int i=0;
      while(sParamDataMngr[i].param != LAST_PARAM)
      {
        if (eParam == sParamDataMngr[i].param)
        {
          break;
        }
        i++;
      }
      sParamDataMngr[i].fp_set(iIndex, sParamDataMngr[i].pData, pValue);
    }
    
    int getParam(configParameter_e eParam, int iIndex, void *pValue)
    {
      int i=0;
      while(sParamDataMngr[i].param != LAST_PARAM)
      {
        if (eParam == sParamDataMngr[i].param)
        {
          break;
        }
        i++;
      }
      sParamDataMngr[i].fp_get(iIndex, sParamDataMngr[i].pData, pValue);
    }
    
    int main ( int argc, char **argv)
    {
      char cTest[10];
      int iTest;
      printf("Test Set String\n");
      setParam(GENERAL_SERVER_1, 0, "cTest1");
      printf("Test Get String\n");
      getParam(GENERAL_SERVER_1, 0, &cTest);
      printf("%s\n", cTest);
    
      printf("Test Set Int\n");
      iTest=1234;
      setParam(GENERAL_PORT, 0, &iTest);
      printf("Test Get Int\n");
      iTest=0;
      getParam(GENERAL_PORT, 0, &iTest);
      printf("%d\n", iTest);
    
      return 0;
    }
    

    Hier kommt nun das Problem:
    Der dritte Eintrag ist nicht möglich

    paramDataMngr_s sParamDataMngr[] = {
      {GENERAL_SERVER_1, &(sDatabase.sDbGeneral.pcServer1), &getString, &setString},
      {GENERAL_PORT, &(sDatabase.sDbGeneral.uiPort), &getInt, &setInt},
      {PROVIDER_COMMENT, &(sDatabase.sDbProvider[i].pcComment), &getString, &setString},
      {LAST_PARAM, NULL, NULL, NULL}
    };
    

    Dabei müsste ich das i eigentlich zur Laufzeit berechnen.
    Ich hoffe es versteht jemand das Problem.



  • Das Pronlem liegt daran, das du dein globales Array sParamDataMngr[] mit einer lokalen Variablen initialisieren willst.

    Warum setzt du den einen Wert nicht einfach per Hand:

    sParamDataMngr[3].fp_get = &(sDatabase.sDbProvider[i].pcComment);
    

    An der Stelle wo auch dein i gültig ist und den richtigen Wert hat.



  • Globale Variablen sind Müll.
    , &cTest); ist hier falsch.
    &getInt u.ä. ist redundant, getint allein reicht völlig aus.
    {GENERAL_PORT, &(sDatabase.sDbProvider[i].pcComment), &getInt, &setInt}, : was soll das bringen? int* wird erwartet und du übergibst char(*)[].
    Außerdem verwendest du hier zweimal den Identifikator GENERAL_PORT, das muss schiefgehen.



  • Ja mein Beispiel hat leider ein paar Macken.
    setString muss natürlich ein strcpy machen daher macht es schon Sinn.

    Beim Parameter mit pcComment sollte getString und setString so wie ein Sinnvoller Identifikator stehen .
    i ist nur als Platzhalter gedacht und soll auf das Problem zeigen. Zur compielzeit kann es so nicht aufgelöst werden.



  • Es ist auch viel zu aufwändig, in dem Manager-Array für jeden Datensatz die Zugriffsfunktionen jeweils nochmals zu definieren, einmalig pro Manager reicht völlig aus:

    #define MAX_ACCOUNTS 4
    
    typedef struct
    {
      char pc[100];
      int port;
    } Wert;  /* für Server+Provider die gleiche Struktur vorgeben (vereinfacht den Code) */
    
    typedef struct Manager {
      struct Manager *m;
      Wert *werte;
      void (*setParam)(struct Manager *m,int i,int port,void *p);
      void *(*getParam)(struct Manager *m,int i,int port);
    } Manager;
    
    void setParam(Manager *m,int i,int port,void *p)
    {
      if(port)
        m->werte[i].port=*(int*)p;
      else
        strcpy(m->werte[i].pc,p);
    }
    
    void *getParam(Manager *m,int i,int port)
    {
      if(port)
        return &m->werte[i].port;
      else
        return m->werte[i].pc;
    }
    
    int main ( int argc, char **argv)
    {
      int p;
      Wert datenbank[1+MAX_ACCOUNTS]={0}; /* Array mit 1x Server + mehrmals Provider */
      Manager m={&m,datenbank,setParam,getParam}; /* eine Manager-Instanz, die die "Datenbank" kennt */
                                                  /* d.h. in der Folge keine Referenz auf Datenbank mehr nötig! */
    
      /* Setzen der Werte */
      m.setParam(&m,0,0,"server");  /* 1.Element String */
      m.setParam(&m,0,1,(p=11,&p)); /* 1.Element Port   */
      m.setParam(&m,1,0,"prov1");   /* 2.Element String */
      m.setParam(&m,1,1,(p=22,&p)); /* 2.Element Port   */
      m.setParam(&m,2,0,"prov2");   /* 3.Element String */
      m.setParam(&m,2,1,(p=33,&p)); /* 3.Element Port   */
    
      /* Auslesen der Werte nach obigem Muster*/
      printf("%s %d\n",m.getParam(&m,0,0),*(int*)m.getParam(&m,0,1));
      printf("%s %d\n",m.getParam(&m,1,0),*(int*)m.getParam(&m,1,1));
      printf("%s %d\n",m.getParam(&m,2,0),*(int*)m.getParam(&m,2,1));
    
      return 0;
    }
    


  • Danke, das muss ich mir erst mal in ruhe durchlesen.
    Meine db hat eigentlich ca 200 Parameter,mal sehen wie das skaliert.
    Sind aber ein paar gute Ideen drinnen



  • So nun habe ich mir das Beispiel von Wutz mal genauer angesehen.

    Die Idee das man struct Wert für alle Daten nimmt hat durchaus eine gute Idee.
    Leider wird dadurch für jeden String zusätzlich noch ein Int im Speicher reserviert. Schlimmer wird es wenn ich nur ein Int haben will, dann wird noch ein char[100] reserviert. Unmöglich wäre es hier das man Werte mit unterschiedlicher Stringlänge hat.
    Im Worst Case würde mein Speicherverbrauch da durch um ein vielfacher größer werden.

    Die Idee mir dem Manager finde ich klasse!

    Was ich nicht ganz verstehe ist den void Parameter beim Set

    m.setParam(&m,0,1,(p=11,&p)); /* 1.Element Port   */
    

    Ist das hier ein Parameter "(p=11,&p)"?
    Diese Schreibweise ist mir unbekannt.



  • Ich könnte natürlich hin gehen

    typedef struct
    {
      char *pcString;
      int iInt;
    } Wert;
    

    definieren und beim starten mir malloc die pcString mit der richtigen Länge anlegen. (init)
    Leider schränkt mich das auch etwas ein. Mir wäre es recht wenn die Datenstruktur ohne Pointer aus kommt. Das macht das kopieren und wegschreiben der ganzen Struktur leichter.



  • Ich würde in den Strukturelementen keine Zeiger verwenden, zumal die selbst ja auch nochmal zusätzlichen Platz benötigen (sizeof(char*)) und um das free müsstest du dich ja dann auch noch kümmern. Dann lieber ein paar Bytes mehr spendieren.
    Eher wäre für das Strukturarray dann dyn. Speicher mittels calloc sinnvoll.

    Und das Manager-Objekt kannst du klaglos überall in deinem Code, der Konfigurationen benötigt, rumreichen und z.B. jeweils als Parameter übergeben.
    Und am Ende des Programms dann den evtl. o.g. Speicher wieder freigeben, auch das kann über den Manager mit einer eigenen Funktion geschehen.



  • decembersoul schrieb:

    Was ich nicht ganz verstehe ist den void Parameter beim Set

    m.setParam(&m,0,1,(p=11,&p)); /* 1.Element Port   */
    

    Ist das hier ein Parameter "(p=11,&p)"?
    Diese Schreibweise ist mir unbekannt.

    Hallo,

    das ist ein Einsatz des Komma-Operators, siehe z.B.:

    http://www.eskimo.com/~scs/cclass/int/sx4db.html

    MfG,

    Probe-Nutzer



  • m.setParam(&m,0,1,(p=11,&p));
    

    Das ist nur eine schnelle Hilfskrücke für den 4.Parameter, um einen int* darzustellen (mit einem konkreten Wert, und z.B. &11 funktioniert natürlich nicht).
    Der 4.Parameter muss ja vom Typ void* sein, int* ist dazu kompatibel. Im realen Programm steht dort dann natürlich was anderes, weniger kryptisches.



  • Wutz schrieb:

    Ich würde in den Strukturelementen keine Zeiger verwenden, zumal die selbst ja auch nochmal zusätzlichen Platz benötigen (sizeof(char*)) und um das free müsstest du dich ja dann auch noch kümmern. Dann lieber ein paar Bytes mehr spendieren.
    Eher wäre für das Strukturarray dann dyn. Speicher mittels calloc sinnvoll.

    Und das Manager-Objekt kannst du klaglos überall in deinem Code, der Konfigurationen benötigt, rumreichen und z.B. jeweils als Parameter übergeben.
    Und am Ende des Programms dann den evtl. o.g. Speicher wieder freigeben, auch das kann über den Manager mit einer eigenen Funktion geschehen.

    Leider habe ich dann immer noch das Problem das jeder "Wert" die selbe Stringlänge hat.
    Auch hat ein "Wert" der eigentlich nur ein Int beinhaltet eine Stringlänge.
    Um diese Limitierung kommt man bei dem Ansatz wohl nicht herum.


Anmelden zum Antworten