#pointers #dll #tcl
#указатели #dll #tcl
Вопрос:
Я использовал 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-fu терпит неудачу при поиске каких-либо действительно хороших примеров с использованием ffidl.
Редактировать: со времени написания моего поста мне с некоторым трудом удалось заставить строки работать, используя ::ffidl::pointer-into-string
команду. Который в основном просто просматривает память и копирует байты, пока не достигнет нулевой точки (я предполагаю). Это не то, что я считаю оптимальным методом, потому что это, похоже, не работает для по ссылке, поскольку я не могу передать указатель по ссылке на определенное местоположение в памяти и просто ожидать, что это сработает без последствий. (Если я не совсем ясно выражаюсь, в основном то, что я бы сделал, эквивалентно выбору случайного подадреса в моем текущем блоке памяти и произнесению «Хм, да. Это кажется хорошим местом, чтобы просто поместить некоторые данные «. что действительно является плохой идеей.)
Я собираюсь продолжать пыхтеть, пытаясь выяснить, как заставить массивы работать. Начинаем с копирования памяти и пытаемся сотворить волшебство с некоторыми binary scan
байтами.
Ответ №1:
Tcl действительно хочет иметь возможность самостоятельно управлять временем жизни своих значений, и он использует модель неизменяемых значений. (Реальность сложнее, но запрограммируйте модель, пожалуйста!) Даже если бы значение не менялось, вам все равно пришлось бы копировать, если только вы не могли бы указать генератору данных использовать собственный распределитель памяти Tcl, Tcl_Alloc
хотя существует ряд ярлыков, доступных в случае, когда вы с удовольствием копируете (см. Tcl_SetResult
например).
Это означает, что объект, передаваемый по ссылке, действительно должен быть сопоставлен абстрактному объекту (представлением которого является произвольное имя, например handle-12345
) и который имеет ряд операций над ним, которые возвращают информацию об объекте, представленном дескриптором; одной из таких операций в вашем случае было бы вернуть копию подстроки, другой может быть возврат общей длины. Единственной обязательной операцией является обработка явного удаления дескриптора.
Я знаю, что все это звучит очень неуклюже. Это просто естественное следствие несоответствия реального импеданса.
Комментарии:
1. Хотя это полезно, помогая мне понять, как Tcl работает «за кулисами», я пока не уверен, как я могу использовать это для интерпретации указателя Ffidl на ячейку памяти в массив, скажем, целых чисел. Не сказать, что это не обязательно отвечает на этот вопрос, просто я пока не знаю, как его применить.
2. Я не совсем уверен, действительно ли моя реализация использовала то, о чем вы конкретно говорили, но я думаю, что это помогло мне в некоторой степени сформулировать мое решение. И за это я благодарю вас.
Ответ №2:
После большой работы я, наконец, пришел к следующему [относительно антиклимактическому] выводу. Я подумал, что другим людям, ищущим этот ответ (если таковой вообще есть), было бы полезно, если бы я задокументировал свое решение. В любом случае, с самого начала, с некоторым кодом C, рассмотрим функцию
void make_int_array(int*amp; out) {
int* a = new int[5];
for (int i = 0; i < 5; i ) {
a[i] = i 1;
}
out = a;
}
По сути, все, что это делает, это создает новый массив {1,2,3,4,5}
и использует его для указателя out. Далее мы напишем оболочку 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
}
Возможно, есть гораздо более приятный способ сделать это, но это определенно сработало, и я рад этому. Однако, если у кого-нибудь есть способ получше, я по-прежнему открыт для идей.