Использование Ffidl с TCL для возврата строк и массивов, передаваемых по ссылке - PullRequest
1 голос
/ 08 апреля 2011

Я использую Ffidl для работы с некоторыми устройствами, которые используют драйверы FTDI, и по большей части все это работает довольно хорошо. Я совершенно уверен, что область, в которой я терплю неудачу, проистекает из моего непонимания того, как TCL обрабатывает указатели за кулисами. В любом случае, я получил примеры, касающиеся функций, которые передаются по ссылке на работу, выполняя что-то вроде этого:

::ffidl::callout rpbrd {pointer-var} int \
        [ffidl::symbol [ffidl::find-lib library] returns_pass_by_reference_double]
set dbl_ptr = [binary format [::ffidl::info format double] 1]
set my_int = [rpbrd dbl_ptr]
binary scan $dbl_ptr [::ffidl::info format double] my_dbl
puts $my_dbl

Однако две вещи, которые мне еще не удалось выяснить, - это возвращаемые строки "по ссылке" (char *), потому что они, кажется, всегда возвращают некоторые ненужные данные всякий раз, когда я пробую описанный выше подход, а также что-либо еще делать с массивами, потому что я даже не знаю, с чего начать, чтобы заставить их работать. Я почти уверен, что процесс, стоящий за этими двумя, будет похожим, поскольку один - это массив символов, а другой - просто массив ... ну, что-то еще.

Любая помощь в выяснении этого очень ценится. Я работал над этим в течение нескольких дней, и мой Google-фу не работает при поиске действительно хороших примеров, связанных с ffidl.

Редактировать: со времени моего поста мне с трудом удалось заставить работать строки, используя команду ::ffidl::pointer-into-string. Который в основном просто смотрит на память и копирует байты, пока не достигнет нулевой точки (я предполагаю). Это не то, что я считаю оптимальным методом, потому что кажется, что он не работает по ссылке, поскольку я не могу передать указатель по ссылке на определенное место в памяти и просто ожидать, что он будет работать без последствий. (Если мне неясно, в основном я буду выбирать случайный субадрес в моем текущем блоке памяти и говорить: «Хм, да. Кажется, это хорошее место, чтобы просто поместить некоторые данные». что действительно плохая идея.)

Я собираюсь продолжать пыхтеть, пытаясь выяснить, как заставить работать массивы. Начиная с копирования памяти и пытаясь поработать магией байтов с некоторыми binary scan s.

Ответы [ 2 ]

1 голос
/ 08 апреля 2011

Tcl действительно любит иметь возможность управлять временем жизни своих значений и использует модель неизменяемых значений. (Реальность более сложна, но запрограммируйте на модель, пожалуйста!) Даже если значение не изменится, вам все равно придется копировать, если вы не можете указать генератору данных использовать собственный распределитель памяти Tcl, Tcl_Alloc, хотя в случае, если вы готовы копировать, доступно несколько ярлыков (см., Например, Tcl_SetResult).

Это означает, что объект, переданный по ссылке, действительно должен отображаться в абстрактный объект (представление которого является произвольным именем, например handle-12345) и который имеет ряд операций над ним, которые возвращают информацию о представленном объекте. за ручку; одна такая операция для вашего случая будет возвращать копию подстроки, другая может возвращать общую длину. Единственная обязательная операция - обработка явного удаления дескриптора.

Я знаю, это звучит очень неуклюже. Это просто естественное следствие реального несоответствия импеданса.

0 голосов
/ 08 апреля 2011

После большой работы я наконец пришел к следующему [относительно анти-климатическому] выводу. Я подумал, что было бы полезно для других людей, ищущих этот ответ (если есть кто-нибудь), если бы я документировать свое решение. В любом случае, с самого начала, с некоторым кодом C, рассмотрим функцию

void make_int_array(int*& out) {
    int* a = new int[5];
    for (int i = 0; i < 5; i++) {
        a[i] = i+1;
    }
    out = a;
}

По сути, все это создает новый массив {1,2,3,4,5} и использует его для указателя на выход. Далее мы напишем оболочку Tcl:

::ffidl::callout _make_int_array {pointer-var} void \
                  [::ffidl::symbol ffidltest.dll make_int_array]

После этого вам нужно будет "приготовить" несколько интерфейсов, чтобы использовать функцию make_int_array. Вы можете сделать это так:

proc make_int_array {} {
    set ints 5
    set intsize [::ffidl::info sizeof int]
    set bytesize [expr $intsize * $ints]

    set ptr [binary format [::ffidl::info format int] 1]
    _make_int_arrayptr ptr
    binary scan $ptr i ptr

    binary scan [::ffidl::peek $ptr $bytesize] \
                [::ffidl::info format int]$ints out
    return $out
}

Может быть, есть более хороший способ сделать это, но это определенно сработало, и я рад за это. Однако, если у кого-то есть более хороший путь, я все еще открыт для идей.

...