4

我愿意使用交互式语言来测试遗留项目中的一些 C 代码。我知道一点 Forth,但我从未在现实世界的项目中使用过它。我现在正在看 pForth。

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

我想我必须编写一些胶水代码来处理参数传递,也许还有一些 Forth 端的结构分配。我想从在这个领域有经验的人那里得到一个估计。这值得么?

4

1 回答 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 中通过定义存根字(即将 Forth 字映射到 C 函数和结构)来完成所有操作。

这是一个示例测试会话,我gettimeofday使用 gforth 的 C 接口调用 libc。

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实际上相当于(void *)&tv + offsetof(struct timeval, tv_sec)C 中的 ,因此它为您提供了结构成员的地址,因此您必须使用@. 这里的另一个问题:由于我使用 64 位 Forth,其中单元格大小为 8 字节,因此存储/获取 8 字节很简单,但获取/存储 4 字节int将需要一些特殊处理。无论如何,Forth 使这变得简单:只需为此定义特殊目的int@int!词语。

如您所见,使用良好的通用 C 接口,您无需在 C 中编写任何胶水代码,只需要用于您的 C 函数和结构的 Forth 存根,但这真的很简单(而且大部分可以自动从您的 C 标头生成)。

一旦您对交互式测试感到满意,您就可以继续进行自动化测试:

  • 将交互式测试会话中的整个输入/输出复制/粘贴到名为 testXYZ.log 的文件中
  • 从会话日志中剥离输出(仅保留输入)并将其写入名为 testXYZ.fs 的文件
  • 要运行测试,请将 testXYZ.fs 通过管道传输到您的第四个解释器,捕获输出并使用 testXYZ.log 对其进行比较。

由于从交互式会话日志中删除输出可能有点乏味,您也可以从编写测试脚本 testXYZ.fs 开始,然后运行它并捕获输出 testXYZ.log,但我更喜欢从交互式会话日志开始。

等等瞧!

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

\ *****************************************************************************
\ 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 ;
于 2013-05-29T16:53:57.390 回答