Macro mit interner Fkt

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Macro mit interner Fkt

Beitrag von Maik81SE »

Moin habe mal eine Frage, bevor ich mich an die Umsetzung mache...

Habe in C++ ein Macro in folgender Form gefunden

Code: Alles auswählen

#define SET_USI_TO_SEND_ACK( ) \
{ \
  /* prepare ACK */ \
  USIDR = 0; \
  /* set SDA as output */ \
  DDR_USI |= ( 1 << PORT_USI_SDA ); \
  /* clear all interrupt flags, except Start Cond */ \
  USISR = \
       ( 0 << USI_START_COND_INT ) | \
       ( 1 << USIOIF ) | ( 1 << USIPF ) | \
       ( 1 << USIDC )| \
       /* set USI counter to shift 1 bit */ \
       ( 0x0E << USICNT0 ); \
}
damit komme ich auch problemlos klar und würde dies auch gerne in FP umschreiben.
dies sollte in etwa so ausschauen.

Code: Alles auswählen

{$define SET_USI_TO_READ_ACK : begin
          // SDA als Input
          DDR_USI := 1 shl PORT_USI_SDA;
          // vorbereiten ACK
          USI_DIR := 0;
          // Interrupts Flacs löschen und USI zähler = 1
          USISR   := (0 shl USISIF) or (1 shl USIOIF) or (1 shl USIPF) or (1 shl USIDC) or ($0E shl USICNT);
          end;
          }
Frage 1)
Würde das so funktionieren?

Frage 2) Wären ggf die Option als Funktion/Procedure angemessener, da die ja nur aufgerufen wird, wenn diese auch verwendet wird.
Macros werden nach meinem Wissenstand wohl immer mit Compiliert.

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Macro mit interner Fkt

Beitrag von PascalDragon »

Maik81SE hat geschrieben:
Do 21. Jan 2021, 05:43
Frage 1)
Würde das so funktionieren?
Es muss {$define SET_USI_TO_READ_ACK := begin heißen, aber ansonsten müsstest du es ausprobieren. Die Makrofunktionalität in FPC ist nur für kleine Sachen gedacht. Deshalb...
Maik81SE hat geschrieben:
Do 21. Jan 2021, 05:43
Frage 2) Wären ggf die Option als Funktion/Procedure angemessener, da die ja nur aufgerufen wird, wenn diese auch verwendet wird.
Macros werden nach meinem Wissenstand wohl immer mit Compiliert.
... ist der Modus Operandi beim Portieren nach FPC Funktionen zu verwenden, die an sich auch als inline deklariert werden können. Und Makros selbst werden ja nicht mitkompiliert, da die rein im Präprozessor behandelt werden. Im Endeffekt ersetzt der Präprozessor jede Fundstelle von SET_USI_TO_READ_ACK durch den Text des Makros (letztlich das was auch inline bewirkt, es sei denn der Compiler entscheidet, dass Inlining hier keine gute Idee ist).

Außerdem kannst du Makros nicht über Unitgrenzen hinweg einsetzen.
FPC Compiler Entwickler

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Macro mit interner Fkt

Beitrag von Socke »

Die Makros in Pascal können nur einfache Textersetzung. Da du im Makro soweit ich sehe keine hast, könnte das tatsächlich funktionieren - probier es einfach aus!

Die besserere Alternative ist eine inline-Prozedur. Damit übernimmt der Compiler den Prozedurrumpf an die Aufrufstelle.

Code: Alles auswählen

procedure SET_USI_TO_READ_ACK; inline;
begin
          // SDA als Input
          DDR_USI := 1 shl PORT_USI_SDA;
          // vorbereiten ACK
          USI_DIR := 0;
          // Interrupts Flacs löschen und USI zähler = 1
          USISR   := (0 shl USISIF) or (1 shl USIOIF) or (1 shl USIPF) or (1 shl USIDC) or ($0E shl USICNT);
end;
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

marcov
Beiträge: 1100
Registriert: Di 5. Aug 2008, 09:37
OS, Lazarus, FPC: Windows ,Linux,FreeBSD,Dos (L trunk FPC trunk)
CPU-Target: 32/64,PPC(+64), ARM
Wohnort: Eindhoven (Niederlande)

Re: Macro mit interner Fkt

Beitrag von marcov »

Wenn man ein inline Prozedur definiert, sollen alle Identifiers definiert (und Typiert) sein. Mit ein Makro ist das nicht nötig, da muss es nur auf Nutz Platz definiert sein.

Inline Prozedures kann man aber in Units definieren und mit USES nutzen.

Macros kann man nur mit $i nutzen, wenn sie in ein anderem Datei definiert werden.

Benutzeravatar
Maik81SE
Beiträge: 308
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.0.0.3 FPC 3.2.2); Windows 10 (L 3.99.0.0 FPC 3.2.0)
CPU-Target: x86-64; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Macro mit interner Fkt

Beitrag von Maik81SE »

PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:12
Es muss {$define SET_USI_TO_READ_ACK := begin heißen, aber ansonsten müsstest du es ausprobieren. Die Makrofunktionalität in FPC ist nur für kleine Sachen gedacht. Deshalb...
Ok, da fällt es schon mal abakta.
PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:12
Außerdem kannst du Makros nicht über Unitgrenzen hinweg einsetzen.
und das könnte ggf in der weiteren Entwicklung des Programmes ein Problem werden.
Socke hat geschrieben:
Do 21. Jan 2021, 09:14
Die Makros in Pascal können nur einfache Textersetzung. Da du im Makro soweit ich sehe keine hast, könnte das tatsächlich funktionieren - probier es einfach aus!

Die besserere Alternative ist eine inline-Prozedur. Damit übernimmt der Compiler den Prozedurrumpf an die Aufrufstelle.

Code: Alles auswählen

procedure SET_USI_TO_READ_ACK; inline;
begin
          // SDA als Input
          DDR_USI := 1 shl PORT_USI_SDA;
          // vorbereiten ACK
          USI_DIR := 0;
          // Interrupts Flacs löschen und USI zähler = 1
          USISR   := (0 shl USISIF) or (1 shl USIOIF) or (1 shl USIPF) or (1 shl USIDC) or ($0E shl USICNT);
end;
Da werd ich wohl mal beide Parallel schreiben. :D :D Hab ja 4 ATtiny25.
Das ergebnis mit dem geringsten Code-verbrauch nehm ich dann. Jedes Byte wird zählen.
Zumindest bekomme ich schon mal keine Fehlermeldungen, hab aber auch erst heute in meiner Frühstückpause angefangen zu schreiben...

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.0.0.3 FPC 3.2.2);
windows 10 (L 3.99.0.0 FPC 3.2.0)

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: Macro mit interner Fkt

Beitrag von hubblec4 »

Maik81SE hat geschrieben:
Do 21. Jan 2021, 18:32
PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:12
Außerdem kannst du Makros nicht über Unitgrenzen hinweg einsetzen.
und das könnte ggf in der weiteren Entwicklung des Programmes ein Problem werden.
Das ist nicht ganz exakt. Man kann die Macros in eine .inc Datei schreiben und diese dann in jeglicher unit einbinden.

Ich bastel da selber gerade an einem größeren Projekt und die Macros funktionieren nun sehr gut.
Die Macros sind in FPC leider nicht soweit entwickelt wie in c++ und man findet nur wenig Infos.

Es fehlt die Parameterisierung. Ich hatte mich ausgibig mit diesem Thema befassen müssen weil der Umfang im Projekt sonst riesig geworden wäre.
Dabei bezeihe ich mich auf identischen bzw. fast identischen Quellcode.

Ich habe nach sehr viel try-and-error einiges herausgefunden:
Socke hat geschrieben:
Do 21. Jan 2021, 09:14
Die Makros in Pascal können nur einfache Textersetzung.
....
Im Prinzip ja, aber es werden mehrere Durchläufe ausgeführt, solange bis alle Macros aufgelöst sind,
und diesen Umstand habe ich mir zu nutzen gemacht um dennoch Parameter für den Quellcode zu nutzen.
(Oder man hätte einen anderen externen PreCompiler installieren müssen, was ich nicht wollte.)

Dabei ist es dann möglich in einer unit (wo die Macro.inc datei eingebunden ist) einen neuen "Parameter-Macro" zu definieren der dann in der .inc Datei angewendet wird.

In meinem Projekt müssen über 270 Elemente definiert werden.
Also hat man dann über 270 Klassen und man kann sich vorstellen wieviel Code das wird,
Für jede Klasse gibts einen constructor, destructor, ein weiteren constructor der gleich das element kopiert usw.

Im c++ code sind, dank des Macros, nur noch zwei Zeilen code nötig für das "anlegen" der Klasse.
Durch die Paramter, welche vorher duch ein Macro definiert werden, brauche ich 3 bis 6 Zeilen um eine Klasse mit all ihren methoden zu definieren.


Macros zu verwenden um reine Methoden damit zu ersetzen finde ich nicht so gut, da ist die "inline" Geschichte schon besser.
Wobei das "inline" oft nur bei methoden verwendet wird, die nur eine Zeile Code haben. Oder zumindestens nicht sehr viel.
Sollte natürlich die Methode welche man oft verwendet sehr komplex sein, macht ein "inline" vielleicht auch keinen Sinn mehr.


Und hier noch ein wenig Code:

das Basis Klassen Macro in der Macro.inc Datei

Code: Alles auswählen

(* EBML class head with context values *)
{$define DECLARE_EBML_CLASS_HEAD:=
 public
  constructor Create; override;
  constructor Create(const ElementToClone: EBML_CLASS); --> EBML_CLASS ist der Parameter Macro
  function CreateElement: TEbmlElement; override;
  class function Get_Context: PEbmlContext; override; static;

  function Clone: TEbmlElement; override;
}  
und hier in einer unit

Code: Alles auswählen

{$i Macro.inc}

type

TEbmlVersion                                    = class(TEbmlUInteger)
  {$define EBML_CLASS:=TEbmlVersion}             DECLARE_EBML_CLASS_HEAD
 end; 

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Macro mit interner Fkt

Beitrag von PascalDragon »

hubblec4 hat geschrieben:
Di 9. Feb 2021, 03:30
Maik81SE hat geschrieben:
Do 21. Jan 2021, 18:32
PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:12
Außerdem kannst du Makros nicht über Unitgrenzen hinweg einsetzen.
und das könnte ggf in der weiteren Entwicklung des Programmes ein Problem werden.
Das ist nicht ganz exakt. Man kann die Macros in eine .inc Datei schreiben und diese dann in jeglicher unit einbinden.
Was funktional das gleiche ist wie das Makro in jeder Unit zu deklarieren. Darum ging es mir.
hubblec4 hat geschrieben:
Di 9. Feb 2021, 03:30
In meinem Projekt müssen über 270 Elemente definiert werden.
Also hat man dann über 270 Klassen und man kann sich vorstellen wieviel Code das wird,
Für jede Klasse gibts einen constructor, destructor, ein weiteren constructor der gleich das element kopiert usw.

Im c++ code sind, dank des Macros, nur noch zwei Zeilen code nötig für das "anlegen" der Klasse.
Durch die Paramter, welche vorher duch ein Macro definiert werden, brauche ich 3 bis 6 Zeilen um eine Klasse mit all ihren methoden zu definieren.
Ich selbst würde da ja einfach zu generics greifen...
FPC Compiler Entwickler

Benutzeravatar
Winni
Beiträge: 1577
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.2.2, fpc 3.2.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Macro mit interner Fkt

Beitrag von Winni »

Hi!

Als jemand, der OOP immer kritisch gegenüberstand, frage:

Müssen denn 270 Klassen wirklich sein?
Durch die advanced Records kann man fast das Gleiche wie mit Klassen erreichen.

Winni

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1432
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Macro mit interner Fkt

Beitrag von fliegermichl »

Das klingt nach einem sinnvollen Einsatz von generics?

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Macro mit interner Fkt

Beitrag von m.fuchs »

Winni hat geschrieben:
Di 9. Feb 2021, 11:15
Müssen denn 270 Klassen wirklich sein?
Durch die advanced Records kann man fast das Gleiche wie mit Klassen erreichen.
Es fehlt dann die Vererbung und damit hat man dann eventuell jede Menge Code-Duplikation.
Also eher ein Nachteil. Jetzt käme die Frage nach dem Vorteil.

Für den OP wären an dieser Stelle aber, wie bereits genannt, Generics wohl die Lösung.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: Macro mit interner Fkt

Beitrag von hubblec4 »

PascalDragon hat geschrieben:
Di 9. Feb 2021, 09:39
hubblec4 hat geschrieben:
Di 9. Feb 2021, 03:30
Maik81SE hat geschrieben:
Do 21. Jan 2021, 18:32



und das könnte ggf in der weiteren Entwicklung des Programmes ein Problem werden.
Das ist nicht ganz exakt. Man kann die Macros in eine .inc Datei schreiben und diese dann in jeglicher unit einbinden.
Was funktional das gleiche ist wie das Makro in jeder Unit zu deklarieren. Darum ging es mir.
Mmhh, aber jedesmal das Marco in eine Unit schreiben macht doch auch keinen Sinn, also manuell meine ich.
Mir ist schon klar das wenn man eine .inc Datei included, dessen Inhalt auch nur "reinkopiert" wird.

PascalDragon hat geschrieben:
Di 9. Feb 2021, 09:39
hubblec4 hat geschrieben:
Di 9. Feb 2021, 03:30
In meinem Projekt müssen über 270 Elemente definiert werden.
Also hat man dann über 270 Klassen und man kann sich vorstellen wieviel Code das wird,
Für jede Klasse gibts einen constructor, destructor, ein weiteren constructor der gleich das element kopiert usw.

Im c++ code sind, dank des Macros, nur noch zwei Zeilen code nötig für das "anlegen" der Klasse.
Durch die Paramter, welche vorher duch ein Macro definiert werden, brauche ich 3 bis 6 Zeilen um eine Klasse mit all ihren methoden zu definieren.
Ich selbst würde da ja einfach zu generics greifen...
Mmmh...damit kenne ich mich noch nicht so genau aus, hatte das nurmal am Rand irgendwo gelesen, aber es scheint mir nicht das richtige zu sein.

Aber bin da natürlich gern offen für neues.

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: Macro mit interner Fkt

Beitrag von hubblec4 »

Winni hat geschrieben:
Di 9. Feb 2021, 11:15
Hi!

Als jemand, der OOP immer kritisch gegenüberstand, frage:

Müssen denn 270 Klassen wirklich sein?
Durch die advanced Records kann man fast das Gleiche wie mit Klassen erreichen.

Winni
Ja das muss alles so sein. Die Klassen lassen sich später besser handhaben als Records.
Es geht schon damit los das Records nicht in eine "Liste" aufgenommen werden können, da brauche ich immer ein Array, und Arrays....naja haben ihre berechtigung, aber ich benutze lieber eine Liste.

Und hinzukommt noch das die Klassen ja nicht völlig identisch sind, und vorallem müssen sie erweiterbar bleiben. Denn bei einigen Elementen gibt es zustzliche Methoden, welche man einfach "anhängen" kann.

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: Macro mit interner Fkt

Beitrag von hubblec4 »

habe mir mal ganz kurz

https://wiki.freepascal.org/Generics

angeschaut.

und es fühlt sich für mich nicht "brauchbar" an.

Ich nutze solche generic Sachen auch um eine "spezial" Liste für einen bestimmten Typen zu haben.

Die normale TObjectList würde ja auch immer funktionieren, aber da hat man eben nur "Objecte" drin welche dann gecastet werden müssen.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1432
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Macro mit interner Fkt

Beitrag von fliegermichl »

hubblec4 hat geschrieben:
Di 9. Feb 2021, 14:29
Es geht schon damit los das Records nicht in eine "Liste" aufgenommen werden können, da brauche ich immer ein Array, und Arrays....naja haben ihre berechtigung, aber ich benutze lieber eine Liste.
Wieso das denn?

Code: Alles auswählen

type
 PMyRecord = ^TMyRecord;
 TMyRecord = record
  Feld1 : string;
  Feld2 : integer;
 end;

var  
 List : TList;
 R : PRecord;
 i : integer;
begin
 for i := 1 to 10 do
 begin
  new(p); with P^ do begin Feld1 := 'Testheimer'; Feld2 := i; end; List.Add(p);
 end;
end;
und schon hast du 10 Records in einer Liste.

hubblec4
Beiträge: 341
Registriert: Sa 25. Jan 2014, 17:50

Re: Macro mit interner Fkt

Beitrag von hubblec4 »

fliegermichl hat geschrieben:
Di 9. Feb 2021, 14:37
hubblec4 hat geschrieben:
Di 9. Feb 2021, 14:29
Es geht schon damit los das Records nicht in eine "Liste" aufgenommen werden können, da brauche ich immer ein Array, und Arrays....naja haben ihre berechtigung, aber ich benutze lieber eine Liste.
Wieso das denn?

Code: Alles auswählen

type
 PMyRecord = ^TMyRecord;
 TMyRecord = record
  Feld1 : string;
  Feld2 : integer;
 end;

var  
 List : TList;
 R : PRecord;
 i : integer;
begin
 for i := 1 to 10 do
 begin
  new(p); with P^ do begin Feld1 := 'Testheimer'; Feld2 := i; end; List.Add(p);
 end;
end;
und schon hast du 10 Records in einer Liste.
Mhh, also genau genommen ist das nicht richtig.
Man hat 10 Pointer in einer Liste, aber nicht die Records selbst...wird aber jetzt zu Offtopic.

Und das mit der Liste ist ja nicht das eigentliche Problem.
Die komplette Vererbungslehre geht bei Records flöten, und diese ist zwingend erforderlich.

Antworten