Rekursiver CallProc

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Mathias
Beiträge: 6923
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Rekursiver CallProc

Beitrag von Mathias »

Ich stehe an, weil ich ein C-Programm umsetzen wollte. In diesem hat es eine CallProc Function, welche man Rekursiv aufrufen muss.
Ich dachte ich hätte dies schon mal, aber bei genaueren betrachten habe ich gesehen, das es dazumal eine procedure war. Und im jetztigen Programm eine function.

Daher habe ich diesen Test geschrieben und muss festellen, das es mit procedure geht, aber mit function hat der Compiler Mühe.

Es kommt folgender Fehler:

Code: Alles auswählen

project1.lpr(24,19) Error: Incompatible type for arg no. 1: Got "Pointer", expected "<procedure variable type of function(LongInt):LongInt;CDecl>"
Kann man dies umgehen, oder habe ich einen Überlegungsfehler ?

Oder geht dies mit FPC einfach nicht ?

Code: Alles auswählen

program project1;

type
  TProc = procedure(i: integer); cdecl;
  TFunc = function(i: integer): integer; cdecl;

  procedure InitProc(fn: TProc);
  begin
    // Ist in einer C-Lib
  end;

  procedure InitFunc(fn: TFunc);
  begin
    // Ist in einer C-Lib
  end;

  procedure proc(i: integer); cdecl;
  begin
    InitProc(@proc);
  end;

  function func(i: integer): integer; cdecl;
  begin
    InitFunc(@func); // geht nicht, Fehlermeldung
  end;

begin
  InitProc(@proc);
  InitFunc(@func);
end.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

MmVisual
Beiträge: 1581
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 4 FPC 3.2.2)
CPU-Target: 32/64Bit

Re: Rekursiver CallProc

Beitrag von MmVisual »

Bei

function func(i: integer): integer;

gibt es ja auch kein Return-Wert.

Mache mal das dazu:
Result := 0;

Und wenn die rekursive Schleife nicht irgend wann mal unterbrochen wird ist irgendwann auch mal der Stack voll und das Programm kann dann nur noch beendet werden.

Das Schlüsselwort "Implementation" fehlt.
Und deklarieren "func" dann im Deklarationsteil, damit müsste der Compiller das wissen,
EleLa - Elektronik Lagerverwaltung - www.elela.de

Mathias
Beiträge: 6923
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Rekursiver CallProc

Beitrag von Mathias »

Mache mal das dazu:
Result := 0;
Dies ist mir schon bewusst, ich habe es auf ein Minimum abgespeckt.
Und wenn die rekursive Schleife nicht irgend wann mal unterbrochen wird ist irgendwann auch mal der Stack voll und das Programm kann dann nur noch beendet werden.
Bei dem ganzen handelt es sich um eine Timer Funktion in einer C-Lib, welche ich durch InitFunc und initProc ersetzt habe.
Das Schlüsselwort "Implementation" fehlt.
Seit wann braucht es implementation im Hauptprogramm, dies braucht es nur in einer Unit.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

MmVisual
Beiträge: 1581
Registriert: Fr 10. Okt 2008, 23:54
OS, Lazarus, FPC: Winuxarm (L 4 FPC 3.2.2)
CPU-Target: 32/64Bit

Re: Rekursiver CallProc

Beitrag von MmVisual »

Ich habe den verdacht (nicht jetzt getestet), dass wenn man eine Funktion nutzen möchte diese vorher bekannt sein muss.
Klar im Programm kann man eine Funktion nutzen, ohne diese vorher zu deklarieren. Allerdings muss man dann die Reihenfolge der Funktionen im Quelltext einhalten. Und da die Funktion sich selbst aufruft müsste diese also vorher deklariert sein (wäre zumindest denkbar).

Ich würde im Code alle Warnungen und Hints raus programmieren, nicht dass die Kombination daraus im Compiller eine falsche Meldung aus gibt. Daher der Hinweis wegen "Result".
EleLa - Elektronik Lagerverwaltung - www.elela.de

Mathias
Beiträge: 6923
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Rekursiver CallProc

Beitrag von Mathias »

Ich habe den verdacht (nicht jetzt getestet), dass wenn man eine Funktion nutzen möchte diese vorher bekannt sein muss.
Dies wäre etwas komisch. Dann würde sich die procedure gleich verhalten wie die function.
Mit procedure gehr es ja.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
Zvoni
Beiträge: 378
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Rekursiver CallProc

Beitrag von Zvoni »

Huh? Ziemlich skurril....
Das hier compiliert

Code: Alles auswählen

program project1;
{$mode objfpc}{$H+}
type
  TProc = procedure(i: integer); cdecl;
  TFunc = function(i: integer): integer; cdecl;

  procedure InitProc(fn: TProc);
  begin
    // Ist in einer C-Lib
  end;

  procedure InitFunc(fn: TFunc);
  begin
    // Ist in einer C-Lib
  end;

  procedure proc(i: integer); cdecl;
  begin
    InitProc(@proc);
  end;

  function func(i: integer): integer; cdecl;
  begin
    InitFunc(TFunc(func)); // <---- "@" ersetzt mit "TFunc"-Cast
  end;

begin
  InitProc(@proc);
  InitFunc(@func);  //Das hier wird aber nicht angemotzt
end.
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Mathias
Beiträge: 6923
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Rekursiver CallProc

Beitrag von Mathias »

Bei mir geht dies nicht. Egal ob mit oder ohne. {$mode objfpc}{$H+}

Code: Alles auswählen

  function func(i: integer): integer; cdecl;
  begin
    InitFunc(TFunc(func)); // <---- "@" ersetzt mit "TFunc"-Cast
  end;
So kompiliert er es:

Code: Alles auswählen

InitFunc(TFunc(@func));

Ich habe das ganze noch ein wenig abgeändert:

Code: Alles auswählen

program project1;
type
  TProc = procedure(i: integer); cdecl;
  PProc=^TProc;
  TFunc = function(i: integer): integer; cdecl;
  PFunc=^TFunc;

  procedure InitProc(fn: TProc);
  const
    i:Integer=0;
  begin
    // Ist in einer C-Lib

    WriteLn('Proc ',i);
    Inc(i);
    fn(i);
  end;

  procedure InitFunc(fn: TFunc);
  const
    i:Integer=0;
  begin
    // Ist in einer C-Lib

    WriteLn('Func ',i);
    Inc(i);
    fn(i);
  end;

  procedure proc(i: integer); cdecl;
  begin
    InitProc(@proc);
  end;

  function func(i: integer): integer; cdecl;
  begin
//    InitFunc(@func); // geht nicht
    InitFunc(TFunc(@func));
    Result:=0;
  end;

begin
  InitProc(@proc);
//  InitFunc(@func);
end.
Starte ich das gabze mit InitProc(@proc); geht der Zähler auf etwas 130'000 und dann überläuft wie erwartet der Stack.
Starte ich mit InitFunc(@func); geht der Zähler auf 2 und dann SIGSEV 216.


So nebenbei habe ich in meinem original Programm bei der C-Bindund die function durch procedure ersetzt, dann läuft es. Es ist ein Timer der alle Sekunde durchläuft. Aber sauber wird dies nicht sein, da die procedure ein Result zurück geben kann.

Das original C-Demo sieht so aus:

In der main.c

Code: Alles auswählen

static int on_timer(Tickit *t, TickitEventFlags flags, void *_info, void *user);
static int on_timer(Tickit *t, TickitEventFlags flags, void *_info, void *user)
{
  int *counterp = user;

  (*counterp)++;
  tickit_window_expose(timerwin, NULL);

  tickit_watch_timer_after_msec(t, 1000, 0, &on_timer, user);

  return 0;
}
Im Header:

Code: Alles auswählen

typedef int TickitCallbackFn(Tickit *t, TickitEventFlags flags, void *info, void *user);
...
void *tickit_watch_timer_after_msec(Tickit *t, int msec, TickitBindFlags flags, TickitCallbackFn *fn, void *user);
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

PascalDragon
Beiträge: 958
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: Rekursiver CallProc

Beitrag von PascalDragon »

In Pascal und Object Pascal ist der Name der Funktion gleichzeitig das Funktionsergebnis (in Object Pascal gibt es hier auch Result als Alias). Das heißt ein Zeiger auf den Namen der Funktion ist in der Tat ein Zeiger auf diese Ergebnisvariable. Da standardmäßig Zeiger nicht typisiert sind (es sei denn du nutzt $TypedAddress On) beschwert sich der Compiler da auch nur bedingt.

Um das Problem zu umgehen, kannst du eine Variable nutzen, welche die Funktion referenziert:

Code: Alles auswählen

function Func(aArg: Integer): Integer; cdecl; forward;

var
  MyFunc: TFunc = @Func;

function Func(aArg: Integer): Integer; cdecl;
begin
  InitFunc(MyFunc);
end;
FPC Compiler Entwickler

Mathias
Beiträge: 6923
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Rekursiver CallProc

Beitrag von Mathias »

Danke so scheint es zu funktionieren.

Was mir aber immer noch ein Rätsel ist, wieso funktioniert es in der Main direkt und innerhalb der Function nicht.?

Code: Alles auswählen

InitFunc(@func);  // nur in der Main

Ich habe noch ein Experiment gemacht.

Code: Alles auswählen

  function func(i: integer): integer; cdecl;
  begin
    WriteLn('func:   ', PtrUInt(@func));
    WriteLn('Myfunc: ', PtrUInt(MyFunc));
    //      InitFunc(MyFunc);
    Result := 0;
  end;

begin
  WriteLn('main:   ', PtrUInt(@func));
  InitFunc(@func);
end.
"func" hat verschiedene Adressen.

Ausgabe:

Code: Alles auswählen

main:   4198832
func:   140731057806292
Myfunc: 4198832
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
Zvoni
Beiträge: 378
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Rekursiver CallProc

Beitrag von Zvoni »

Vermutung:
Weil so wie PascalDragon es gesagt hat:
Innerhalb der "Func"-Function ist "@Func" der Zeiger zur Result-Variablen, während das "@Func" in der Main tatsächlich der Zeiger zur Funktion ist?
Lässt sich ja einfach prüfen mit einem zusätzlichen
"Writeln(PtrUint(@Result));" innerhalb der "Func"-Funktion
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Benutzeravatar
theo
Beiträge: 10890
Registriert: Mo 11. Sep 2006, 19:01

Re: Rekursiver CallProc

Beitrag von theo »

Genau!

Result:=12; ist innerhalb der Funktion "MyFunc" das Gleiche wie MyFunc:=12;

Ausserhalb natürlich nicht.

Mathias
Beiträge: 6923
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Rekursiver CallProc

Beitrag von Mathias »

Result:=12; ist innerhalb der Funktion "MyFunc" das Gleiche wie MyFunc:=12;
Jetzt ist mir ein :idea: aufgegangen.
Das kann ja gar nicht funktionieren. Das kommt davon wen man nur noch Result verwendet. :oops:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Mathias
Beiträge: 6923
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Rekursiver CallProc

Beitrag von Mathias »

Ich habe es jetzt auch in meinem portierten C-Programm probiert und es läuft, auch wen es sehr unschön aussieht.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

PascalDragon
Beiträge: 958
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: Rekursiver CallProc

Beitrag von PascalDragon »

Zvoni hat geschrieben: Mi 7. Mai 2025, 08:41 Innerhalb der "Func"-Function ist "@Func" der Zeiger zur Result-Variablen, während das "@Func" in der Main tatsächlich der Zeiger zur Funktion ist?
Genau das.
FPC Compiler Entwickler

Antworten