作为交互式C程序测试工具的Forth

4

我希望使用一种交互式语言来测试一个遗留项目中的一些C代码。我懂一点Forth,但从未在实际项目中使用过。我现在正在研究pForth。

使用交互式Forth解释器来测试C程序中某个函数的行为是否合理?这个C代码有很多结构体、指向结构体的指针、句柄和其他常见的C结构。

我想我需要编写一些粘合代码来处理参数传递,也许还需要在Forth端进行一些结构分配。我想听听有经验的人的估计。这样做值得吗?


毫无疑问,用一种与C语言集成良好的语言编写C代码的测试工具会更有意义。让我们看看,我确定我在这里见过其中之一。哦,是的,它就叫做C语言 - paxdiablo
3
我想要一个互动式的东西,而且我没有底层操作系统。如果使用C语言,我就必须编写一个小的解释器,并且似乎比使用Forth或Lua更加麻烦。因此提出这个问题。 - ivarec
pForth似乎支持从内部调用C函数。我没有使用过pForth,所以我不知道这个功能有多强大。这是将要推广到更大的组织中的东西吗?如果是这样,您可能需要证明使用pForth比使用C更好的决定。如果它是适合情况的正确工具,我本人对使用Forth没有偏见。我只是非常清楚当我提到其他语言时,在C/C++环境中会发生什么。 :) - Dave Newman
1
这只是为了帮助我测试和理解庞大的遗留项目。我同意你的看法。我已经尝试过使用C语言编写pForth,感觉不错,但我想听听有经验的人对我可能会遇到哪些问题的看法。 - ivarec
1
我并不是Forth专家,但我对它很着迷,并且用它做了一些小型实用程序。如果pForth适合你并且你喜欢它,那么请放心使用,但我可能会选择Python。它也是交互式的,并且与C集成得很好。 - jcomeau_ictx
1
有更好的选择,但我想在非常有限的硬件上完成这个任务。不使用POSIX操作系统。Libffi可能无法工作。 - ivarec
1个回答

9
如果您想进行交互式测试并针对嵌入式平台进行操作,那么Forth肯定是一个好的选择。您将始终找到在目标平台上运行的Forth实现。即使需要编写一个,这也不难。
不要编写特定于您的立即需求的粘合代码,而是使用通用目的的Forth到C界面。我使用gforth的通用C接口,这非常容易使用。对于Forth中的结构处理,我使用MPE样式实现,当涉及到与C的接口时非常灵活(但要注意正确的对齐方式,参见gforth %align / %allot / nalign)。
通用目的结构处理单词的定义大约需要20行Forth代码,单链表处理或哈希表也是如此。
由于您无法使用gforth(仅限POSIX),因此为您选择的Forth编写扩展模块,实现类似的C接口。只需确保您的Forth和C接口模块使用与您想要测试的C代码相同的malloc()和free()即可。
有了这样的接口,您可以通过定义存根字(即将Forth单词映射到C函数和结构)在Forth中完成所有工作。
以下是一个示例测试会话,在该会话中,我使用gforth的C接口调用libc的 gettimeofday
s" structs.fs" included also structs \ load structure handling code

clear-libs
s" libc" add-lib  \ load libc.so. Not really needed for this particular library

c-library libc    \ stubs for C functions
\c #include <sys/time.h>
c-function gettimeofday gettimeofday a a -- n ( struct timeval *, struct timezone * -- int )
end-c-library

struct timeval          \ stub for struct timeval
    8 field: ->tv_sec   \ sizeof(time_t) == 8 bytes on my 64bits system
    8 field: ->tv_usec
end-struct

timeval buffer: tv

\ now call it (the 0 is for passing NULL for struct timezone *)
tv 0 gettimeofday .  \ Return value on the stack. output : 0
tv ->tv_sec @ .      \ output : 1369841953

请注意,tv -> tv_sec 实际上相当于 C 语言中的 (void *)&tv + offsetof(struct timeval, tv_sec),因此它给出了结构成员的地址,您需要使用 @ 获取该值。另一个问题是:由于我使用的是单元格大小为 8 字节的 64 位 Forth,因此存储/获取 8 字节的 long 很简单,但是获取/存储 4 字节的 int 将需要一些特殊处理。不管怎样,Forth 可以轻松处理这个问题:只需为此定义专用的 int@int! 词。

正如您所看到的,有了良好的通用 C 接口,您就不需要编写任何 C 中的粘合代码,只需要编写 Forth 子程序来调用您的 C 函数和结构即可,这非常简单(大部分可以从您的 C 标头自动生成)。

一旦您对交互测试感到满意,就可以转向自动化测试:

  • 将整个交互测试会话的输入/输出复制/粘贴到名为 testXYZ.log 的文件中
  • 从会话日志中取出输出(仅保留输入),并将其写入名为 testXYZ.fs 的文件中
  • 运行测试,将 testXYZ.fs 管道到您的 Forth 解释器中,捕获输出并将其与 testXYZ.log 进行比较。

由于从交互式会话日志中移除输出可能有些繁琐,因此您还可以先编写测试脚本 testXYZ.fs,然后运行它并捕获输出 testXYZ.log,但我更喜欢从交互式会话日志开始。

Et voilà!

供参考,这是我在上面的示例中使用的结构处理代码:

\ *****************************************************************************
\ structures handling
\ *****************************************************************************

\ Simple structure definition words. Structure instances are zero initialized.
\
\ usage :
\ struct foo
\     int: ->refCount
\     int: ->value
\ end-struct
\ struct bar
\            int: ->id
\     foo struct: ->foo
\       16 chars: ->name
\ end-struct
\
\ bar buffer: myBar
\ foo buffer: myFoo
\ 42 myBar ->id !
\ myFoo myBar ->foo !
\ myBar ->name count type
\ 1 myBar ->foo @ ->refCount +! \ accessing members of members could use a helper word

: struct ( "name" -- addr 0 ; named structure header )
    create here 0 , 0
  does>
    @ ;

\ <field-size> FIELD <field-name>
\ Given a field size on the stack, compiles a word <field-name> that adds the
\ field size to the number on the stack.

: field: ( u1 u2 "name" -- u1+u2 ; u -- u+u2 )
    over >r \ save current struct size
    : r> ?dup if
    postpone literal postpone +
    then
    postpone ;
    + \ add field size to struct size
; immediate

: end-struct ( addr u -- ; end of structure definition )
    swap ! ;

: naligned ( addr1 u -- addr2 ; aligns addr1 to alignment u )
    1- tuck + swap invert and ;

\ Typed field helpers
: int: cell naligned cell postpone field: ; immediate
: struct: >r cell naligned r> postpone field: ; immediate
: chars: >r cell naligned r> postpone field: ; immediate
\ with C style alignment
4 constant C_INT_ALIGN
8 constant C_PTR_ALIGN
4 constant C_INT_SIZE
: cint: C_INT_ALIGN naligned C_INT_SIZE postpone field: ; immediate
: cstruct: >r C_PTR_ALIGN naligned r> postpone field: ; immediate
: cchars: >r C_INT_ALIGN naligned r> postpone field: ; immediate

: buffer: ( u -- ; creates a zero-ed buffer of size u )
    create here over erase allot ;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接