Forum: Mikrocontroller und Digitale Elektronik Struct alias


von Walter T. (nicolas)


Lesenswert?

Guten Morgen,

ich habe im SRAM mehrere structs, die nichts gemeinsam haben, außer, 
dass sie an zur Compilezeit bekannten konstanten Speicherstellen sein 
müssen und dass sie niemals gleichzeitig benötigt werden, also sehr 
wenig.

Der Grund für ersteres ist, dass Zeiger darauf im Flash abgelegt werden.

Beispiel:

Ein System:
1
struct TempSystem1_s
2
{
3
    float maxDist;
4
    float meanDist;
5
    float meanVel;
6
    int8_t spin;
7
}
8
TempSystem1;
9
10
/** Menue System 1 */
11
constflash Menuitem_t MenuSystem1[] =
12
{
13
    { MT_CONTINUE,                  .All     = {exitMenu}                                                                    },
14
    { MT_ABORT,                     .All     = {exitMenu}                                                                    },
15
    { "Maximum distance %f km",     .Float32 = {flin32, &TempSystem1.maxDist,     .min= 0.05f,    .max= 200.f,    .inc=1.f}  },
16
    { "Mean distance %f km",        .Float32 = {flin32, &TempSystem1.meanDist,    .min= 0.05f,    .max= 200.f,    .inc=1.f}  },
17
    { "Mean velocity %f km/s",      .Float32 = {flin32, &TempSystem1.meanVel,     .min= 1,        .max= 255,      .inc=.1f}  },
18
    { "Orientation %i",             .Int8    = {int8,   &TempSystem1.spin,        .min=-1,        .max= 1,        .inc=2}    },
19
    { " ",                          .All     = {nothing}                                                                     },
20
    { MT_NULL,                      .All     = {endmarker}                                                                   },
21
};

Ein weiteres System:
1
struct
2
{
3
    int16_t idx;
4
    uint8_t color;
5
    uint8_t sealIdx;
6
    uint8_t animal;
7
}
8
TempSystemB;
9
10
constflash char *strlist_animal[2]          = {"lamb", "horse"};
11
constflash char *strlist_color[4]           = {"black", "white", "red", "green"};
12
13
14
/** Menue System B*/
15
constflash Menuitem_t MenuSystemB[] =
16
{
17
    { MT_CONTINUE,                  .All     = {exitMenu}                                                                   },
18
    { MT_ABORT,                     .All     = {exitMenu}                                                                   },
19
    { "Animal: %s",                 .Strlist = {strlist, &TempSystemB.animal,   .str=strlist_animal,    .len=2, .inc=1}     },
20
    { "Animal no. %i",              .Int16   = {int16,   &TempSystemB.idx,      .min=0,     .max=3,     .inc=1}             },
21
    { "Animal colour: %s",          .Strlist = {strlist, &TempSystemB.color,    .str=strlist_color,     .len=4, .inc=1}     },
22
    { "Seal no. %i",                .Uint8   = {uint8,   &TempSystemB.sealIdx,  .min=1,     .max=7,     .inc=1}             },
23
    { MT_NULL,                      .All     = {endmarker}                                                                  },
24
};
Denkt euch die Beispiele größer, die Formatierung im Forum ist nicht für 
große Tabellen gemacht.

Da die großen Structs TempSystem1 und TempSystemB ohnehin nie 
gleichzeitig gebraucht werden, will ich hier den Speicherverbrauch 
verringern. Es ist extrem einfach, sicherzustellen, dass sich die beiden 
Systeme nicht in die Quere kommen.

Was funktionieren würde, wäre ein union mit beiden Structs als Elemente. 
Damit hätte ich allerdings zwei ansonsten völlig unabhängigen 
Komponenten deutlich enger gekoppelt, weil alle Temp-Structs von allen 
Komponenten plötzlich in einem gemeinsamen Header definiert sein 
müssten.

Das Struct als Zeiger auf einen Alias als char array zu definieren 
klappt nicht, weil der ARM-GCC dann moniert: "Initializer element is not 
constant".

Die Zeiger direkt in jedes Feld als Cast legen funktioniert, also so:
1
typedef struct TempSystem1_s TempSystem1_t;
2
char array[100];
3
4
constflash Menuitem_t MenuSystem1[] =
5
{
6
    { "Maximum distance %f km",     .Float32 = {flin32, &(((TempSystem1_t *)array)->maxDist),     .min= 0.05f,    .max= 200.f,    .inc=1.f}  },
7
};
aber das sieht für mich nach der perfekten Investition in eine künftige 
Unlesbarkeit und Fehlerhäufung aus.

Geht es besser?

von Wilhelm M. (wimalopaan)


Lesenswert?

Was stört Dich genau an dem union-Ansatz. Der ist eigentlich genau dafür 
geeignet (wenn ich Dein etwas wirres Problem verstanden haben sollte).

Die Koppelung über das Bekanntmachen der Typen findet ja erst in dem 
Header statt, wo Du die union brauchst. Kommt also Komposition gleich.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Wilhelm M. schrieb:
> Was stört Dich genau an dem union-Ansatz [?]

Daran stört mich, dass es dann einen Header gibt, z.B. temp.h, der zig 
Deklarationen von Variablentypen enthält, die jeweils nur von einem 
einzigen der diesen Header inkludierenden Quellcodedatei benötigt 
werden.

Das ist für mich so ziemlich das Gegenteil von modular.

von Wilhelm M. (wimalopaan)


Lesenswert?

Du musst das doch sowieso alles inkludieren. Läuft also aufs Gleiche 
raus.

von Walter T. (nicolas)


Lesenswert?

Leider nein - vor allem dann, wenn in den structs nicht nur primitive 
Datentypen stehen, sondern andere Datentypen, die von anderen Headern 
bereitgestellt werden.

Dann wird der Header mit dem großen Union plötzlich der, der unterm 
Strich fast alle anderen Header mitzieht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Dann wird der Header mit dem großen Union plötzlich der, der unterm
> Strich fast alle anderen Header mitzieht.

Na klar. Aber das macht doch nichts. Das hättest Du bei jeder anderen 
Lösung auch. Denn die Typen müssen ja bekannt sein.

von Walter T. (nicolas)


Lesenswert?

Die Typen sollten nur dort bekannt sein müssen, wo die Structs 
letztendlich benutzt werden. An allen anderen Stellen reicht es aus, 
wenn der Speicherplatz groß genug ist.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Ich verstehe Dein Problem nicht:

- Du hast eine Komponente A in A.h
- Du hast eine Komponente B in B.h
- A und B sind unabhängig
- Du hast Dein Projekt mit P.h
- In Deinem Projekt P.h führst Du A.h und B.h zusammen
- In P.h kannst Du Deine union deklarieren
- Damit bleiben A und B unabhängig.

von Walter T. (nicolas)


Lesenswert?

Ich habe eine Komponente A, die temporär einen Speicherplatz für ein 
Struct benötigt:
1
/* system_a.h */
2
struct TempA_s
3
{
4
    Aa1_t A1;       /* deklariert in Aa1.h */
5
    Aa2_t A2;       /* deklariert in Aa2.h */
6
    Aa3_t A3;       /* deklariert in Aa3.h */
7
    uint32_t a4;    /* deklariert in stdint.h */
8
}

Ich habe eine Komponente B, die temporär einen Speicherplatz für ein 
Struct benötigt:
1
/* system_b.h */
2
struct TempB_s
3
{
4
    Bb1_t B1;       /* deklariert in Bb1.h */
5
    Bb2_t B2;       /* deklariert in Bb2.h */
6
    Bb3_t B3;       /* deklariert in Bb2.h */
7
    float b4, b5, b6;
8
}
Bis jetzt ist alles schön getrennt.

Jetzt kommt das Union:
1
/* temp.h */
2
union
3
{
4
    struct TempA_s A;
5
    struct TempB_s B;
6
}
7
Temp;
dann wird temp.h indirekt alle Header includieren und alles ist 
vermischt.

: Bearbeitet durch User
von Monk (roehrmond)


Lesenswert?

Walter T. schrieb:
> Daran stört mich, dass es dann einen Header gibt, z.B. temp.h, der zig
> Deklarationen von Variablentypen enthält, die jeweils nur von einem
> einzigen der diesen Header inkludierenden Quellcodedatei benötigt
> werden.

Niemand zwingt dich dazu, alle Typen in einer header Datei zu haben. 
Verschiebe die betroffenen Typen in neue separate Header Dateien, dann 
musst du nur das inkludieren, was du wirklich brauchst.

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Jetzt kommt das Union:/* temp.h */
> union
> {
>     struct TempA_s A;
>     struct TempB_s B;
> }
> Temp;
> dann wird temp.h indirekt alle Header includieren und alles ist
> vermischt.

Na klar. Aber das passiert nur in Deinem Projekt. A und B bleiben 
deswegen unabhängig.
(Andernfalls hast Du Deinen Code falsch strukturiert)

: Bearbeitet durch User
von EAF (Gast)


Lesenswert?

Walter T. schrieb:
> und alles ist vermischt.

Da mischt sich nix....
Und wenn, Typedefinitionen fressen kein Brot.

Und ja, das geht kaum anders, da ja der Compiler die Größe beider 
Strukturen braucht um die größe der Union bestimmen zu können.

Was du da planst, dir wünscht, hört sich an, wie:
"Wasch mich, aber mach mich nicht nass!"

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Jetzt kommt das Union:/* temp.h */
> union
> {
>     struct TempA_s A;
>     struct TempB_s B;
> }
> Temp;
> dann wird temp.h indirekt alle Header includieren und alles ist
> vermischt

Damit hast Du eine Definition (keine Deklaration) in einer Header-Datei, 
was Du wohl nicht willst. Besser in der Header-Datei und den union-Typ 
deklarieren und die (globale) Variable in einer Implementierungsdatei.

von Walter T. (nicolas)


Lesenswert?

EAF schrieb:
> Und ja, das geht kaum anders, da ja der Compiler die Größe beider
> Strukturen braucht um die größe der Union bestimmen zu können.

Und deswegen gefällt mir die Union nicht, und wir sind wieder am Anfang.

Typdefinitionen fressen kein Brot, aber Zeit. Wenn ich aus einem uint8_t 
in Aa1.h ein int8_t mache, wird auch alles im B-System neu gebaut.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> EAF schrieb:
>> Und ja, das geht kaum anders, da ja der Compiler die Größe beider
>> Strukturen braucht um die größe der Union bestimmen zu können.
>
> Und deswegen gefällt mir die Union nicht, und wir sind wieder am Anfang.

Das brauchst Du aber bei jeder Lösung, auch wenn Du die Union als 
poor-mans-union baust.

> Typdefinitionen fressen kein Brot, aber Zeit. Wenn ich aus einem uint8_t
> in aa.h ein int8_t mache, wird auch alles im B-System neu gebaut.

Nein. Dann wären A und B nicht unabhängig.
Da hast Du ein Verständnisproblem oder Dein Code ist falsch 
strukturiert.

von (prx) A. K. (prx)


Lesenswert?

Das beschriebene Problem ist direkt mit der statischen Allokation 
verknüpft. Da muss der Compiler alles kennen, um die Grösse zu 
bestimmen.

Ist das Problem nicht riesig gross, sehe ich in den gesammelten Includes 
kein Problem. Ist es riesig gross, sind Pointer wohl vertretbar. Hat man 
dann noch im Auge, dass Unions und Structs unvollständig bleiben können, 
solange sie nur als Pointer darauf vorkommen und nicht dereferenziert 
werden, hat man auch die Casts vom Hals.

: Bearbeitet durch User
von EAF (Gast)


Lesenswert?

Walter T. schrieb:
> wird auch alles im B-System neu gebaut.
Ja und?

Wenn ich das richtig verstehe, möchtest du die Zeit, welche deine 
Toolchain fürs Bauen braucht verringern, in dem du in den Code dirty 
Hacks einbaust?

Naja....
Kommt mir sehr seltsam vor.

von Walter T. (nicolas)


Lesenswert?

Ich habe eine Lösung gefunden: Ich setze in Assembler einfach zwei Label 
auf die gleiche Speicherstelle. Kein Union, keine fremden Header.
[asm]
#include "temp.h"

.align 4
.bss
.global Temp1
.global Temp2
Temp1:
Temp2:
    .space sizeof(Temp_t)
[/asm]

von (prx) A. K. (prx)


Lesenswert?

(prx) A. K. schrieb:
> dass Unions und Structs unvollständig bleiben können

Falls das nicht bekannt sein sollte:
  struct S *p;
ist ohne jede Information über S zulässig. Dort, wo p dereferenziert 
wird, muss S natürlich deklariert sein. Andernorts nicht unbedingt. 
Damit sollte sich das ohne Casts lösen lassen.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

(prx) A. K. schrieb:
> struct S *p;
> ist ohne jede Information über S zulässig.

Ja, aber dann muss ich den Platz für den Inhalt von S wieder irgendwo 
vorsehen und die Geschichte fängt auch wieder von vorn an.

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> (prx) A. K. schrieb:
>> struct S *p;
>> ist ohne jede Information über S zulässig.
>
> Ja, aber dann muss ich den Platz für den Inhalt von S wieder irgendwo
> vorsehen und die Geschichte fängt auch wieder von vorn an.

Du hast ein wirres Problem mit der Strukturierung Deines Codes. 
Anscheinend sind Deine Komponenten A und B doch nicht unabhängig.

von (prx) A. K. (prx)


Lesenswert?

Walter T. schrieb:
> Ja, aber dann muss ich den Platz für den Inhalt von S wieder irgendwo
> vorsehen und die Geschichte fängt auch wieder von vorn an.

Ich dachte, es geht um Modularisierung und die Trennung des Quellcodes 
in Module, die nicht jeweils alles includen müssen. Das wäre darüber zu 
machen, ausser dort, wo eine maximale Grösse der Elemente bekannt sein 
muss. Dein sizeof(Temp_t) fällt aber auch nicht vom Himmel.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Du hast ein wirres Problem mit der Strukturierung Deines Codes.

Mindestens die Beschreibung seines Problems wirkt etwas wirr. Es ist 
schwierig, im Rahmen des Threads perfekte Lösungen für nicht wirklich 
verstandene Probleme zu liefern.

Ist beispielsweise dies

Walter T. schrieb:
> dass sie an zur Compilezeit bekannten konstanten Speicherstellen sein
> müssen

eine zwingende Forderung aus unbekanntem Grund, oder bereits eine 
vorweggenommene Lösung? Also dass die Adresse feststehen muss. Oder muss 
sie das eigentlich nicht, sondern es soll nur Platz gespart werden?

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

(prx) A. K. schrieb:
> eine zwingende Forderung aus unbekanntem Grund, oder bereits eine
> vorweggenommene Lösung?

Es ist eine zwingende Forderung aus einem im Eröffnungspost behandelten 
Grund, nämlich dass darauf aus in großen im Flash liegenden Structs 
gezeigt wird.

Diese großen structs könnten auf einem im SRAM und einem im Flash 
liegenden Teil aufgeteilt werden, aber es ist so schon unübersichtlich 
genug. Nicht, weil sie wahnsinning unübersichtlich strukturiert wären, 
sondern weil viel drin steht.

(prx) A. K. schrieb:
> Dein sizeof(Temp_t) fällt aber auch nicht vom Himmel.

Es fällt nicht vom Himmel, aber es lässt sich mit static assertions 
recht gut prüfen, dass die Größe zumindest ausreichend ist.

: Bearbeitet durch User
von Micha (Gast)


Lesenswert?

Walter T. schrieb:
> Jetzt kommt das Union:/* temp.h */
> union
> {
>     struct TempA_s A;
>     struct TempB_s B;
> }
> Temp;
> dann wird temp.h indirekt alle Header includieren und alles ist
> vermischt.

Richtig, genau das ist die saubere Lösung.

Ob du nun eine Deklaration oder Definition für das Union ins Headerfile 
machst, das bleibt dir überlassen.

Walter T. schrieb:
> [asm]
> #include "temp.h"
>
> .align 4
> .bss
> .global Temp1
> .global Temp2
> Temp1:
> Temp2:
>     .space sizeof(Temp_t)
> [/asm]

Soll den Murks ausser dir sonst noch jemand nachvollziehen können? Du 
baust potentielle Problemstellen und Eigenkreationen, um dir ein paar 
verschachtelte Header Includes zu sparen?

von Walter T. (nicolas)


Lesenswert?

So sieht es aus. Aus einem include-Spaghettiteller ist ein sauberer Baum 
geworden, und wenige Zeilen Assembler haben gerade etliche zig Zeilen 
C-Quelltext überflüssig gemacht.

Zumindest das letztere bin ich eher umgekehrt gewohnt.

von Peter D. (peda)


Lesenswert?

Walter T. schrieb:
> ich habe im SRAM mehrere structs, die nichts gemeinsam haben, außer,
> dass sie an zur Compilezeit bekannten konstanten Speicherstellen sein
> müssen und dass sie niemals gleichzeitig benötigt werden, also sehr
> wenig.
>
> Der Grund für ersteres ist, dass Zeiger darauf im Flash abgelegt werden.

Die meisten CPUs beherrschen indirekte Adressierung. Es reicht also 
völlig, nur einen Pointer auf die Struct zu reservieren und alle 
Zugriffe über den Offset in der Struct machen zu lassen.
Der Pointer wird einmalig auf einen entsprechend großen RAM-Bereich 
gesetzt.
Ob im Flash nun die Absolutadresse oder der Offset in der Struct steht, 
ist nur ein kleiner Unterschied in der Syntax:
1
xx.member // absolut
2
pxx->member // indirekt

Beim AVR können Pointerzugriffe sogar Flash sparen, da ein absoluter 
LDS/STS 4 Byte Code braucht, ein LD/ST auf Pointer mit Displacement aber 
nur 2 Byte.

von (prx) A. K. (prx)


Lesenswert?

Die Assembler-Trickserei kannst du in GCC auch ohne Assembler haben. 
Siehe "alias" in 
https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

(prx) A. K. schrieb:
> Siehe "alias"

Kenne ich, aber:
"Except for top-level qualifiers the alias target must have the same 
type as the alias."

Peter D. schrieb:
> Es reicht also völlig, nur einen Pointer auf die Struct zu reservieren

Ich überlege mal "laut":
Das könnte dann gut klappen, wenn in jeder Struct im Flash nur auf 
jeweils Felder in einem einzigen anderen Struct verwiesen wird. Dann 
müssten die relativen Adressen mit offsetof() hinterlegt werden, und die 
Adresse des Temp-Structs auf anderem Weg weitergegeben werden.

Wenn ich auf Elemente in mehreren Structs verweise (daß ich das tue, 
habe ich in der Eröffnung unterschlagen, weil es hier nur um die 
Temporäten Daten ging), müsste ich die Adresse wieder für das 
Eltern-Struct jedes Elements einzeln übergeben. Dann könnte ich aber 
auch direkt wieder die direkte Adresse hinterlegen, wäre also auch 
wieder am Anfang, dass ich die Daten für die Feldbeschreibungen und das 
Ziel (Feldinhalt) separat behandeln müsste.

Irgendwie dreht sich das im Kreis.

Wenn ich den Aufwand, für jedes Element zwei Structs zu pflegen, dem 
insgesamt projektweit vier Zeilen Assembler zu pflegen 
gegenüberstelle...


Ich bin mit der Assembler-Lösung nicht glücklich. Aber das andere ist 
noch schlimmer.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

M.a.W: Du hast dich mit dem Konzept derart in die Ecke gepinselt, dass 
es genau eine saubere Lösung gibt - und die lehnst du ab.

: Bearbeitet durch User
von EAF (Gast)


Lesenswert?

So ähnliches vermute ich auch!

Der eigentliche Fehler wird 1 bis 3 Schichten vorher stecken.
Und jetzt wird an den Symptomen gebastelt.

Vergleichbar mit:
> Wer in die falsche Richtung läuft, braucht sich nicht zu beeilen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Ich habe eine Lösung gefunden: Ich setze in Assembler einfach zwei Label
> auf die gleiche Speicherstelle. Kein Union, keine fremden Header.
> [asm]
> #include "temp.h"
>
> .align 4
> .bss
> .global Temp1
> .global Temp2
> Temp1:
> Temp2:
>     .space sizeof(Temp_t)
> [/asm]

... und was steht in temp.h?

von Walter T. (nicolas)


Lesenswert?

Das sieht jetzt so aus:
1
/* temp.h */
2
#ifndef TEMP_H_INCLUDED
3
    #define TEMP_H_INCLUDED
4
5
    #define TEMP_BYTES 96
6
7
    #if !defined( __ASSEMBLER__ )
8
9
    extern char GlTemp[TEMP_BYTES];
10
11
    #endif
12
13
#endif
1
/* temp.S */
2
#include "temp.h"
3
4
/* Alle Globalen "Temp"-Variablen" liegen an der gleichen Speicherstelle */
5
6
.align 4
7
.bss
8
.global GlTemp
9
.global MrtmTemp
10
.global MenuTemp
11
.global MainScreenTemp
12
.global DebgugScreenTemp
13
.global AgcSettingsTemp
14
.global AhcSettingsTemp
15
.global GtCtrlTemp
16
17
GlTemp:
18
MrtmTemp:
19
MenuTemp:
20
MainScreenTemp:
21
DebgugScreenTemp:
22
AgcSettingsTemp:
23
AhcSettingsTemp:
24
GtCtrlTemp:
25
    .space TEMP_BYTES
1
/* temp.c */
2
#include "temp.h"
3
4
/** Globale Temp-Variable für Menüstrukturen u.Ä. aller Art. In der Assembler-Variante liegen alle an der gleichen Speicherstelle. */
5
char GlTemp[TEMP_BYTES];
6
7
char MrtmTemp[TEMP_BYTES];
8
char MenuTemp[TEMP_BYTES];
9
char MainScreenTemp[TEMP_BYTES];
10
char DebgugScreenTemp[TEMP_BYTES];
11
char AgcSettingsTemp[TEMP_BYTES];
12
char AhcSettingsTemp[TEMP_BYTES];
13
char GtCtrlTemp[TEMP_BYTES];
Und in einer der verwendenten Dateien:
1
/* mrtm.c */
2
3
#include "mrtm.h"
4
#include "temp.h"
5
6
static_assert( sizeof(MrtmTemp_t) <= sizeof(GlTemp), "Temp-Variable zu klein");
7
extern MrtmTemp_t MrtmTemp;

Insgesamt spart die Aktion nicht ganz 400 Bytes gegenüber dem vorherigen 
Stand. Ob ich die jetzt als Sicherheitsmarge behalte oder gewinnbringend 
bei Ebay verkaufe, weiß ich noch nicht.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> char MrtmTemp[TEMP_BYTES];
>
> extern MrtmTemp_t MrtmTemp;

Na, das ist ja der Knaller ;-)

Warum so ein Sch..., statt union oder wenigstens eine poor-mans-union zu 
benutzen.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Und was bitte sehr soll eine poor man's union sein?

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Und was bitte sehr soll eine poor man's union sein?

Sowas (hier schon mit Diskriminator für die Zusicherungen):
1
#include <assert.h>
2
3
// in temp.h
4
enum types {TypeA, TypeB, TypeC};
5
6
struct Union {
7
    enum types d;
8
    char buffer[1024];
9
};
10
11
// in temp.c
12
struct Union unionLikeBuffer;
13
14
// in a.h
15
struct A {
16
    int a;
17
    int b;
18
};
19
// in b.h
20
struct B {
21
    float c;
22
    double d;
23
};
24
// in c.h
25
struct C {
26
    char text[80];
27
};
28
29
int main() {
30
    unionLikeBuffer.d = TypeA;
31
    // ...
32
    assert(unionLikeBuffer.d == TypeA);
33
    struct A* const a = (struct A*)unionLikeBuffer.buffer;
34
    // ...
35
    a->a = 42;
36
    
37
    return a->a;
38
}

von Walter T. (nicolas)


Lesenswert?

Hng. Wie ich schon im Eröffnungsbeitrag schrieb, können solche 
"konstanten" structs nicht aus einem konstanten Zeiger im Flash 
adressiert werden. "Initializer element is not constant".

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Hng. Wie ich schon im Eröffnungsbeitrag schrieb, können solche
> "konstanten" structs nicht aus einem konstanten Zeiger im Flash
> adressiert werden. "Initializer element is not constant".

Warum willst Du im flash überhaupt eine Adresse ablegen, die immer 
dieselbe ist bzw. die berechenbar ist. Wenn Du auf die Komponenten 
zugreifen willst, benutzt Du Accessor-Funktionen.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Perfekt. Dann kann ich für jede Variable auch noch eine 
Accessor-Funktion mitpflegen. Und ich dachte schon, ich hätte schlechte 
Ideen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Perfekt. Dann kann ich für jede Variable auch noch eine
> Accessor-Funktion mitpflegen.

Macht sich manchmal nicht schlecht, wenn man Invarianten der Klasse 
sicherstellen will / muss (ja, auch in C).

> Und ich dachte schon, ich hätte schlechte
> Ideen.

Die mit großem Abstand schlechteste Idee hast Du ja schon selbst 
gebracht und willst sie einsetzen. Dazu erstmal viel Spaß bei Deinem 
Heldenprojekt.

Die besten Idee (Union) hast Du aus unerklärlichen Gründen abgelehnt.

von BMC (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Die besten Idee (Union) hast Du aus unerklärlichen Gründen abgelehnt.

Der Beitrag des TO diente doch mal wieder nur dazu zu erklären, wie toll 
der TO ein nicht vorhandenes Problem gelöst hat.

von Rudolph R. (rudolph)


Lesenswert?

Wenn das zur Compile-Zeit für verschiedene Systeme aufgedröselt werden 
soll, was spricht dagegen das mit bedingter Compilierung über #ifdef zu 
machen?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Walter T. schrieb:
> dass sie an zur Compilezeit bekannten konstanten Speicherstellen sein
> müssen und dass sie niemals gleichzeitig benötigt werden, also sehr
> wenig.
>
> Der Grund für ersteres ist, dass Zeiger darauf im Flash abgelegt werden.

Versteh ich jetzt nicht.  Um eine Variable zur Load-Time mit einer 
Adresse zu initialisieren, genügt doch, dass die Adresse zur Link-Time 
bekannt ist:

Hier eine Variable var im RAM, deren Adresse in einer Variablen pvar im 
Flash steht (hier mit avr-gcc):
1
int var;
2
3
int * const __flash pvar = &var;
4
5
int get_var (void)
6
{
7
    return *pvar;
8
}
Auch var in __flash (oder pvar im RAM) geht ebenso.


Wenn ich dein Problem richtig verstehe?

Dein Programm kann in verschiedenen Teilprogrammen A, B, ... sich 
befinden, die aber nie gleichzeitig auftreten.  Beispielsweise in einer 
Spieleconsole mit verschedenen Spielen, wo man aber jeweils nur eines 
der Spiele auswählen und spielen kann.  Um Spiel B zu spielen muss man 
Spiel A verlassen.  Entsprechend dürfen die RAM-Resourcen von A und B an 
der gleichen Adresse liegen.

Mit normaler Allocation hätte man Speicherbedarf

RAM(A) + RAM(B) + RAM(C) + ...

aber im besten Falle braucht man nur

max (RAM(A), RAM(B), ...)

von Walter T. (nicolas)


Lesenswert?

Johann L. schrieb:
> Wenn ich dein Problem richtig verstehe?

Genau, das trifft die Sache.

Johann L. schrieb:
> Versteh ich jetzt nicht.  Um eine Variable zur Load-Time mit einer
> Adresse zu initialisieren, genügt doch, dass die Adresse zur Link-Time
> bekannt ist

Auch das stimmt.

Dein Beispiel (Funktion) funktioniert, aber wenn es keine Funktion 
sondern ein struct im Flash ist, funktioniert die Zuweisung nicht 
("Initializer element is not constant").

Das ist zumindest beim ARM-GCC der Fall. MinGW (GCC für Windows) stört 
das nicht. Wie es beim AVR-GCC aussieht, weiß ich nicht, da momentan auf 
meinem aktuellen Rechner nicht installiert, da momentan*) kein offenes 
AVR-Projekt.

*) Vielleicht zwischen den Jahren wieder.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Dein Beispiel (Funktion) funktioniert, aber wenn es keine Funktion
> sondern ein struct im Flash ist, funktioniert die Zuweisung nicht
> ("Initializer element is not constant").

Da das Zielelement im Flash ist, ist es ja zwangsweise konstant (mal von 
der Möglichkeit abgesehen, den Flash zur Laufzeit zu ändern). Du 
solltest das also eh entsprechend deklarieren als Konstante, dann dürfte 
das trotzdem klappen. (Gut, C ist da manchmal eigenwillig, da es keine 
echten Konstanten gibt, C++ ist da besser dran.)

Ansonsten könntest du natürlich auch eine eigene section im Linkerscript 
für deine Konstanten deklarieren, die einfach nur groß genug ist. Da 
musst du nur sehen, wie du die im Flash anordnest, damit sie dir den 
Flashbereich für den Rest nicht zerstückelt. Wäre allemal besser als ein 
Assembler-Hack.

von Walter T. (nicolas)


Lesenswert?

Jörg W. schrieb:
> Gut, C ist da manchmal eigenwillig, da es keine
> echten Konstanten gibt, C++ ist da besser dran.

Ja, das Problem ist mir schonmal auf die Füße gefallen, sonst ich die 
Struct-artigen Zugriffe einfach als Zeiger auf ein char array erzeugt, 
siehe hier:
Beitrag "Initializer element is not constant"

Die strict-alias-Regeln hätten das erlaubt, und es wären keinerlei 
Klimmzüge nötig.

Jörg W. schrieb:
> [sinngemäß: besser im Linkerscript als in Assembler herumhacken]

Vielleicht. Ich bin bei beidem nicht glücklich. Bei der 
Assembler-Variante sehe ich zumindest direkt im Projektbaum, dass hier 
für ARM und PC unterschiedlich gebaut wird.

: Bearbeitet durch User
von BMC (Gast)


Lesenswert?

Das Problem kann man - wie oben schon beschrieben - ganz einfach über 
union lösen. Genau dafür ist union gemacht worden: um Speicher zu sparen 
(nicht für type punning).  Alles andere ist unwartbarer Murks. Und ja: 
Du kannst Deinen Code so strukturieren, dass das ohne versteckte 
Abhängigkeiten machbar ist.

von Wilhelm M. (wimalopaan)


Lesenswert?

BMC schrieb:
> Das Problem kann man - wie oben schon beschrieben - ganz einfach über
> union lösen. Genau dafür ist union gemacht worden: um Speicher zu sparen
> (nicht für type punning).  Alles andere ist unwartbarer Murks. Und ja:
> Du kannst Deinen Code so strukturieren, dass das ohne versteckte
> Abhängigkeiten machbar ist.

Und der weitere Punkt ist: die Adressen der Element-Variablen haben im 
flash nichts zu suchen. Den Zugriff kapselt man in Accessoren (In C als 
freie Funktionen leider ohne Überladung, in C++ als freie Funktion mir 
Überladung oder als Elementfunktionen der Union).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Natürlich kann es sinnvoll sein, Adressen von Variablen im Flash (oder 
in Strukturen, die im Flash leben) zu haben.  Einfaches Beispiel sind 
Menus, die Unter-Menus enthalten.

Walter T. schrieb:
> Dein Beispiel (Funktion) funktioniert, aber wenn es keine Funktion
> sondern ein struct im Flash ist, funktioniert die Zuweisung nicht
> ("Initializer element is not constant").

Da hast du mein Beispiel nicht richtig gelesen.  Es verwendet keine 
Funktion, sondern:
1
int var;
2
int * const __flash pvar = &var;
Die Funktion zeigt lediglich einen Zugriff auf pvar zur Laufzeit.  Und 
ob da die Adresse eines Skalars oder eines Komposits genommen wird, ist 
wurscht.

----------------------------------

In einem meiner Projekte hatte ich auch das Problem, das Teilprogramme 
einen (großen) Puffer brauchen, aber nie gleichzeitig.  Die Puffer waren 
lokal im jeweiligen Modul, und um eine Union für alle Buffer zu haben, 
hätte ich fast alles in den Modules global machen müssen.  Alles in den 
> 10 Teilprogramm zu globalisieren, nur um eine Union für eine lokale 
Variable machen zu können, hatte ich fix wieder aufgegeben, da zu 
hässlich. Und "Puffer" waren jeweils ausgewachsene Structs, also nicht 
simple uint8_t-Arrays.

Ich hab dann ein Overlay als Lösung genommen: In jedem Modul, das so 
einen Buffer braucht, wird der Buffer als global extern deklariert:
1
// In modulA.c
2
extern bufferA_t bufferA;
3
// Die Adresse der Puffer ist zur Link-Time bekannt, kann also in
4
// einem Initializer verwendet werden:
5
[static] bufferA_t *pbuf = &bufferA;
Instanziierung gibt es auf C-Ebene nicht, stattdessen werden Symbole zur 
Linkzeit in einem opt-File buffer.opt definiert, das beim Linken per 
@buffer.opt angegeben wird:
1
-Wl,--defsym,bufferA=__heap_start
2
-Wl,--defsym,bufferB=__heap_start
Das Projekt war mit avr-gcc, und dort ist __heap_start ein Symbol, das 
im Linker-Script definiert wird.  Es folgt auf den Bereich für Static 
Storage (.data, .bss, .noinit). Dementsprechend hat man folgende 
Einschränkungen:

1) Mit avr-gcc oder mit Toolchain, die ein vergleichbares Speicherlayout 
hat (also malloc nicht per sbrk).

2) Wenn kein malloc verwendet wird (oder man müsste __malloc_heap_start 
vor dem ersten Aufruf von malloc anpassen).

3) Man kann nur ein solches Overlay anlegen.

4) Das Overlay wird vom Startup-Code nicht initialisiert.

Wer sich daran stört, dass bufferA etc. global sind: Bei einer Lösung 
mit Union müsste man noch viel mehr Zeug global machen, nämlich alles, 
was zur Definition von bufferA_t gebraucht wird.

Das opt-File kann man selbst schreiben, oder nach Gusto von der 
Build-Umgebung erzeugen lassen.  Anstatt eines opt-Files (bzw. 
Definition per Optionen) könnte man auch den Assembly-Name setzen:
1
// In modulA.c
2
extern bufferA_t bufferA __asm("__heap_start");

von Johnny B. (johnnyb)


Lesenswert?

Walter T. schrieb:
> Geht es besser?

Union ist doch gut für sowas, wurde oben ja schon zur Genüge 
abgehandelt.

Ich würde es nicht machen, aber als Alternative kannst Du den benötigten 
Speicher auch erst zur Laufzeit jeweils mit malloc() holen und mit 
free() wieder freigeben.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Ich hab dann ein Overlay als Lösung genommen: In jedem Modul, das so
> einen Buffer braucht, wird der Buffer als global extern deklariert

Ganz und gar automatisch könnte man das mit Rückgriff auf die 
FORTRAN-Zeit haben: mit einem COMMON-Block. Dessen Natur bestand ja auch 
darin, den gleichnamigen Speicher verschiedener Objektmodule zu 
überlagern.

Wer sich erinnert: bis vor noch gar nicht so langer Zeit war die 
Allokation globaler nicht initialisierter Daten (.bss) in einem 
COMMON-Block auch bei GCC & Co. noch die Voreinstellung. Das wurde erst 
in letzter Zeit geändert, jetzt muss man diese mit -fcommon explizit 
anfordern. Aber sowas funktioniert damit:
1
$ cd /tmp
2
$ cat foo.c
3
int something;
4
$ cat bar.c
5
double something;
6
$ cat mumble.c
7
int main(void) { return 0; }
8
$ echo Geht nicht
9
Geht nicht
10
$ cc -O -o foo -fno-common foo.c bar.c mumble.c
11
/usr/bin/ld: /tmp/ccIqUgYZ.o:(.bss+0x0): multiple definition of `something'; /tmp/cczAQ7uZ.o:(.bss+0x0): first defined here
12
collect2: error: ld returned 1 exit status
13
$ echo Geht
14
Geht
15
$ cc -O -o foo -fcommon foo.c bar.c mumble.c
16
$ nm --print-size foo | fgrep something
17
0000000000004018 0000000000000008 B something

Wenn man das nicht für alle .bss-Variablen haben möchte, sollte sich das 
auch auf eine einzelne .section limitieren lassen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> Johann L. schrieb:
>> Ich hab dann ein Overlay als Lösung genommen: In jedem Modul, das so
>> einen Buffer braucht, wird der Buffer als global extern deklariert
>
> Ganz und gar automatisch könnte man das mit Rückgriff auf die
> FORTRAN-Zeit haben: mit einem COMMON-Block. Dessen Natur bestand ja auch
> darin, den gleichnamigen Speicher verschiedener Objektmodule zu
> überlagern.

Diese Lösung hatte ich tatsächlich ne Zeit lang, bin aber dann wieder 
davon ab.  Ist schon ne Zeit her, ich weiß jetzt nicht mehr, was mich 
daran gestört hat :-( Möglicherweise, dass alle Objekte den gleichen 
Namen haben müssen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Möglicherweise, dass alle Objekte den gleichen Namen haben müssen.

Das wieder sollte sich doch aber mit einem Alias beheben lassen, oder? 
Man benennt sie erstmal gleich (wie "common_block"), aber macht dann 
jeweils einen aussagekräftigen Alias in jedem Modul dafür, den man im 
Code benutzt.

von Walter T. (nicolas)


Lesenswert?

Endlich mal eine sinnvolle Diskussion. Ich hatte schon befürchtet, alle 
vernünftigen Menschen wären schon lange abgehauen und nur wir Deppen 
hätten das Memo nicht bekommen wohin.

Johann L. schrieb:
> Da hast du mein Beispiel nicht richtig gelesen.  Es verwendet keine
> Funktion, sondern:
> int var;
> int * const __flash pvar = &var;
> Die Funktion zeigt lediglich einen Zugriff auf pvar zur Laufzeit.  Und
> ob da die Adresse eines Skalars oder eines Komposits genommen wird, ist
> wurscht.

Komposit oder Skalar ist wurscht, aber
1
#define __flash /* ARM-GCC kennt das nicht */
2
int var;
3
int * const __flash pvar = &var; /* geht */
4
int * const __flash pp = pvar;   /* geht nicht mehr */
Beim Skalar ist die doppelte Verzeigerung natürlich komplett Mumpitz. 
Beim Struct würde var den Speicherplatz bereitstellen, pvar die 
Offsets-Struktur und pp ist letztendlich der Zeiger auf die Nutzdaten 
innerhalb der Zielstruktur.

Ich weiß nicht, ob das beim AVR-GCC anders aussieht. Ich installiere das 
gerade, um einen Vergleich ziehen zu können. (Nachtrag: Es sieht genauso 
aus.)

Jörg W. schrieb:
> Ganz und gar automatisch könnte man das mit Rückgriff auf die
> FORTRAN-Zeit haben: mit einem COMMON-Block.

Genau das Konstrukt ist es, was ich vermisse.

Jörg W. schrieb:
> Das wieder sollte sich doch aber mit einem Alias beheben lassen, oder?

Also ein char array, ein Alias auf das char array als char array und 
dann in einen Struct-Zeiger casten? Das habe ich tatsächlich noch nicht 
probiert.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Genau das Konstrukt ist es, was ich vermisse.

Ist ja prinzipiell da.

Ist nur so, dass .bss mittlerweile nicht mehr automatisch als 
COMMON-Block registriert wird. Aber wie gezeigt, mit -fcommon kannst du 
das sofort wieder erreichen.

Alternative wäre halt eine eigene section nur mit diesem Attribut zu 
versehen, aber dann musst du dich vermutlich im Linkerscript noch drum 
kümmern.

Die Aliase musst du nicht unbedingt benutzen. Wenn es dich nicht stört, 
dass die Variable halt in allen Übersetzungseinheiten gleich benannt 
werden muss, obwohl sie völlig verschiedene Inhalte hat, kannst du das 
auch direkt dort deklarieren. Dann kann die auch in jeder 
Übersetzungseinheit einen anderen Typ haben, du musst nur wissen, was du 
tust. Der Linker wählt dann automatisch die längste davon für die 
Reservierung.

von Walter T. (nicolas)


Lesenswert?

Ah, ich denke, ich habe es gefunden:
1
char Test0[4];
2
extern char *Test1 __attribute__((alias("Test0"))) ;
-> Fehlermeldung "Error: `Test1' can't be equated to common symbol 
'Test0'"
1
char Test0[4]= {};
2
extern char *Test1 __attribute__((alias("Test0"))) ;
-> Funktioniert.

Quelle: Erster Kommentar bei 
https://stackoverflow.com/questions/35875526/making-global-variables-hidden-and-aliased

Damit funktioniert wieder alles in halbwegs normalem C ohne 
Assembly-Hacks.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wie geschrieben, mit einem COMMON-Block sparst du dir sogar noch den 
Stress, genügend viele Bytes im Array anzulegen, da der Linker 
automatisch Platz für den längsten reserviert. Allerdings bist du damit 
auf einen Datentyp pro Übersetzungseinheit limitiert, mit den Aliasen 
kannst du natürlich x-beliebig viele produzieren.

von Walter T. (nicolas)


Lesenswert?

Jörg W. schrieb:
> mit einem COMMON-Block sparst du dir sogar noch den
> Stress, genügend viele Bytes im Array anzulegen

Was für ein Sprung - von "alles sehr mühsam" zu "zwei sehr brachbare 
Lösungen".

Vielen Dank an GJ und Dich für euren hilfreichen Beiträge!

von Wilhelm M. (wimalopaan)


Lesenswert?

Walter T. schrieb:
> Jörg W. schrieb:
>> mit einem COMMON-Block sparst du dir sogar noch den
>> Stress, genügend viele Bytes im Array anzulegen
>
> Was für ein Sprung - von "alles sehr mühsam" zu "zwei sehr brachbare
> Lösungen".

Die einfache und nachvollziehbare Lösung wäre die union gewesen. Dies 
ist einfach nur ein Hack.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Die einfache und nachvollziehbare Lösung wäre die union gewesen.

Wenn diese allerdings einen elendigen Rattenschwanz an anderen includes 
nach sich zieht, nur damit man die notwendigen Typdefinitionen beisammen 
hat (die ja für die komplette Union vorliegen müssen, egal, welchen Teil 
davon man gerade braucht), kann ich das Anliegen durchaus 
nachvollziehen.

Ich hätte allerdings vermutlich selbst lieber zu malloc/free gegriffen, 
auch wenn das bei vielen verpönt ist. Das hätte das Problem natürlich 
genauso gelöst.

von Walter T. (nicolas)


Lesenswert?

Jörg W. schrieb:
> Ich hätte allerdings vermutlich selbst lieber zu malloc/free gegriffen,

Ich auch, aber die Zeiger auf den Speicherblock von malloc() lassen sich 
halt nicht im Flash hinterlegen, und wenn ich die Wahl habe zwischen 
einer oder zwei großen Tabellensammlungen zu pflegen, weiß ich, was mir 
wichtiger ist.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Wenn diese allerdings einen elendigen Rattenschwanz an anderen includes
> nach sich zieht, nur damit man die notwendigen Typdefinitionen beisammen
> hat (die ja für die komplette Union vorliegen müssen, egal, welchen Teil
> davon man gerade braucht), kann ich das Anliegen durchaus
> nachvollziehen.

Und genau das kann bei sauberer Trennung, von der der TO sprach, nicht 
passieren.

Der TO sprach von "völlig unabhängigen Komponenten"!
(s.a. 
Beitrag "Re: Struct alias").

von (prx) A. K. (prx)


Lesenswert?

Jörg W. schrieb:
> Ich hätte allerdings vermutlich selbst lieber zu malloc/free gegriffen,
> auch wenn das bei vielen verpönt ist.

Allerdings klappt das nicht so gut, wenn die Adresse des allozierten 
Speichers im Flash liegen soll. Und so dreht sich das im Kreis. Sobald 
das Flash nicht mehr direkt auf das RAM zeigen muss, eröffnen sich 
allerlei Möglichkeiten.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
>> Ich hätte allerdings vermutlich selbst lieber zu malloc/free gegriffen,
>
> Ich auch, aber die Zeiger auf den Speicherblock von malloc() lassen sich
> halt nicht im Flash hinterlegen

Stimmt auch wieder.

Beitrag #7275782 wurde von einem Moderator gelöscht.
Beitrag #7275808 wurde vom Autor gelöscht.
Beitrag #7275877 wurde von einem Moderator gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

BMC schrieb im Beitrag #7275782:
> Wow: die Renaissance des Common-Block: das ist mal innovativ! Was für
> ein Sprung in die Steinzeit: vollkommener Mist.

Leider konnte uns der TO nicht erklären, warum er seine "total 
unabhängigen" Komponenten nicht in einer union zusammenführen konnte. 
Scheint am TO zu liegen ...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Leider konnte uns der TO nicht erklären, warum er seine "total
> unabhängigen" Komponenten nicht in einer union zusammenführen konnte.

Du willst die Antwort nur nicht akzeptieren. Die Erklärung dafür stand 
schließlich schon im Eröffnungsposting.

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Du willst die Antwort nur nicht akzeptieren. Die Erklärung dafür stand
> schließlich schon im Eröffnungsposting.

Mmh ...

Walter T. schrieb:
> Was funktionieren würde, wäre ein union mit beiden Structs als Elemente.
> Damit hätte ich allerdings zwei ansonsten völlig unabhängigen
> Komponenten deutlich enger gekoppelt, weil alle Temp-Structs von allen
> Komponenten plötzlich in einem gemeinsamen Header definiert sein
> müssten.

Und das beutet keine enge Koppelung der Komponenten, denn die bleiben 
unabhängig.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Und das beutet keine enge Koppelung der Komponenten, denn die bleiben
> unabhängig.

Wenn du eine ideale Welt hast, in der es Headerfiles nur für Typen gibt 
und die auch nicht kreuz und quer von anderem abhängen, mag das 
funktionieren.

Ich habe leider selbst schon ausreichend "historisch gewachsenes" 
Headerfile-Spaghetti mit so vielen gegenseitigen Abhängigkeiten erlebt, 
dass ich den Wunsch des TE durchaus nachvollziehen kann, möglichst eben 
nicht alles irgendwo reinziehen zu müssen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Wilhelm M. schrieb:
>> Und das beutet keine enge Koppelung der Komponenten, denn die bleiben
>> unabhängig.
>
> Wenn du eine ideale Welt hast, in der es Headerfiles nur für Typen gibt
> und die auch nicht kreuz und quer von anderem abhängen, mag das
> funktionieren.

Also was denn nun: total unabhängig, wie der TO es von seinen 
Komponenten sagt, oder kreuz und quer?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Lass es doch bitte, du willst es wohl bloß nicht verstehen. Anders kann 
ich mir dein Herumreiten nicht erklären.

Beitrag #7276200 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Bitte die Forenregeln beachten: ein Name pro Thread.

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.