Forum: PC-Programmierung [Ada] Record Representation Clauses - Wert eines "Records" in einem Zug füllen?


von Ada-Neuling (Gast)


Lesenswert?

Hallo Leute,

dies ist eine Frage an die Ada-Profis (vielleicht auch SPARK/VHDL), wie 
ich es gerne einmal, wenn ich groß bin, werden möchte. Vielleicht gibt 
es ja hier auch einen der scheinbar sehr exotischen/seltenen 
Ada-Programmiere.

Eigentlich komme ich aus der C-Ecke und habe mit Ada da noch so manche 
Probleme.

Konkret habe ich eine Frage, die ich zwar in C lösen kann, aber kein 
Ada-Gegenspieler finde. - oder, das will ich nicht ausschießen, momentan 
noch zu unerfahren bin.

Ich will einen Datentyp (eine "Record" 32Bit breit) erzeugen und 
beispielsweise als 4x8Bit Datentyp benutzen. Das geht ja (zumindest die 
erste meiner Anforderungen) seit mind. Ada95 über die "Record 
Representation Clauses". - Analog dazu in C wäre ja ein Union.
1
type BYTE is mod 2**8;
2
type Data is
3
        record
4
            BYTE_1 : BYTE;
5
            BYTE_2 : BYTE;
6
            BYTE_3 : BYTE;
7
            BYTE_4 : BYTE;
8
        end record;
9
            
10
    for Data use
11
        record 
12
            BYTE_1  at 0 range  0 ..  7;
13
            BYTE_2  at 0 range  8 .. 15;
14
            BYTE_3  at 0 range 16 .. 23;
15
            BYTE_4  at 0 range 24 .. 31;
16
        end record;
Wenn ich die Größe des Records abfrage, bekomme ich als Antwort 32 (Bit) 
zurück geliefert. Das geht also schon einmal :-)

Jetzt würde ich aber diesen Record einen 32Bit Wert zuweisen.
1
Dat: Data := 2#1010_1010_0101_0101_1010_1010_0101_0101#;
Das geht jedoch leider nicht.

Wohl aber
1
Dat: Data.BYTE_1 := 2#1010_1010#;
2
Dat: Data.BYTE_2 := 2#1010_1010#;
3
Dat: Data.BYTE_3 := 2#0101_0101#;
4
Dat: Data.BYTE_4 := 2#0101_0101#;

Auch das folgende geht leider nicht. Die Idee eine 32Bit Variable in den 
Record zu legen, der die selbe Adresse hat wie die 4 Bytes zuvor 
sozusagen eine Überladung/Überlagerung:
1
type BYTE is mod 2**8;
2
type DWORD is mod 2**32
3
type Data is
4
        record
5
            BYTE_1 : BYTE;
6
            BYTE_2 : BYTE;
7
            BYTE_3 : BYTE;
8
            BYTE_4 : BYTE;
9
            DWORD_1: DWORD;
10
        end record;
11
            
12
    for Data use
13
        record 
14
            BYTE_1  at 0 range  0 ..  7;
15
            BYTE_2  at 0 range  8 .. 15;
16
            BYTE_3  at 0 range 16 .. 23;
17
            BYTE_4  at 0 range 24 .. 31;
18
            DWORD_1 at 0 range  0 .. 31; -- Idee: Benutzt den selben Bereich wie die anderen Bytes
19
        end record;
Da spuckt mir der Compiler "component "DWORD_1" overlaps "BYTE_1" at 
line xxx" aus. Ja, das war ja auch die Idee dahinter, aber scheinbar ist 
es unzulässig - "C" hat mich halt versaut :-(

Generell wollte ich es so machen um nicht zu viel des kostbaren 
Speichers zu benutzen; in einem Zug große Mengen an Werten kompakt zu 
schreiben und einzelne Bytes eines 32Bit Werts einfach zu manipulieren.

Kann mir jemand sagen, ob es so in der Art überhaupt geht? - Und wenn 
ja, wie?



PS: Ich Formatiere den Code als VHDL damit die Forumsoftware sie etwas 
aufhübscht ;)

von Bauteiltöter (Gast)


Lesenswert?

Erste Frage: Warum tust du so eine Schweinerei?

Erste Antwort:
du könntest eine unchecked_conversion DWORD -> Data benutzen, das geht 
auf jeden Fall.

ABER: wie der Name schon sagt ist das ganze dann ohne Typprüfung. Bei 
deinem Record ist das noch kein Problem weil es keinen DWORD-Datentyp 
gibt der einen illegalen "Data"-Record darstellt. Aber wenn du jetzt 
z.B. das BYTE_2 auf ein myint änderst der range 0 .. 200 hat, dann gibt 
es plötzlich illegale Werte. Wenn du nun über eine unchecked_conversion 
einen illegalen Wert in deinen Record lädst, dann kracht es irgendwann 
beim Zugriff auf den Record. (NICHT!! beim Zuweisen, das ist ja 
unchecked).

von Bauteiltöter (Gast)


Lesenswert?

Ungetestetes Beispiel vergessen:
1
with Ada.Unchecked_Conversion;
2
3
type BYTE is mod 2**8;
4
type DWORD is mod 2**32
5
type Data is
6
        record
7
            BYTE_1 : BYTE;
8
            BYTE_2 : BYTE;
9
            BYTE_3 : BYTE;
10
            BYTE_4 : BYTE;
11
        end record;
12
            
13
    for Data use
14
        record 
15
            BYTE_1  at 0 range  0 ..  7;
16
            BYTE_2  at 0 range  8 .. 15;
17
            BYTE_3  at 0 range 16 .. 23;
18
            BYTE_4  at 0 range 24 .. 31;
19
        end record;
20
21
    function dword_to_data is
22
       new Ada.Unchecked_Conversion
23
              (Target => Data,
24
               Source => DWord);
25
26
    Dat: Data := dword_to_data(2#1010_1010_0101_0101_1010_1010_0101_0101#);

von Ada-Neuling (Gast)


Lesenswert?

Vielen Dank schon einmal.

Wie würde man den so etwas den Ada-Regeln entsprechend implementieren?
Wie gesagt, ich lerne noch, kenne noch nicht alle Kniffe und bin von C 
versaut.

von Torben H. (bauteiltoeter)


Lesenswert?

Was genau möchtest du denn erreichen?

Ada-Neuling schrieb:
> Generell wollte ich es so machen um nicht zu viel des kostbaren
> Speichers zu benutzen; in einem Zug große Mengen an Werten kompakt zu
> schreiben und einzelne Bytes eines 32Bit Werts einfach zu manipulieren.
Nur um es kompakt zu schreiben?
Dann setz lieber die vier Bytes nacheinander. Der Compiler macht sehr 
wahrscheinlich am Ende das gleiche daraus.

Wenn du mehr ernsthaften Ada-Code schreiben willst, dann wird das eh auf 
Dauer nichts weil deine Datentypen nicht spezifisch genug sind.
Wenn ich auf unsere Ada-Codebase schaue (Millionen LOC), dann sind die 
Ada-Standarddatentypen sehr selten.

Die meisten Variablen (und Record member) sind von einem Typ, der genau 
dem Inhalt entspricht.
Also wenn ich einen Record mit Luftdruck, Temperatur und Luftfeuchte 
habe, dann sind da nicht 3 floats drin, sondern ein T_air_pressure, ein 
T_temperature und ein T_humidity. Somit kann man die nicht ausversehen 
mit anderen Dingen vermischen (Temperatur + Druck = Banane).

von P.S. (Gast)


Lesenswert?

Da es sich um einen Record handelt, nimmt man ein Record-Aggregat. 
Müsste etwa so aussehen:
1
Dat: Data := Data'(BYTE_1 => 2#1010_1010#,
2
                   BYTE_2 => 2#0101_0101#,
3
                   BYTE_3 => 2#1010_1010#,
4
                   BYTE_4 => 2#0101_0101#);

Oder:
1
Dat: Data := Data'(2#1010_1010#,
2
                   2#0101_0101#,
3
                   2#1010_1010#,
4
                   2#0101_0101#);

von Ada-Neuling (Gast)


Lesenswert?

Torben H. schrieb:
> Wenn ich auf unsere Ada-Codebase schaue (Millionen LOC), dann sind die
> Ada-Standarddatentypen sehr selten.

Cool, dann gibt es ja doch noch ein paar Entwickler, die diese an sich 
coole alte Sprache noch nutzen :)
Das freut mich ungemein und steigert auch meine Entschlossenheit damit 
weiter zu machen!


Torben H. schrieb:
> Die meisten Variablen (und Record member) sind von einem Typ, der genau
> dem Inhalt entspricht.
> Also wenn ich einen Record mit Luftdruck, Temperatur und Luftfeuchte
> habe, dann sind da nicht 3 floats drin, sondern ein T_air_pressure, ein
> T_temperature und ein T_humidity. Somit kann man die nicht ausversehen
> mit anderen Dingen vermischen (Temperatur + Druck = Banane).

Ja okay, leuchtet in der strengen Typisierung, die Ada einem vorgibt 
ein. Macht den Code auch ohne Ausführung deutlich lesbarer.

P.S. schrieb:
> Da es sich um einen Record handelt, nimmt man ein Record-Aggregat.

Das macht den Code auf jeden fall lesbarer als mein Ansatz. Ich glaube, 
dieses "viel mit wenig beschreibung" machen zu wollen muss ich mir 
abgewöhnen! - Ich muss die C-Denke im meinem Kopf noch etwas mehr 
abblocken ;)


Apropro lesbar: Ich musste mal einen LEX/YACC Code von Hand optimieren. 
Das war zwar C, was ich eigentlich beherrsche, aber sowas von kryptisch. 
Ich habe tage lang den Code angestarrt, bevor ich darin anfangen konnte 
zu arbeiten.
Ada Code ist für mich von Anfang an sehr gut lesbar gewesen, selbst als 
ich die einfachsten Sprachelemente noch nicht kannte, wusste ich 
meistens, was der Code bewirken soll. Dafür, das sie aus den ende der 
'70er stammt, bei dem man mit jedem Bit geizig war, finde ich das eine 
wirklich gelungene Sache!

von klausi (Gast)


Lesenswert?

Ada-Neuling schrieb:
> Ada Code ist für mich von Anfang an sehr gut lesbar gewesen, selbst als
> ich die einfachsten Sprachelemente noch nicht kannte, wusste ich
> meistens, was der Code bewirken soll. Dafür, das sie aus den ende der
> '70er stammt, bei dem man mit jedem Bit geizig war, finde ich das eine
> wirklich gelungene Sache!

Ja, wirklich beeindruckend.. krass z.B. kenn ich es, da für Steuerungen 
in Leitsystemen im Bahnverkehr das immer noch eingesetzt wird; deutscher 
Konzern mit großem "S" am Anfang..

von Ada-Neuling (Gast)


Lesenswert?

Hat zwar nur bedingt mit meinem ursprünglichem Post zu tun, aber ich 
habe die letzten Tage wirklich viel über Ada gelernt! Genial, welche 
Möglichkeiten man mit dieser Sprache hat. Es geht so weit, das man bei 
selbst angelegten Variablen, sogar festlegen kann ob diese per Big oder 
Little Indian angelegt werden soll. Das nenne ich mal Portabilität. Das 
geht, soweit ich weiß unter C/C++ und Co nicht und ist immer vom 
Compiler und/oder der Plattform abhängig abhängig.

Hier mal ein umfangreiches Beispiel
1
   type Error_T is (None, Read_Error, Write_Error, Power_Fail, Other);
2
   for Error_T use (None => 0, Read_Error => 1, Write_Error => 2, Power_Fail => 3, Other => 4);
3
   
4
   type Function_T is (Read, Write, Seek);
5
   for Function_T use (Read => 1, Write => 2, Seek => 3);
6
   
7
   type Unit_T is new Integer range 0 .. 7;
8
   
9
   Word : Constant := 2; --numbers of storage units in a Word
10
   Bits_In_Word : Constant := 16; --bits in word
11
   type Csr_T is record
12
      Error : Error_T;
13
      Busy : Boolean;
14
      Unit : Unit_T;
15
      Done : Boolean;
16
      Ienable : Boolean;
17
      Dfun : Function_T;
18
      Denable : Boolean;
19
   end record;
20
   for Csr_T use
21
      record
22
         Denable at 0*Word range 0..0; --at word 0 bit 0;
23
         Dfun at 0*Word range 1..2;
24
         -- bits 3-5 unused
25
         Ienable  at 0*Word range 6..6;
26
         Done at 0*Word range 7..7;
27
         Unit at 0*Word range 8..10;
28
         Busy at 0*Word range 11..11;
29
         Error at 0*Word range 12..15;
30
      end record;
31
   for Csr_T'Size use Bits_in_Word; --the size of object of Csr type
32
   for Csr_T'Alignment use Word; --object should be word aligned
33
   for Csr_T'Bit_Order use System.Low_Order_First; --first bit is least significant bit of byte

Man kann Aufzählungstypen sogar einen Wert zuweisen, die das Abbild im 
Speicher erzeugen soll oder dem Record sagen; wie groß es ist und wie 
seine Ausrichtung oder seine Bitreinfolge ist - Und das 
Plattformunabhängig.

Klar, das ist wirklich viel zum schreiben, aber ich glaube, wenn man 
soetwas ich C/C++ macht (wenn es denn überhaupt geht), ist der 
Schreibaufwand ähnlich groß. Aber wenn der Ada-Compiler das gefressen 
hat, geht bei C/C++ das tagelange debuggen vermutlich erst los.
Mag vielleicht auch daran liegen, das der Ada-Programmierer sich 
deutlich mehr um seinen Code Gedanken machen muss, als alle anderen.

Das war das Wort zum Sonntag ;)
Happy Coding euch allen :P

PS:
Nicht falsch verstehen, ich komme aus der Embedded C-Welt! Ich kenne, 
liebe und verfluche es. Ich wollte einfach mal wieder was neues lernen, 
nachdem ich zwei Wochen lang einen Bug in einem nicht von mir stammenden 
C-Code gesucht habe und relativ frustriert war, wie weitreichend der 
Code neu gestaltet werden musste, um ihn auszumerzen ohne die restliche 
Funktion des Programms aufrecht zu halten. Noch dazu, da dieser Code auf 
2 (x86 und ARM) von drei (x86, ARM, PPC) Plattformen lief, ohne Probleme 
zu bereiten - was nicht heißen soll, das der Fehler auch nicht auf den 
anscheinend Fehlerfreien Plattformen auftreten könnte :-/

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.