我希望使用一种交互式语言来测试一个遗留项目中的一些C代码。我懂一点Forth,但从未在实际项目中使用过。我现在正在研究pForth。
使用交互式Forth解释器来测试C程序中某个函数的行为是否合理?这个C代码有很多结构体、指向结构体的指针、句柄和其他常见的C结构。
我想我需要编写一些粘合代码来处理参数传递,也许还需要在Forth端进行一些结构分配。我想听听有经验的人的估计。这样做值得吗?
我希望使用一种交互式语言来测试一个遗留项目中的一些C代码。我懂一点Forth,但从未在实际项目中使用过。我现在正在研究pForth。
使用交互式Forth解释器来测试C程序中某个函数的行为是否合理?这个C代码有很多结构体、指向结构体的指针、句柄和其他常见的C结构。
我想我需要编写一些粘合代码来处理参数传递,也许还需要在Forth端进行一些结构分配。我想听听有经验的人的估计。这样做值得吗?
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.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 ;