Hi, While translating some headers I have run across the following problem (I am sure had this in different variations many times before already and always lived with it and ignored it by casting from untyped pointers). Consider this situation:
unit Unit1; {$mode objfpc}{$H+} interface type PData = Pointer; PCallback = procedure(Data: PData); cdecl; procedure RegisterCallback(Proc: PCallback; Data: PData); implementation procedure RegisterCallback(Proc: PCallback; Data: PData); begin //just call the callback now for demonstration purpose Proc(Data); end; end. The above unit might be an API for some C library or some other piece of code that will allow me to register a callback (a timer or something similar) and pass additional data via a pointer to my callback function. The following unit will use this API: program project1; {$mode objfpc}{$H+}{$T+} uses Unit1; type PMyData = ^TMyData; TMyData = record Foo: Integer; Bar: Integer; end; procedure MyUglyCallback(D: Pointer); cdecl; begin // ugly typecasting everytime I access my data writeln(PMyData(D)^.Foo); Dispose(PMyData(D)); end; procedure MyNiceCallback(var D: TMyData); cdecl; begin // this function looks much nicer but although it is binary // identical its signature is deemed incompatible by FPC writeln(D.Foo); Dispose(@D); end; procedure Run; var D : PMyData; begin New(D); D^.Foo := 42; RegisterCallback(@MyUglyCallback, D); New(D); D^.Foo := 23; // unsafe typecasting, would accept *any* procedure RegisterCallback(PCallback(@MyNiceCallback), D); end; begin Run; end. None of the two possibilities feels right, the first one is what I have always used (out of desperation) because it will at least enforce that I do not accidentally forget the cdecl in my callback function but the casting of the Pointer inside the function looks just ugly to me and simply does not feel right, it is against the spirit of the language and it is still unsafe. The second option will almost guarantee that in one of my (many) callback functions I will sooner or later accidentally forget the cdecl because It will allow me to pass *anything* to RegisterCallback, this one feels even more type-unsafe to me. Now I have come up with the following: program project1; {$mode objfpc}{$H+}{$T+} uses Unit1; type PMyData = ^TMyData; TMyData = record Foo: Integer; Bar: Integer; end; PMyCallback = procedure(var Data: TMyData); cdecl; function CastMyCallback(Proc: PMyCallback): PCallback; inline; begin Result := PCallback(Proc); end; procedure MyNiceCallback(var D: TMyData); cdecl; begin // this function looks much nicer but although it is binary // identical its signature is deemed incompatible by FPC writeln(D.Foo); Dispose(@D); end; procedure Run; var D : PMyData; begin New(D); D^.Foo := 5; // this is the only way I have found to let me have my // own types and still retain total type-safety RegisterCallback(CastMyCallback(@MyNiceCallback), D); end; begin Run; end. This has an overhead of only 4 code lines (the CastMyCallback function) and only once for all my different callback functions that use the same data structure and it would completely retain strong type safety. What do you think, is this a good idea or would there have been an even more elegant way to achieve the same? Bernd _______________________________________________ fpc-pascal maillist - fpc-pascal@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-pascal