12

自恐龙时代以来,Turbo Pascal 和现在的 Delphi 都有一个 Write() 和 WriteLn() 程序,它们可以安静地做一些简洁的事情。

  • 参数的数量是可变的;

  • 每个变量可以是各种类型;您可以提供整数、双精度、字符串、布尔值,并以任意顺序将它们混合在一起;

  • 您可以为每个参数提供附加参数:

写('你好':10,'世界!':7);//对齐参数

  • 它甚至以一种特殊的方式出现在代码完成下拉菜单中:
    • 写 ([var F:File]; P1; [...,PN] )
    • WriteLn ([var F:File]; [ P1; [...,PN]] )

现在我正在输入这个,我注意到 Write 和 WriteLn 在代码完成下拉列表中没有相同的括号。因此,看起来这不是自动生成的,而是有人硬编码的。

无论如何,我是否能够自己编写这样的程序,或者所有这些都是一些神奇的硬编码编译器诡计?

4

9 回答 9

26

Writeln 就是我们所说的编译器“魔术”函数。如果您在 System.pas 中查找,您将找不到一个被声明为您所期望的任何内容的 Writeln。编译器从字面上将其分解为对各种特殊运行时库函数的单独调用。

简而言之,在不修改编译器的情况下,没有办法实现您自己的版本,它与内置的 writeln 做所有相同的事情。

于 2009-03-06T04:40:03.870 回答
4

正如艾伦所说,您不能编写自己的函数来完成所有相同的事情。

但是,您可以编写一个执行自定义操作的文本文件驱动程序,并在使用标准 Write(ln) 写入您的文本文件驱动程序时。我们在过去的 DOS 时代就这样做了:)

(前面语句上下文中的“驱动程序”只是一段 Pascal 代码,它通过切换系统单元 IIRC 中的指针连接到系统中。自从我上次使用这个技巧以来已经很久了。)

于 2009-03-06T06:59:58.747 回答
3

据我所知,帕斯卡标准不包括可变参数。

话虽如此,IIRC,GNU Pascal 让我们这样说:Procecdure Foo(a: Integer; b: Integer; ...);

尝试在编译器的语言文档中搜索“变量参数列表”或“一致性数组”。这是后者的示例:http ://www.gnu-pascal.de/demos/conformantdemo.pas 。

正如上一张海报所说, writeln() 很神奇。我认为问题与堆栈如何在帕斯卡函数中组装有关,但是自从我考虑堆栈上的位置以来已经有很长时间了:)

但是,除非您正在编写“writeln”函数(已经编写好了),否则您可能不需要实现带有变量参数的过程。尝试迭代或递归:)

于 2009-03-06T04:43:39.337 回答
3

这是神奇的编译器行为,而不是常规过程。不,没有办法编写这样的子例程(不幸的是!)。代码生成解析实际参数及其类型的计数,并在编译时转换为适当的 RTL 调用(例如Str())。这反对经常建议的 const 数组(实际上是单个变量数组形式参数)导致在运行时执行相同的操作。我发现后来的方法很笨拙,它在一定程度上损害了代码的可读性,并且 Bugland(Borland/Inprise/Codegear/Embarcadero/name it)破坏了变体开放数组构造函数的 Code Insight(是的,我很在意,我使用 OutputDebugString(PChar(Format ('...', [...])))) 和代码完成在那里不能正常工作(或根本不能)。因此,模拟魔术行为的最接近的方法是声明许多重载子例程(实际上很多,每个特定形式参数类型在特定位置一个)。也可以称其为 kludge,但这是获得可变参数列表灵活性的唯一方法,并且可以隐藏在单独的模块中。

PS:我故意忽略了格式说明符,因为语法不允许分号在Str()Write()Writeln()接受它们的地方使用。

于 2010-01-08T02:10:22.727 回答
1

是的,您可以在 Delphi 和朋友(例如 free pascal、Kylix 等)中做到这一点,但不能在更“标准”的 pascal 中做到这一点。查找变体开放数组参数,这些参数与以下语法一起使用:

procedure MyProc(args : array of const);

(已经有几年了,我手头没有手册,所以在继续之前检查详细信息)。这为您提供了一个开放数组TVarData(或类似的东西),您可以从中提取 RTTI。

不过请注意:我认为您无法匹配 x:y 格式化语法(这是特殊的),并且可能必须使用稍微冗长的包装器。

于 2009-03-06T05:42:03.130 回答
1

大部分已经说了,但我想补充一些东西。

首先,您可以使用格式功能。将几乎任何类型的变量转换为字符串并控制其大小都很棒。虽然它有缺点:

myvar := 1;
while myvar<10000 do begin
  Memo.Lines.Add(Format('(%3d)', [myVar]));
  myvar := myvar * 10;
end;

产生:

(  1)
( 10)
(100)
(1000)

所以尺寸是最小尺寸(就像 :x:y 结构一样)。

要获得最少数量的变量参数,您可以使用默认参数和重载函数:

procedure WriteSome(const A1: string; const A2: string = ''; const A3: string = '');

或者

procedure WriteSome(const A1: string); overload;
procedure WriteSome(const A1: Integer); overload;
于 2009-03-06T08:02:51.760 回答
1

你不能用旧的 Pascal 编写自己的 write/writeln。它们是由编译器、格式化、对齐等生成的。这就是为什么一些程序员喜欢 C 语言,甚至是灵活的标准函数,例如 printf、scanf,都可以由任何有能力的程序员实现。

如果您倾向于创建比 C 供应商实现的更高性能的东西,您甚至可以为 C 创建相同的 printf 函数。它们中没有魔术,您的代码只需要“遍历”变量参数。

附言

但正如 MarkusQ 所指出的,Pascal 的一些变体(Free Pascal、Kylix 等)可以促进可变参数。自 DOS 时代以来,我最后一次修补 Pascal,Turbo Pascal 7。

于 2009-03-06T08:56:55.767 回答
1

Writeln 不是基于“const 数组”,而是由编译器分解为各种调用,这些调用将参数转换为字符串,然后调用原语 writestring。“LN”只是一个将 lineending 写入字符串的函数。(取决于操作系统)。原语的过程变量(函数指针)是文件类型(Textrec/filerec)的一部分,这就是它们可以定制的原因。(例如TP中的AssignCrt)

如果 {$I+} 模式打开,则在每个元素之后,都会调用 iocheck 函数。

上面制作的 GPC 构造是无限的 C 开放数组。FPC(和 afaik Delphi 也是)也支持这一点,但语法不同。

过程 somehting (a:array of const);cdecl;

将被转换为与 C、printf 样式兼容的 ABI。这意味着相关函数(在这种情况下为somehting)无法获取参数的数量,而必须依赖格式字符串解析。所以这与 const 数组不同,后者是安全的。

于 2009-05-02T12:32:04.600 回答
1

虽然不是直接回答您的问题,但我想添加以下评论:我最近使用 Writeln(...) 语法将一些代码重写为使用 StringList,用 Format(...) 填充“行”和只是普通的 IntToStr(...)、FloatToStr(...) 函数等。

这种变化的主要原因是速度的提高。使用 StringList 和 SaveFileTo 比 WriteLn、Write 组合快得多。

如果您正在编写一个创建大量文本文件的程序(我正在研究一个网站创建程序),这会产生很大的不同。

于 2010-01-09T10:18:46.233 回答