Далее как интерактивный тестер программы на С
Я готов использовать интерактивный язык, чтобы протестировать некоторый код C из унаследованного проекта. Я немного знаю Forth, но я никогда не использовал это в реальном проекте. Я смотрю на pForth прямо сейчас.
Разумно ли использовать интерактивный интерпретатор Forth для проверки поведения некоторой функции в C-программе? Этот код C имеет множество структур, указателей на структуры, дескрипторы и другие общие структуры, найденные в C.
Я предполагаю, что мне придется написать некоторый связующий код для обработки передачи параметров и, возможно, некоторого распределения структуры на стороне Forth. Я хочу оценку от кого-то с опытом в этой области. Стоит ли оно того?
1 ответ
Если вы хотите интерактивное тестирование и нацелены на встраиваемые платформы, то 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).
Вот пример тестового сеанса, где я вызываю libc gettimeofday
используя интерфейс С 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-битный Forth, где размер ячейки составляет 8 байтов, сохранение / выборка длиной 8 байтов не вызывает затруднений, но выборка / сохранение 4-байтового int потребует некоторой специальной обработки. Во всяком случае, Forth делает это легко: просто определите специальное назначение 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 ;