When you assign a “specialized” pointer, say a PInteger, to a generic Pointer variable, the assignment is valid because the compiler knows that PInteger is a subtype of Pointer. There is however no general way of determining a subtype relation between procedural types, even if some of their parameters are known to be related. Even in unary cases, T1 ≺ T2 does not imply that a PROCEDURE (T1) can be safely assigned to a PROCEDURE (T2)–typed variable, nor vice versa; for example, given PMyRecord ≺ Pointer, you probably wouldn’t want
◦ @FreeMem() assigned to VAR finalizeMyRecord: PROCEDURE (rec: PMyRecord); or ◦ something like @InitMyRecord() assigned to VAR: reserve100GB: PROCEDURE (ptr: Pointer). Using a generic callback procedure as in your scenario does potentially permit such unwanted operations. I do vaguely remember a UNIV modifier that’s available in the Macintosh Pascal mode, but I couldn’t find any documentation about it (nor do I know if it can be enabled in other modes) and I’m not sure what it exactly does. Although it does seem to eliminate the need for manual typecasts, type-safety is generally not guaranteed. Your last proposal is not completely safe, because you can still use non-PMyData pointers with RegisterCallback(). So to answer your question, my suggestion would be to define a custom callback type with a custom RegisterCallback() for each of your custom data structures. Hope this helps. On April 26, at 02:39, Bernd wrote: > 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 -- Best Regards, JC Chu _______________________________________________ fpc-pascal maillist - fpc-pascal@lists.freepascal.org http://lists.freepascal.org/mailman/listinfo/fpc-pascal