(Отказ от ответственности: Я понимаю, что это массивная стена с текстом, но я сделал все возможное, чтобы сварить вещи до сути. Если вы знакомы с libtiff, это не очень сложный вопрос)Путаница при реализации _TIFFVGetField при добавлении пользовательского TAG
Я уже задал этот вопрос в списке рассылки libtiff, но я подумал, что у меня тоже есть хорошие шансы, если есть кто-нибудь, кто работал с библиотекой.
Я добавляю свои собственные встроенные тег в библиотеку, используя документацию здесь: http://libtiff.maptools.org/addingtags.html
Итак, я добавил запись в массив TIFFFieldInfo, определенном в верхней части tif_dirinfo.c как так:
{ TIFFTAG_CUSTOM_XXX, 4, 4, TIFF_SLONG, FIELD_XXX, 1, 0, "XXX" },
затем я добавил поле к TIFFDirectory
структуре, определенной в tif_dir.h
:
typedef struct {
/* ... */
int32 td_xxx[4];
} TIFFDirectory;
Теперь я пошел вперед и изменен _TIFFVSetField
и _TIFFVGetField
в соответствии с инструкциями. Здесь я столкнулся с проблемой.
В имитируя рисунок уже присутствует в библиотеке (см реализацию TIFFTAG_YCBCRSUBSAMPLING
, аналогичное тому, что я делаю), я добавил следующий код _TIFFVGetField
:
/* existing, standard tag for reference */
case TIFFTAG_YCBCRSUBSAMPLING:
*va_arg(ap, uint16*) = td->td_ycbcrsubsampling[0];
*va_arg(ap, uint16*) = td->td_ycbcrsubsampling[1];
break;
/* my new tag */
case TIFFTAG_CUSTOM_XXX:
*va_arg(ap, int32*) = td->td_xxx[0];
*va_arg(ap, int32*) = td->td_xxx[1];
*va_arg(ap, int32*) = td->td_xxx[2];
*va_arg(ap, int32*) = td->td_xxx[3];
break;
Насколько я могу скажите, что это совершенно неверно. Цель состоит в том, чтобы заполнить ввод в списке переменных аргументов, основанном на массиве int. Единственное сохранение благодати заключается в том, что аргумент, указанный в va_list
, всегда имеет тип int32
, а код YcBr использует два int16
. Таким образом, он работает, но я не могу скопировать эту реализацию.
_TIFFVGetField
в конечном итоге вызван от TIFFWriteNormalTag
в tif_dirwrite.c
. Соответствующий код заключается в следующем:
case TIFF_LONG:
case TIFF_SLONG:
case TIFF_IFD:
if (fip->field_passcount) {
uint32* lp;
if (wc == (uint16) TIFF_VARIABLE2) {
TIFFGetField(tif, fip->field_tag, &wc2, &lp);
TDIRSetEntryCount(tif,dir, wc2);
} else { /* Assume TIFF_VARIABLE */
TIFFGetField(tif, fip->field_tag, &wc, &lp);
TDIRSetEntryCount(tif,dir, wc);
}
if (!TIFFWriteLongArray(tif, dir, lp))
return 0;
} else {
if (wc == 1) {
uint32 wp;
/* XXX handle LONG->SHORT conversion */
TIFFGetField(tif, fip->field_tag, &wp);
TDIRSetEntryOff(tif,dir, wp);
} else {
/* ---------------------------------------------------- */
/* this is the code that is called in my scenario */
/* ---------------------------------------------------- */
uint32* lp;
TIFFGetField(tif, fip->field_tag, &lp);
if (!TIFFWriteLongArray(tif, dir, lp))
return 0;
}
}
break;
Итак, неинициализированная указатель lp
объявлена и его адрес передается TIFFGetField
. Это, в свою очередь, настраивает va_list (только lp
) и вызывает TIFFVGetField
, который вызывает _TIFFVGetField
с поставленным va_list
и указателем на неинициализированный указатель.
Здесь есть две проблемы.
Во-первых, это то, как библиотека извлекает данные (мой код, но опять же, следуя образцу уже присутствует)
*va_arg(ap, int32*) = td->td_xxx[0];
Это кажется неправильным. Он устанавливает исходный указатель на значение int. Я предположил, что, возможно, в следующем примере (TIFFTAG_YCBCRSUBSAMPLING) эти целые числа были фактически адресами. Так хорошо, но даже если это так, хотя есть и другая проблема.
В библиотеке вызывается va_args
N
раз, где N
- это количество элементов в массиве. Из того, что я вижу, список переменных содержит только один аргумент (адрес указателя).Это неопределенное поведение согласно стандартной (важный бит в начале):
Если нет никакого фактического Следующий аргумент, или если тип не совместим с типом фактический следующий аргумент (как повышен в соответствии с объявления по умолчанию), поведение не определено.
Правильная версия будет
*va_arg(ap, int32**) = td_xxx;
Это устанавливает ранее неинициализированный указатель на массив, который является действительным. Мне не нравится, что он указывает на сами данные, а не на копию, но что угодно; по крайней мере, это не сбой и дает правильный результат.
Меня беспокоит то, что у меня что-то не хватает. Это программное обеспечение устарело и используется многими, многими людьми. Таким образом, называя это ошибкой, мне кажется, что я виню крах компилятора, который почти всегда ошибается.
Однако я не могу объяснить, как это правильно, в частности, как библиотека записывает то, что возвращает va_arg
при вызове более одного раза.
Любая помощь была бы принята с благодарностью. Заранее спасибо.