Если вы хотите интерактивное тестирование и ориентируетесь на встраиваемые платформы, то Forth, безусловно, является хорошим кандидатом. Вы всегда найдете реализацию Forth, которая работает на вашей целевой платформе. Написать один даже не сложно, если это необходимо.
Вместо того, чтобы писать связующий код, специфичный для ваших непосредственных потребностей, используйте универсальный интерфейс Forth to C. Я использую общий C-интерфейс gforth, который очень прост в использовании. Для обработки структуры в Forth я использую реализация в стиле MPE, которая очень гибкая, когда дело доходит до взаимодействия с C (см. Однако для правильного выравнивания см. gforth %align / %allot / nalign).
Определение слов обработки структуры общего назначения занимает около 20 строк кода Forth, то же самое для обработки одиночных связанных списков или хеш-таблиц.
Поскольку вы не можете использовать gforth (только POSIX), напишите модуль расширения для выбранного вами Forth, который реализует аналогичный интерфейс C. Просто убедитесь, что ваш Forth и модуль интерфейса C используют те же функции malloc() и free(), что и код C, который вы хотите протестировать.
С таким интерфейсом вы можете делать все в Forth, просто определяя слова-заглушки (т. е. сопоставлять слова Forth с функциями и структурами C).
Вот пример тестового сеанса, в котором я вызываю gettimeofday
libc, используя интерфейс C gforth.
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-битный Форт, где размер ячейки равен 8 байтам, сохранение/выборка 8-байтового long является простым, но выборка/сохранение 4-байтового int требует специальной обработки. Во всяком случае, Форт упрощает эту задачу: просто определите для этого специальные слова int@
и int!
.
Как вы можете видеть, с хорошим универсальным интерфейсом C вам не нужно писать какой-либо связующий код на C, нужны только заглушки Forth для ваших функций и структур C, но это действительно просто (и большая часть этого может быть автоматически сгенерированный из ваших заголовков 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 ;
person
wldsvc
schedule
29.05.2013