为什么这么多指针?

时间:2017-06-30 18:22:46

标签: delphi

搜索Win32 LDAP API函数的引用,我找到了以下JwaWinLDAP.pas单元。

在此单元上,声明了函数ldap_search_st

function ldap_search_st(ld: PLDAP; base: PAnsiChar; scope: ULONG;
  filter, attrs: PAnsiChar; attrsonly:  ULONG; var timeout: TLDAPTimeVal;
  var res: PLDAPMessage): ULONG; cdecl;

超时: TLDAPTimeVal 参数声明为:

  PLDAPTimeVal = ^TLDAPTimeVal;
  l_timeval = packed record
    tv_sec: Longint;
    tv_usec: Longint;
  end;
  LDAP_TIMEVAL = l_timeval;
  PLDAP_TIMEVAL = ^LDAP_TIMEVAL;
  TLDAPTimeVal = l_timeval;

在代码中,如果我使用类似的东西:

procedure foo;
var
  TimeVal: PLDAPTimeVal;
begin
 ldap_search_st(foo1, Nil, 0, PAnsiChar('(objectClass=*)'), Nil, 0, TimeVal, foo2);
end;

编译器给我错误:

  

[dcc32错误]必须是实际和正式var参数的类型   相同

因为超时参数。如果我将TimeVal类型更改为 TLDAPTimeVal ,则会编译并且应用程序正常工作。

问题是: 当我在Delphi中看到类型声明时,它们总是如下:

type
 PType1 = ^Type1
 Type1 = record...

在引用的具体例子中,它可能是:

  l_timeval = packed record
    tv_sec: Longint;
    tv_usec: Longint;
  end;
  TLDAPTimeVal = l_timeval;

它会以完全相同的方式工作(我认为)......为什么会对这种声明产生如此多的混淆?

2 个答案:

答案 0 :(得分:7)

type
 PType1 = ^Type1
 Type1 = record...

上面的类型声明声明了两种类型 - 一种是记录,另一种是特定记录类型的类型指针。它们通常成对声明,因为它们是相关的。但是如果您或任何代码不需要类型指针,则不需要声明指针类型。

函数ldap_search_st仅使用声明的记录类型。但是该单元中的一些其他函数期望指针作为参数。这就是声明同时具有的原因。

有问题的代码是Delphi的LDAP Windows API头文件翻译。 Windows API使用指针将结构传递给函数。

API翻译通常很复杂,有时候看似多余的声明。为了完整起见,翻译通常包含所有原始声明(符号) - 那些是l_timevalLDAP_TIMEVALPLDAP_TIMEVAL,虽然这些声明足以使用API​​,但还有两个声明,唯一目的是提供Delphi样式名称,以获得更加用户友好的体验PLDAPTimeValTLDAPTimeVal

如果您查看原始LDAP函数声明,它们都使用指针来传递结构。例如:

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
); 

ULONG ldap_connect(
  _In_ LDAP         *ld,
  _In_ LDAP_TIMEVAL *timeout
);

考虑到timeout参数,这两者之间存在一个差异。

ldap_search_st期望timeout参数中的非空值 - 并且该参数的Delphi转换为var timeout: TLDAPTimeVal以更清楚地匹配该意图 - 该声明可防止您意外传递null。虽然TLDAPTimeVal不是指针类型,但使用var关键字会使我们的timeout参数表现得像一个。在幕后,Delphi会将指针传递给结构,并且与原始函数声明完全匹配。

另一方面,ldap_connect timeout可以包含空值。在这种情况下,将使用默认超时值。满足该要求的唯一方法是使用指针类型来超时结构。换句话说,PLDAPTimeVal和该函数声明的Delphi翻译是

function ldap_connect(ld: PLDAP; timeout: PLDAPTimeval): ULONG;

答案 1 :(得分:0)

编码标准和约定的问题很多,因为LDAP声明被PSDK采用然后翻译成Delphi。另外,由于Pascal不允许在形式参数内使用指针类型声明(例如^Integer)而不像普通C(例如int *),因此相应的指针类型被添加到声明中。

在这里,我在声明中标记了各种约定,注意了套管和前缀的区别:

  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)

奇怪的是:Econos convention也强制要求Delphi指针的前向声明( 结构之前)。原始PSDK代码在结构之后声明指针