2
votes

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?

2

2 Answers

7
votes
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;
0
votes

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.