Forum: PC-Programmierung C Struct einzelne Elemente mit Funktion ändern


von Peda (Gast)


Lesenswert?

Hallo,
ich habe zwei C Dateien: in der einen solle der struct und 
Zugriffsfunktionen deklariert werden und in der Anderen der Zugriff 
erfolgen:

//structDatei.c
1
struct test
2
{
3
  int i;
4
  int x;
5
};
6
static struct test Test;
7
8
void setStruct_i(int value)
9
{
10
  Test.i = value;  
11
}
12
void setStruct_x(int value)
13
{
14
  Test.x = value;  
15
}

Das blöde ist jetzt das ich für jedes Struct-Element eine eigene set 
Fuktion schreiben muss.
Ich hätte gerne eine elegantere universelle Methode in etwa so:
void setStruct_int("parameter", int value)
{
  Test.“parameter“ = value;
}

„parameter“ gibt das Struct-Element an, welches geändert werden soll.
Hat jemand eine Idee wie man das in C realisieren kann?

von Cyblord -. (cyblord)


Lesenswert?

Peda schrieb:
> „parameter“ gibt das Struct-Element an, welches geändert werden soll.
> Hat jemand eine Idee wie man das in C realisieren kann?

Das geht über Arrays und den Index als enum.

ABER:
Warum willst du überhaupt setter wenn die so extrem allgemein sein 
sollen? Setter sind selten allgemein weil die bestimmte Invarianten 
garantieren sollen, die aber vom jeweiligen Datum abhängen.

Will ich einfach jeden beliebigen Zugriff zulassen, dann kann man auch 
gleich direkt auf die Variable zugreifen.

von ? DPA ? (Gast)


Lesenswert?

Willst du einen Parameter anhand eines Strings setzen, oder geht es 
wirklich nur um die Reduktion von Schreibarbeit?

In letzterem Fall könnte man ein Macro nehmen:
1
#define STRUCT_TEST_GENERIC_PUBLIC_MEMBERS \
2
  X(int, i) \
3
  X(int, x)
4
5
struct test
6
{
7
#define X(T, ID) T ID;
8
STRUCT_TEST_GENERIC_PUBLIC_MEMBERS
9
#undef X
10
};
11
static struct test Test;
12
13
#define X(T, ID) \
14
  void setStruct_ ## ID(T value_) \
15
  { \
16
    Test.ID = value_; \
17
  } \
18
  T getStruct_ ## ID() \
19
  { \
20
    return Test.ID; \
21
  }
22
STRUCT_TEST_GENERIC_PUBLIC_MEMBERS
23
#undef X

von Steinzeit (Gast)


Lesenswert?

Willkommen in der Zukunft. Lerne C++ und wende dieses an:
1
struct bla{
2
  uint8_t x;
3
4
  void setX(uint8_t x){
5
    this->x = x;
6
  }
7
}
1
bla variable;
2
variable.setX(100);

von pointer (Gast)


Lesenswert?

Peda schrieb:
> Ich hätte gerne eine elegantere universelle Methode in etwa so:
> void setStruct_int("parameter", int value)
> {
>   Test.“parameter“ = value;
> }

kannst du doch:
1
#include <stdio.h>                                                      
2
#include <stddef.h>                                                     
3
                                                                        
4
struct test {                                                           
5
   int x;                                                               
6
   int y;                                                               
7
};                                                                      
8
                                                                        
9
void                                                                    
10
set_some_value(int* data, int value)                                    
11
{                                                                       
12
   *data = value;                                                       
13
}
14
15
                                                                       
16
int                                                                     
17
main(void)                                                              
18
{                                                                       
19
   struct test t;                                                       
20
   set_some_value(&t.x, 1);                                             
21
   set_some_value(&t.y, 2);                                             
22
                                                                        
23
   return EXIT_SUCCESS;                                                 
24
}

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

cpp der C-Präprozessor kann man als Vorfahre von Word+VBA ansehen: ein 
programmierbares Textverarbeitungssystem das in der Lage ist den Text so 
umzuarbeiten dass am Schluss kompilierbares C (ohne, resp. mit 
aufgelösten Makros) rauskommt.

Beides ist in etwa ähnlich kryptisch und vergleichbar mit 
Verdauungssysteme: wenn man damit arbeitet klebt einem zum Schluss 
irgendwie 5che1**e an den Fingern.

---

Wenn man Sowas (structs mit Zugriffsfunktionen) systematisch und "viel" 
braucht (z.B. als gemeinsame Basis in heterogenen Systeme wo mit 
unterschiedlichen Programmiersprachen und/oder über verschiedene 
Architekturen Daten auszutauschen sind) ist man besser dran eine 
Datenbeschreibungssprache mit konziser/kompakter Syntax zu verwenden 
und davon per Converterprogramm den C-Code generieren zu lassen.
Das lässt sich dann gut auch mit Kompatibilitätschecks ausschmücken (f. 
Compiletime & Runtime) was sich beim Debugging in heterogenen Systemen 
sehr bezahlt macht.
BTDT, mehrmals & mit Erfolg

von Adam P. (adamap)


Angehängte Dateien:

Lesenswert?

pointer schrieb:
> struct test t;
> set_some_value(&t.x, 1);

Ja, das geht, aber dann gehört dir die Struct eh und dann kannst auch 
gleich
t.x = 1;
schreiben - falls du auf die Funktionsprüfung verzichten möchtest.

Peda schrieb:
> Das blöde ist jetzt das ich für jedes Struct-Element eine eigene set
> Fuktion schreiben muss.
> Ich hätte gerne eine elegantere universelle Methode

Also eigentlich, willst du deine Struktur vor falschem setzen 
schützen...
und Sie gehört nur dem Modul. OK.

Aber das kannst du auch anders lösen,
anstatt die Technik der Klassen aus C++ nachbilden zu wollen...
ja jetzt schreien gleich wieder alle, aber die Basis des allen ist doch 
die gleiche:

Man möchte verhindern, dass falsche Parameter gesetzt werden.
Doch das kann man auch mit einer set Fkt. lösen und nicht für jede 
Variable mit einer.
...Denn ob die Struktur nun static im Modul ist und man wird vom 
falschen
setzen der Werte gehindert - oder ob man Sie selbst besitzt oder auch 
nicht
und gehindert wird...kommt aufs gleiche rauß.

Siehe Anhang.

von Hmmm (Gast)


Lesenswert?

Peda schrieb:
> Ich hätte gerne eine elegantere universelle Methode

Sowas kannst Du mit offsetof() lösen.

von Cyblord -. (cyblord)


Lesenswert?

Adam P. schrieb:
> Also eigentlich, willst du deine Struktur vor falschem setzen
> schützen...
> und Sie gehört nur dem Modul. OK.

Aber nochmal: Was bringt es, eine Struktur "privat" zu halten um dann 
doch über eine generelle setter jeglichen Zugriff darauf zu erlauben?

von Adam P. (adamap)


Lesenswert?

Cyblord -. schrieb:
> "privat"

Sie ist nicht "privat", sie gehört dem Modul, es gibt sie nur 1x.

Das war ja sein wunsch.
Klar ist das inkonsistent.

Da würde noch ein "verwalter" hingehören, der jedem anforderer eine neue 
instanz gibt (speicherbereich: array of foo_t)

und dann durch ne queue per callback den zugriff verwaltet...aber hey.
er wollte doch nur ne lösung für sein problem um die verwaltung des 
zugriffs hatte er ja nicht gefragt ;)

Cyblord -. schrieb:
> jeglichen Zugriff darauf zu erlauben?

tut er ja dann nicht, wenn die werte nicht stimmen.

: Bearbeitet durch User
von Adam P. (adamap)


Lesenswert?

Cyblord -. schrieb:
> Aber nochmal: Was bringt es, eine Struktur "privat" zu halten um dann
> doch über eine generelle setter jeglichen Zugriff darauf zu erlauben?

Du nimmst dir halt das Interface und kannst es nutzen....falls du es 
richtig konfigurierst. Aber es existiert im besten fall nur einmal, oder 
als Array mit Job-Queue.

Wenn du eine COM oder UART verwenden willst, hast du auch eine Config. 
die du setzen musst. Aber das Objekt kann, muss aber nict bei dir 
liegen, das ist alles Design-Sache.

...so nebenbei:
Betriebssysteme erlauben nur den EINEN zugriff auf COM...warum?

Wenn man es doch so möchte, würde das doch auch gehen:
1
Task1  Task2
2
  \    /
3
   \  /
4
access_mgr
5
    |
6
    |
7
   UART

Mache ich auf dem µc jeden Tag, 1x I²C, aber 5 Devices.
Die Tasks fragen per DMA aber geben den Job in Auftrag und der Manager 
kümmert sich drum und gibt ne Callback.

Problem gelöst.

OK, das geht am Thema vorbei, aber wollt nur verdeutlichen, dass man das 
"Objekt" nicht immer besitzen muss...alles design sache.

---

Eigentlich wollte der TO nur wissen, wie man eine static gehaltene 
varibale im eigenen modul sicher setzen kann...
und um tipp arbeit zu ersparen, kommt ja je aufs gleiche rauß:

get_default_foo
// do anything with your foo
set_my_foo
// and hope ;)

: Bearbeitet durch User
von foobar (Gast)


Lesenswert?

> Ich hätte gerne eine elegantere universelle Methode in etwa so:
> void setStruct_int("parameter", int value)
> {
>   Test.“parameter“ = value;
> }

Die C Version davon:

struct.h:
1
enum StructElem { ELEM_I, ELEM_X };
2
void setStruct(enum StructElem what, int value);

struct.c
1
#include "struct.h"
2
3
static struct {
4
  int i;
5
  int x;
6
} test;
7
8
void setStruct(enum StructElem what, int value)
9
{
10
  switch (what)
11
  {
12
    case ELEM_I:  test.i = value;  break;
13
    case ELEM_X:  test.x = value;  break;
14
  }
15
}

main.c
1
#include "struct.h"
2
3
  ...
4
  setStruct(ELEM_I, 42);
5
  setStruct(ELEM_X, 0);

Falls der Datentyp von "value" je nach Element unterschiedlich ist (z.B. 
int oder double oder pointer), kann man das mit stdarg.h machen.

von Adam P. (adamap)


Lesenswert?

foobar schrieb:
> Die C Version davon:

elegante Idee....
aber ich frag mich ob das so intuitiv beim tippen ist?

will ich wirklich jedes mal angeben, was ich setzen will....
dazu kommt noch der overhead der funktionsaufrufe :

: Bearbeitet durch User
von Programmiersprachentheaterintendant (Gast)


Lesenswert?

foobar schrieb:
>> Ich hätte gerne eine elegantere universelle Methode in etwa so:
>> void setStruct_int("parameter", int value)
>> {
>>   Test.“parameter“ = value;
>> }
>
> Die C Version davon:
>
> struct.h:
>
1
> enum StructElem { ELEM_I, ELEM_X };
2
> void setStruct(enum StructElem what, int value);
3
>
>
> struct.c
>
1
> #include "struct.h"
2
> 
3
> static struct {
4
>   int i;
5
>   int x;
6
> } test;
7
> 
8
> void setStruct(enum StructElem what, int value)
9
> {
10
>   switch (what)
11
>   {
12
>     case ELEM_I:  test.i = value;  break;
13
>     case ELEM_X:  test.x = value;  break;
14
>   }
15
> }
16
>


Ja, auch solche CodePatterns kommen gerne vor in heterogenen Systemen.
Da dann auch oft die Anzahl Felder in den structs ergo die Anzahl 
Einträge für die Enums nicht klein bleibt, werden Wartung und 
Erweiterung solchen Codes sehr Disziplinlastig.

Deshalb: Datenbeschreibungsprache --> generierten Code um diese Daten.

von Adam P. (adamap)


Lesenswert?

Oder halt so...geht auch:

main.c
1
#include <stdio.h>
2
#include <stdlib.h>
3
4
#include "foo.h"
5
6
int main()
7
{
8
    bool ret;
9
10
    /* TEST 1 */
11
    ret = set_my_foo(&foo.i, 1400);
12
13
    if (ret)    printf("ret: ok\n");
14
    else        printf("ret: failed\n");
15
16
    ret = set_my_foo(&foo.x, 60);
17
18
    if (ret)    printf("ret: ok\n");
19
    else        printf("ret: failed\n");
20
21
    return 0;
22
}

foo.h
1
#ifndef FOO_H
2
#define FOO_h
3
4
#include <stdint.h>
5
#include <stdbool.h>
6
7
typedef struct foo_t
8
{
9
    int i;
10
    int x;
11
} foo_t;
12
13
extern foo_t foo;
14
15
bool set_my_foo(foo_t *src, int value);
16
17
#endif // FOO_H

foo.c
1
#include "foo.h"
2
3
foo_t foo;
4
5
bool set_my_foo(foo_t *src, int value)
6
{
7
    if ((&foo.i == &src->i) && (value > 1000))
8
        return false;
9
10
    if ((&foo.x == &src->x) && (value < 45))
11
        return false;
12
13
    foo.i = src->i;
14
    foo.x = src->x;
15
16
    return true;
17
}

von Rolf M. (rmagnus)


Lesenswert?

Adam P. schrieb:
> will ich wirklich jedes mal angeben, was ich setzen will....

Musst du doch sowieso.

von oli (Gast)


Lesenswert?

eine weitere Möglichkeit:

[c]

#define OFFSETOF(TYPE, ELEMENT) ((int)&(((TYPE *)0)->ELEMENT))

struct test
{
  int i;
  int x;
  int y;
};

static struct test Test;

void setStructInt(int offset, int value)
{
  char *ptr;
  ptr = (char*)&Test;

  ptr += offset;
  *ptr = value;
}

int main()
{

  setStructInt( OFFSETOF(struct test, i),1);
  setStructInt( OFFSETOF(struct test, x),2);
  setStructInt( OFFSETOF(struct test, y),3);


  return 0;
}

/[c]

von oli (Gast)


Lesenswert?

oli schrieb:
> void setStructInt(int offset, int value)
> {
>   char *ptr;
>   ptr = (char*)&Test;
>
>   ptr += offset;
>   *ptr = value;
> }

muss so heißen:

void setStructInt(int offset, int value)
{
  int *ptr;
  ptr = (int*)&Test;

  ptr += offset/4;
  *ptr = value;
}

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

oli schrieb:
> eine weitere Möglichkeit:
>
> [c]
>
> #define OFFSETOF(TYPE, ELEMENT) ((int)&(((TYPE *)0)->ELEMENT))

und wo soll da jetzt der Vorteil gegenüber der schon vorhanden offsetof 
sein?
https://en.cppreference.com/w/c/types/offsetof

von oli (Gast)


Lesenswert?

Irgend W. schrieb:
> und wo soll da jetzt der Vorteil gegenüber der schon vorhanden offsetof
> sein?

Ist das gleiche Makro!

Es ging nur um die Methodik, wie man das realisiert.

von P. S. (namnyef)


Lesenswert?

Peda schrieb:
> Das blöde ist jetzt das ich für jedes Struct-Element eine eigene set
> Fuktion schreiben muss.

Es ist zwar die Frage um wie viele Elemente es geht und welchen Kontext 
sie zueinander haben, aber warum ist das blöd? Grundsätzlich sind viele 
Funktionen mit geringer Komplexität wartungsfreundlicher als wenige 
Funktionen mit hoher Komplexität. Natürlich ist - je nach Anwendungen - 
auch oft eine andere Herangehensweise sinnvoll, aber prinzipiell sind 
viele Funktionen erst mal nichts schlimmes, wenn man sich dadurch keine 
anderen Nachteile einhandelt (doppelter Code, usw.).

von A. S. (Gast)


Lesenswert?

Peda schrieb:
> Hat jemand eine Idee wie man das in C realisieren kann?

Solange der TO nicht sagt, was er erreichen will, macht keine Antwort 
Sinn.

Wenn der setter nur setzt, ist er über. Wenn er mehr macht, kommt es 
darauf an, was er machen soll.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.