Results 1 to 8 of 8

Thread: Problems when using Callbacks

  1. #1
    ddaedalus
    Guest

    Problems when using Callbacks

    Hi,

    I'm trying to realize a Callback using TFPList ( many Procs should be able to hook themselves up ) . But when I try to add a Procedure to the List i get a:
    Code:
    WidgetDesktop.UserEventCallbackList.Add(@UserEvent);
    ...
    infor.pp&#40;74,59&#41; Error&#58; Incompatible type for arg no. 1&#58; Got "<procedure>", expected "Pointer"
    FreePascal fixes_2_2, quite recent svn

    Thanks in advance
    Bartek

  2. #2

    Problems when using Callbacks

    Maybe they added the "Typed @ operator" compiler setting to improve Delphi compatibility, and it is turned on?

  3. #3
    ddaedalus
    Guest

    Problems when using Callbacks

    Thx, I didn't know a compiler directive like this existed. But:
    Code:
    1.1.59 $T or $TYPEDADDRESS &#58; Typed address operator &#40;@&#41;
    In the &#123;$T+&#125; or &#123;$TYPEDADDRESS ON&#125; state the @ operator, when applied to a variable, returns
    a result of type ÀÜT, if the type of the variable is T. In the &#123;$T-&#125; state, the result is always an untyped
    pointer, which is assignment compatible with all other pointer types.
    It only applies to variables. Nevertheless I tried turning it off. Didn't help.

  4. #4

    Problems when using Callbacks

    Bartek, can you post up a simple sample application that shows the same problem that you are having. If we had that I'm sure you would get an answer or a fix much faster.

    The other option would be to show us the definition of your method that your adding and the block of code that is adding it. This way we have a bit more context then just a single line.

    Callbacks usually take the form of:[pascal]type
    TMyCallBackMethod=procedure(SomeVar:AnsiString);

    procedure AMethodToCallback(SomeVar:AnsiString);
    begin
    // Do something
    end;

    begin
    // Adding to list some other method
    whatindex := FMyProcList.Add(@AMethodToCallback);
    // Calling
    FMyProcList.Methods[whatindex]('This is a test');
    end;[/pascal]

    As I said, if this doesn't help, then post up a simple sample and see what you get back. Building the simple sample may also help you find your solution

  5. #5
    ddaedalus
    Guest

    Problems when using Callbacks

    Sorry, I thought i made a very stupid error, therefore I didn't post more. Seems like my problem is slightly more complex than i thought.
    Code:
    &#123;$mode objfpc&#125;&#123;$h+&#125;
    program test;
    uses sysutils, classes;
    type
    	TCallback = procedure&#40;param1&#58; integer; param2&#58; TObject&#41; of object;
    	
    	TBlub = class
    		constructor Create&#40;cbl&#58; TFPList&#41;;
    		destructor Destroy; override;
    		
    		procedure Callback&#40;param1&#58; integer; param2&#58; TObject&#41;;
    	end;
    
    constructor TBlub.Create&#40;cbl&#58; TFPList&#41;;
    begin
    	cbl.Add&#40;@Callback&#41;;
    end;
    
    destructor TBlub.Destroy;
    begin
    	inherited Destroy;
    end;
    
    procedure TBlub.Callback&#40;param1&#58; integer; param2&#58; TObject&#41;;
    begin
    	writeln&#40;'Callback'&#41;;
    end;
    
    var
    	CallbackList&#58; TFPList;
    	blub&#58; TBlub;
    begin
    	CallbackList&#58;=TFPList.Create;
    	blub&#58;=TBlub.Create&#40;CallbackList&#41;;
    	TCallback&#40;CallbackList.items&#91;0&#93;^&#41;&#40;0, nil&#41;;
    end.
    ...
    bartek@bartek-laptop&#58;/tmp$ fpc test.pp
    Free Pascal Compiler version 2.1.3 &#91;2007/05/04&#93; for i386
    Copyright &#40;c&#41; 1993-2007 by Florian Klaempfl
    Target OS&#58; Linux for i386
    Compiling test.pp
    test.pp&#40;16,19&#41; Error&#58; Incompatible type for arg no. 1&#58; Got "<procedure>", expected "Pointer"
    test.pp&#40;37&#41; Fatal&#58; There were 1 errors compiling module, stopping
    Fatal&#58; Compilation aborted
    Error&#58; /usr/local/bin/ppc386 returned an error exitcode &#40;normal if you did not specify a source file to be compiled&#41;
    This should represent what I'm trying to do. Don't be offended because of my hacky accessing of items and not freeing

    bartek

  6. #6

    Problems when using Callbacks

    Ahh... The problem is that you are trying to place a class method pointer into a standard list. This causes problems due to the fact that the class instance is lost when the method is called. Thus FPC (and Delphi for that matter) don't know how to resolve the calling structure.

    You can hack around this if you are careful not to reference self within you callback methods, but I refuse to show you as it can break things that you never thought about .

    The best option is to move callback outside of the object itself and make it a standard procedure. Then you can add it to the list and call the callback with the object reference directly. Here is a sample made from your code:[pascal]program CBTest1;

    {$mode objfpc}{$H+}

    uses
    {$IFDEF UNIX}{$IFDEF UseCThreads}
    cthreads,
    {$ENDIF}{$ENDIF}
    Classes
    { add your units here };

    type
    TBlub = class
    constructor Create(cbl: TFPList);
    destructor Destroy; override;

    end;
    TCallback = procedure(self : TBlub; param1: integer; param2: TObject); // Changed to normal method

    procedure Callback(self : TBlub; param1: integer; param2: TObject); // Define method and add a self reference for useability
    begin
    writeln('Callback: ', integer(pointer(self)), #9, param1, #9, integer(pointer(param2))); // Just to show it works
    end;

    constructor TBlub.Create(cbl: TFPList);
    begin
    cbl.Add(@Callback); // Register
    end;

    destructor TBlub.Destroy;
    begin
    inherited Destroy;
    end;

    var
    CallbackList: TFPList;
    blub: TBlub;
    i: integer;
    begin
    CallbackList:=TFPList.Create;
    blub:=TBlub.Create(CallbackList);
    for i := 0 to 9 do // Just to show it works
    TCallback(CallbackList.items[0])(blub, i, nil); // Remove ^ you don't need to de-reference the pointer
    Callbacklist.Free; // Cleanup
    blub.Free; // Cleanup
    readln();
    end.[/pascal]

    Of course, if you don't need the object pointer, then don't use objects or simply remove the self option. In the end this performs the same action and has little more overhead on implementation .

  7. #7
    ddaedalus
    Guest

    Problems when using Callbacks

    Ok, I see. Thanks. Is my understanding right, that I would by able to use this, If i made a customized ( in fpc generics, when generics would work ) list?
    Your way solving this is good, and doesn't requiere me to rewrite things. Nevertheless you made me curious. How would I be able to hack around this. :twisted: ( "hack to learn" ^^ )

    Thanks again
    bartek

  8. #8

    Problems when using Callbacks

    It requires non-crossplatform assembly code. The best way to learn is to look at Pascal Script for Delphi http://www.remobjects.com/page.asp?i...-EEBBE7E302E6}). Basically you have to push the pointer to the class instance onto the stack, then push the method address and variables onto the stack. Call the method and check the stack and registers for return values. Finally cleanup after yourself. Honestly, this isn't something I fell comfortable explaining more then just pointing to a sample, as far as I know there is no other way that will work. I can show you code that will compile, but it will blow up when you run it .

    Actually, here is code that will compile (in theory you would only have to put the method pointer above the calling of the actual method in practice it doesn't work):[pascal]program cbtest2;

    {$mode objfpc}{$H+}

    uses
    {$IFDEF UNIX}{$IFDEF UseCThreads}
    cthreads,
    {$ENDIF}{$ENDIF}
    Classes
    { add your units here };

    type
    TMyCallback = procedure(intval1 : integer; obj : TObject) of object;
    PMyCallback = ^TMyCallback;

    { TMyCallbackList }

    TMyCallbackList=class
    private
    fMethods : TList;
    function getCount: integer;
    function GetMethod(index: integer): TMyCallback;
    public
    constructor Create;
    destructor Destroy; override;

    function Add(WhatMethod : TMyCallback) : integer;
    property Method[index:integer]: TMyCallback read GetMethod;
    property Count : integer read getCount;
    end;

    { TMyObject }

    TMyObject = class
    public
    constructor Create(WhatList : TMyCallbackList);

    procedure ACallback(intval1 : integer; obj : TObject);
    end;

    { TMyObject }

    constructor TMyObject.Create(WhatList: TMyCallbackList);
    begin
    WhatList.Add(@ACallback);
    end;

    procedure TMyObject.ACallback(intval1: integer; obj: TObject);
    begin
    writeln('Testing the callback method: ', integer(pointer(self)), #9, intval1, #9, integer(pointer(obj)));
    end;

    { TMyCallbackList }

    function TMyCallbackList.getCount: integer;
    begin
    result := fMethods.Count;
    end;

    function TMyCallbackList.GetMethod(index: integer): TMyCallback;
    begin
    result := TMyCallback(fMethods[index]^);
    end;

    constructor TMyCallbackList.Create;
    begin
    fMethods := TList.Create;
    end;

    destructor TMyCallbackList.Destroy;
    begin
    fMethods.Free;
    inherited Destroy;
    end;

    function TMyCallbackList.Add(WhatMethod: TMyCallback): integer;
    begin
    fMethods.Add(@WhatMethod);
    end;

    var
    ml : TMyCallbackList;
    o : TMyObject;
    i : Integer;
    begin
    ml := TMyCallbackList.Create;
    try
    o := TMyObject.Create(ml);
    try
    for i := 0 to 9 do
    ml.Method[0](i, nil);
    finally
    o.Free;
    end;
    finally
    ml.Free;
    end;
    end.
    [/pascal]

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •