Why so many pointers? - delphi

Searching for references of Win32 LDAP API functions, I found the following JwaWinLDAP.pas unit.
On this unit, to function ldap_search_st is declared:
function ldap_search_st(ld: PLDAP; base: PAnsiChar; scope: ULONG;
filter, attrs: PAnsiChar; attrsonly: ULONG; var timeout: TLDAPTimeVal;
var res: PLDAPMessage): ULONG; cdecl;
The timeout: TLDAPTimeVal parameter is declared as:
PLDAPTimeVal = ^TLDAPTimeVal;
l_timeval = packed record
tv_sec: Longint;
tv_usec: Longint;
end;
LDAP_TIMEVAL = l_timeval;
PLDAP_TIMEVAL = ^LDAP_TIMEVAL;
TLDAPTimeVal = l_timeval;
On the code, if I use something like:
procedure foo;
var
TimeVal: PLDAPTimeVal;
begin
ldap_search_st(foo1, Nil, 0, PAnsiChar('(objectClass=*)'), Nil, 0, TimeVal, foo2);
end;
Compiler gives me error:
[dcc32 Error] Types of actual and formal var parameters must be
identical
because of the timeout parameter. If I change TimeVal type to TLDAPTimeVal it compiles and the application works.
The question is:
when I see declaration of types in Delphi, they are always like:
type
PType1 = ^Type1
Type1 = record...
In the specific example cited, it could be:
l_timeval = packed record
tv_sec: Longint;
tv_usec: Longint;
end;
TLDAPTimeVal = l_timeval;
and it would work the exact same way (I think)... Why so much confusion on this kind of declaration?

type
PType1 = ^Type1
Type1 = record...
Above type declaration declares two types - one is record, and another one is typed pointer for that specific record type. They are usually declared in pairs, because they are related. But if your or any code does not need typed pointer, it does not need to declare pointer type.
Function ldap_search_st uses only declared record type. But some other functions in that unit expect pointer as parameter. That is why declaration has both.
Code in question is LDAP Windows API header translation for Delphi. Windows API uses pointers for passing structures to functions.
API translations are usually complex and sometimes have seemingly superfluous declarations. For completeness, translations usually contain all original declarations (symbols) - those are l_timeval, LDAP_TIMEVAL and PLDAP_TIMEVAL, and while those would be sufficient to use the API, there are two additional declarations whose only purpose is providing Delphi style names for more user friendly experience PLDAPTimeVal and TLDAPTimeVal
If you take a look at original LDAP function declarations they all use pointers for passing structures. For instance:
ULONG ldap_search_st(
_In_ LDAP *ld,
_In_ PCHAR base,
_In_ ULONG scope,
_In_ PCHAR filter,
_In_ PCHAR attrs[],
_In_ ULONG attrsonly,
_In_ struct l_timeval *timeout,
_Out_ LDAPMessage **res
);
and
ULONG ldap_connect(
_In_ LDAP *ld,
_In_ LDAP_TIMEVAL *timeout
);
There is one difference in those two considering timeout parameter.
ldap_search_st expects non-null value in timeout parameter - and Delphi translation of that parameter is var timeout: TLDAPTimeVal to match that intent more clearly - that declaration prevents you from accidentally passing null. While TLDAPTimeVal is not a pointer type, having var keyword makes our timeout parameter to behave like one. Behind the scenes Delphi will pass pointer to the structure and that will perfectly match original function declaration.
On the other hand in ldap_connect timeout can contain null value. In that case default timeout value will be used. The only way to satisfy that requirement is to use pointer type to timeout structure. In another words PLDAPTimeVal and Delphi translation of that function declaration is
function ldap_connect(ld: PLDAP; timeout: PLDAPTimeval): ULONG;

It is the matter of coding standards and conventions and there is so many of them because LDAP declarations were adopted into PSDK and then translated to Delphi. Additionally, since Pascal doesn't allow pointer type declaration (eg ^Integer) within formal parameters unlike plain C (eg int *), corresponding pointer types were added to declaration.
Here, I marked various conventions in the declaration, note the difference in casing and prefixes:
PLDAPTimeVal = ^TLDAPTimeVal; // Delphi pointer (Econos convention)
l_timeval = packed record // canonic structure (LDAP convention)
tv_sec: Longint;
tv_usec: Longint;
end;
LDAP_TIMEVAL = l_timeval; // Windows structure (PSDK convention)
PLDAP_TIMEVAL = ^LDAP_TIMEVAL; // Windows pointer (PSDK convention)
TLDAPTimeVal = l_timeval; // Delphi structure (Econos convention)
Curious thing: the forward declaration (before structure) of Delphi pointer is also mandated by Econos convention. Original PSDK code declares the pointer after the structure.

Related

Is the [Ref] attribute for const record parameters useful?

With the latest Delphi version (Berlin/10.1/24), is the [Ref] attribute really necessary?
I ask this because the online doc says:
Constant parameters may be passed to the function by value or by
reference, depending on the specific compiler used. To force the
compiler to pass a constant parameter by reference, you can use the
[Ref] decorator with the const keyword.
It's pretty much as described by the documentation. You'd use [ref] if you had a reason to enforce the argument to be passed by reference. One example that I can think of is for interop. Imagine you are calling an API function that is defined like this:
typedef struct {
int foo;
} INFO;
int DoStuff(const INFO *lpInfo);
In Pascal you might wish to import it like this:
type
TInfo = record
foo: Integer;
end;
function DoStuff(const Info: TInfo): Integer; cdecl; external libname;
But because TInfo is small, the compiler might choose to pass the structure by value. So you can annotate with [ref] to force the compiler to pass the parameter as a reference.
function DoStuff(const [ref] Info: TInfo): Integer; cdecl; external libname;
Another example is the new FreeAndNil procedure declaration in Delphi 10.4 in SysUtils.pas, which now finally ensures that only TObject descendants can be used with FreeAndNil. In previous Delphi versions, you could pass anything to this function, even if it didn't make sense.

Delphi "default" keyword with Record types in older Delphi versions

I have this code in Delphi Detours library which I'm trying to port:
type
TInstruction = record
Archi: Byte; { CPUX32 or CPUX64 ! }
AddrMode: Byte; { Address Mode }
Addr: PByte;
VirtualAddr: PByte;
NextInst: PByte; { Pointer to the Next Instruction }
OpCode: Byte; { OpCode Value }
OpType: Byte;
OpKind: Byte;
OpTable: Byte; { tbOneByte,tbTwoByte,... }
OperandFlags: Byte;
Prefixes: Word; { Sets of Prf_xxx }
...
end;
var
Inst: TInstruction;
begin
...
Inst := default (TInstruction); // <-
Inst.Archi := CPUX;
Pvt := PPointer(AIntf)^; // vTable !
PCode := PPointer(Pvt + Offset)^; // Code Entry !
Inst.NextInst := PCode;
...
end;
What does the "default" keyword do?
I assume something like:
FillChar(Inst, SizeOf(TInstruction), 0);
Is my assumption correct?
Default() is an undocumented intrinsic function introduced to support generics. The design of Delphi generics was heavily inspired by .net generics and you might benefit from reading the analagous documentation for .net: https://msdn.microsoft.com/en-GB/library/xwth0h0d.aspx
The purpose of Default() is to allow you to default initialize a variable. When working with generic types Default() allows you to do so for a variable whose type is generic.
If you wish to replicate the behaviour of Default() do the following:
Finalize(Inst);
FillChar(Inst, SizeOf(Inst), 0);
The call to Finalize is needed in case the type is managed. That is if the type is managed, or contains any members that are managed. Managed types include strings, dynamic arrays, interfaces, variants, anonymous methods etc.
If the type does not contain managed types then the call to Finalize may be omitted. It doesn't hurt to include it though, because the compiler will eliminate it if not needed. If you can be 100% certain that no managed types have been assigned a value then you could also omit that call.
Default initialization means the following:
Zero for numeric types.
The value with ordinal zero for enumerated types.
False for boolean types.
#0 for character types.
The empty string for strings.
The empty variant for Variant.
nil for classes, dynamic arrays, interfaces and anonymous methods.

Pointer typecasting in Delphi

How can I correctly typecast to a structure in Delphi? This does not work exactly like in C++ where one would just pass a &Data according to the MSDN documentation.
program Project1;
uses
System.SysUtils,
Winapi.Windows,
Winapi.Winsock2;
function WSAStartup(wVersionRequired: WORD; out lpWSAData: LPWSADATA): Integer; WINAPI; external 'ws2_32.dll';
var
Data: WSADATA;
begin
WSAStartup(WINSOCK_VERSION, LPWSADATA(#Data)); // E2197 Constant object cannot be passed as var parameter
ReadLn;
end.
I guess that you've translated the function from the MSDN documentation which reads:
int WSAStartup(
_In_ WORD wVersionRequested,
_Out_ LPWSADATA lpWSAData
);
The confusion stems from the use of the _Out_ annotation. That is a macro that expands to nothing. It is used to convey intent to tools that, for instance, convert the header file declaration to different languages. More information can be found here:
What is the meaning of _In_ and _Out_ in documentation of any function?
What is the purpose of __in __out __in_opt __allowed(), how do they work? Should I use similar constructs in my own code?
You've erroneously translated _Out_ to the Delphi out keyword. You could simply remove that keyword and your declaration would be correct:
function WSAStartup(wVersionRequired: WORD; lpWSAData: LPWSADATA): Integer;
WINAPI; external 'ws2_32.dll';
Then your call would be:
WSAStartup(WINSOCK_VERSION, #Data);
Alternatively, since this parameter is not optional, you could translate it like this:
function WSAStartup(wVersionRequired: WORD; out lpWSAData: WSADATA): Integer;
WINAPI; external 'ws2_32.dll';
You would then call like this:
WSAStartup(WINSOCK_VERSION, Data);
You should however, use the declaration of the function that can be found in Winapi.Winsock2 and so avoid risking making such mistakes. That is, assuming that Embarcadero have not made mistakes in translation, which does sometimes happen.
Finally, it would be remiss of me were I not to chide you, at least mildly, for ignoring the return value of the function call.

What is exact signature of System.Assign (for use in procedural expression)?

Unfortunately, exact Assign signature is not available in the RTL source, and my attempts to guess, like:
const
Assign: procedure (var F; const FileName: string) = System.Assign;
{ or }
Assign: function (var F; const FileName: string): Integer = System.Assign;
{ also I tried "internal" one from _AssignFile }
didn't yield any positive results, and compiler refuses to treat this contant expression as procedural and complains about right-value (E2029 '(' expected but ';' found)).
So, which type should I use to match Delphi RTL exactly?
Assign is a language remnant of Delphi tracing its origins to the original Turbo Pascal syntax. Long before function overloading was added to the language syntax, long before the RTL's text file and "file of type" internal data structures were documented, there was Assign.
Assign is a simple enough procedure to use, but when you look at what it has to do you'll see that it's pretty much impossible to implement without some sort of compiler magic. File of TFoo creates a file type that is distinct and incompatible with File of Integer. And yet, both can be passed as the first parameter to Assign.
Today, you could probably implement Assign using a generic type param for the file parameter. The generic type would conform to the type of the variable passed in. That's great and all, but we needed a way to associate a typed file variable with a string filename 25 years before generics were added to the mix.
Assign is a compiler intrinsic function. That means it lives outside of the Delphi/Pascal syntax space. Assign is essentially a type conforming procedure, which can't be represented in the strongly typed Delphi/Pascal language (without modern and complex language extensions such as generic types). It's not typeless, and it's not dynamically typed because the actual parameter type is fully determined at compile time.
You can't take the address of the Assign function. Even if you could, because it is amorphous, you won't be able to define a procedure type that accurately represents all of its possible call signatures.
Compiler magic functions exist outside the language to take care of tasks that could not be easily represented in the syntax available at the time. Assign is one example. Writeln() is another.
System.Assign is an intrinsic function. The official documentation of it is completely hopeless. It says:
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
I can only guess as to why the documentation generator cannot cope with this function. But the three identical overloads are clearly bogus. And it's not a function, rather it is a procedure.
No matter. Because it is an intrinsic, you cannot assign it to a function pointer. The solution is to wrap it up in a function of your own.
procedure MyAssign(var F: File; const FileName: string);
begin
Result := System.Assign(F, FileName);
end;
You can then assign that function to a variable or constant of procedural type.
const
AssignProc: procedure(var F: File; const FileName: string) = MyAssign;
The other variant of Assign takes a third parameter that specifies the code page, passed as a Word. You can only call that function if the first argument to Assign is a TextFile.
So, intrinsics are really a law unto themselves.
Note that the documentation does state that Assign should no longer be used. Instead you should use AssignFile. The documentation is no better there mind you!
If to open System.pas file and read it, in XE2 you have those internal procedures:
function _AssignFile(var t: TFileRec; const s: PChar): Integer;
function _AssignText(var t: TTextRec; const s: PChar; const CP: word): Integer;
And i don't think you have any global stable procedure w/o underscore in names - those listed are Delphi compiler internals, like invisible procedures, that are covertly called when you declare string or dynarray variable.
http://en.wikipedia.org/wiki/Intrinsic_function
Like a lot of procedures like _Write0LString which are implementing usual WriteLn calls.
So you would have to make a wrapper function that would call AssignFile and put reference to your wrapper into your variable instead;
However you did not put tag with your Delphi version, and different vesions may have different declarations for intrinsics. So look into sources of your particular Delphi.
Also, why would you need it ? I remember i did such a thing in TurboPascal 5.5 to make string-based file - make WriteLn act like C sprintf. But why would you need it in modern Delphi, when you can use TStream instead?

Untyped / typeless parameters in Delphi

What type are parameters without type like in the class TStringStream:
function Read(var Buffer; Count: Longint): Longint; override;
What is the type of Buffer parameter (is it a type of Pointer ?).
I wrote an article about this very topic a few years ago:
What is an untyped parameter?
Untyped parameters are used in a few situations; the TStream.Read method you ask about most closely matches with the Move procedure I wrote about. Here's an excerpt:
procedure Move(const Source; var Dest; Count: Integer);
The Move procedure copies data from an arbitrary variable
into any other variable. It needs to accept sources and destinations of
all types, which means it cannot require any single type. The procedure
does not modify the value of the variable passed for Source, so that
parameter’s declaration uses const instead of var, which is the
more common modifier for untyped parameters.
In the case of TStream.Read, the source is the stream's contents, so you don't pass that in as a parameter, but the destination is the Buffer parameter shown in the question. You can pass any variable type you want for that parameter, but that means you need to be careful. It's your job, not the compiler's, to ensure that the contents of the stream really are a valid value for the type of parameter you provide.
Read the rest of my article for more situations where Delphi uses untyped parameters.
Check out the Delphi help for "Untyped parameters"
You can pass in any type, but you have to cast it in the implementation. Help says that you cannot pass it a numeral or untyped numeric constant. So basically you have to be know what type to expect, and compiler can not help you, so you need a good reason to do it this way. I suppose it could be of use if you need the method to handle incompatible types, but then again you could write several overloaded versions for each expected type, I would suggest that as a better solution.
Perhaps surprisingly, it is legal to pass a dereferenced pointer as an untyped parameter. And the pointer itself doesn't even have to have a type.
procedure SomeMethod(var aParameter);
∶
procedure CallSomeMethod(aIsInteger : Boolean);
type
buffer : Pointer;
intValue : Integer;
realValue : Single;
begin
if aIsInteger then
begin
buffer := #intValue;
end
else
begin
buffer := #realValue;
end;
SomeMethod(buffer^);
Of course it would probably have been easier if the parameter to SomeMethod() had been a pointer, but this might not be under your control.
var in a parameter list is the Delphi syntax for call by reference. It can be typed as e.g. the AllowChange parameter in the OnChanging handler of an Listview:
procedure TSomeForm.LVOnChanging(Sender: TObject; ...; var AllowChange: Boolean);
begin
if SomeProblemOccurred then
AllowChange := False;
end;
or untyped as in your example.

Resources