Здравствуйте! Перевожу (пытаюсь) заголовочник C на Delphi. Типы данных и объявления функций, экспортируемых DLL. Существует несколько версий этой DLL, двоично несовместимых между собой. В заголовочнике C это учитывается с помощью директив условной компиляции. Выглядит это примерно так:
Проблема в том, что основные настройки компиляции хотелось бы запихнуть в файл config.inc, по примеру оригинала. Насколько идеологически правильно убирать в него константу? Стоит ли так делать (идея была в том, что режим компиляции настраивается без правки header.pas)? Можно ли сделать как-то по-другому?
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
22-07-2013 14:27 | Сообщение от автора вопроса
Python,
>>>А что находится ПОД директивами условной компиляции?
Кратко: директивами условной компиляции определяется наличие или отсутствие в записи некоторых полей, общее количество которых превышает 50 :D
В общем, у меня появилась такая идея...
1. Привести libJPEG.pas в соответствие актуальному jpeglib.h из поставки libjpeg-turbo
2. Сделать заголовочник для TurboJPEG API, переведя turbojpeg.h
3. Двоичной совместимостью с DLL libJPEG v6b (jpeg62.dll) управлять с помощью директив условной компиляции, как это сделано в jpeglib.h
4. Статическим / динамическим подключением DLL управлять с помощью директив условной компиляции
5. Написать объектную обертку над libJPEG — TLibJpegCodec, инкапсулирующий структуры для кодирования / декодирования и обработку ошибок
6. Написать клон TJPEGImage, повторяющий интерфейс оригинального класса, а внутри являющийся переходником к TLibJpegCodec
7. Совместимость с Delphi 5-7+
8. Совместимость с FPC, раз уж она заявлена в libJPEG.pas
9. Потокобезопасность, раз уж она реализована в libJPEG
10. Документация в HTML (сейчас она прямо в исходном коде, в соответствии с оригиналом, но мне не очень нравится)
Прогресс:
1-4 - частично
5 - набросок public-части, меняется в процессе копания в библиотеке
6 - в планах
7 - автоматически :)
8 - в планах
9 - частично, плохо знаю тему
10 - на стадии чтения мануала к FPDoc :)
Этот вопрос был по п. 1-4. Сейчас мне видится такая схема:
jpeglib.h -> libJPEG.pas
turbojpeg.h -> TurboJPEG.pas
+ libJPEG_config.pas — тут директивы условной компиляции для п. 1-4 плюс реализация динамической загрузки DLL по запросу. Этот модуль должен использоваться двумя другими. Не знаю, почему я сначала подумал про INC-файл.
Была libJPEG v6b. На ней, кстати, основан стандартный дельфийский TJPEGImage. Там тупо libJPEG скомпилирована в *.obj и прилинкована в таком виде к заголовочнику *.pas. Для libJPEG v6b в виде динамически подключаемой DLL, есть еще заголовочник libJPEG.pas 2008 года (по идее, совместимый с FPC, но не проверял), который больше не поддерживается. С тех пор libJPEG ушла далеко вперед. В версиях 7 и 8 появились новые возможности, из-за которых пришлось сломать обратную двоичную совместимость с 6-й версией. Там в функции кодирования и декодирования передаются огромные записи jpeg_compress_struct и jpeg_decompress_struct, больше 50 полей в каждой. Так вот, в версиях 7 и 8 в этих записях появились новые поля, что делает невозможным (без перекомпиляции) использование новых версий DLL программами, рассчитанными на 6-ю версию.
У libJPEG есть форк, libjpeg-turbo. Функции оптимизированы с использованием инструкций MMX, SSE2, NEON. Разработчики решили поддерживать двоичный интерфейс libJPEG v6b, поэтому DLL libjpeg-turbo можно спокойно использовать с заголовочником libJPEG.pas, рассчитанным на эту версию. Получается быстрее стандартного TJPEGImage примерно в 5-6 раз. Можете посмотреть демо-программу Sapersky, которую он предложил мне в »вопрос КС №82040«.
В примере Sapersky как раз используется libJPEG.pas. Проблема в том, что libJPEG декодирует изображение в формат RGB, а в TBitmap используется обратный порядок BGR. Поэтому в том примере после декодирования потом еще "вручную" преставляются байты. Я посмотрел в современные заголовочники libjpeg-turbo, а там уже есть возможность декодирования в нужный формат:
typedef enum {
JCS_UNKNOWN, /* error/unspecified */
JCS_GRAYSCALE, /* monochrome */
JCS_RGB, /* red/green/blue as specified by the RGB_RED, RGB_GREEN,
RGB_BLUE, and RGB_PIXELSIZE macros */
JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */
JCS_CMYK, /* C/M/Y/K */
JCS_YCCK, /* Y/Cb/Cr/K */
JCS_EXT_RGB, /* red/green/blue */
JCS_EXT_RGBX, /* red/green/blue/x */
JCS_EXT_BGR, /* blue/green/red */
JCS_EXT_BGRX, /* blue/green/red/x */
JCS_EXT_XBGR, /* x/blue/green/red */
JCS_EXT_XRGB, /* x/red/green/blue */
/* When out_color_space it set to JCS_EXT_RGBX, JCS_EXT_BGRX,
JCS_EXT_XBGR, or JCS_EXT_XRGB during decompression, the X byte is
undefined, and in order to ensure the best performance,
libjpeg-turbo can set that byte to whatever value it wishes. Use
the following colorspace constants to ensure that the X byte is set
to 0xFF, so that it can be interpreted as an opaque alpha
channel. */
JCS_EXT_RGBA, /* red/green/blue/alpha */
JCS_EXT_BGRA, /* blue/green/red/alpha */
JCS_EXT_ABGR, /* alpha/blue/green/red */
JCS_EXT_ARGB /* alpha/red/green/blue */
} J_COLOR_SPACE;
Одно из полей записи jpeg_decompress_struct как раз имеет тип J_COLOR_SPACE. Я безболезненно дописал libJPEG.pas и получил еще 24% рост производительности. Тут можно было вляпаться с выравниванием, но, к счастью, все работает на ура.
{$MINENUMSIZE 4}
...
// New in libJPEG-turbo
{$DEFINE JCS_EXTENSIONS}
{$DEFINE JCS_ALPHA_EXTENSIONS}
// jpeglib.h line 219
J_COLOR_SPACE = (
JCS_UNKNOWN, { error/unspecified }
JCS_GRAYSCALE, { monochrome }
JCS_RGB, { red/green/blue }
JCS_YCbCr, { Y/Cb/Cr (also known as YUV) }
JCS_CMYK, { C/M/Y/K }
JCS_YCCK { Y/Cb/Cr/K }
{$IFDEF JCS_EXTENSIONS},
JCS_EXT_RGB, { red/green/blue }
JCS_EXT_RGBX, { red/green/blue/x }
JCS_EXT_BGR, { blue/green/red }
JCS_EXT_BGRX, { blue/green/red/x }
JCS_EXT_XBGR, { x/blue/green/red }
JCS_EXT_XRGB { x/red/green/blue }
{$IFDEF JCS_ALPHA_EXTENSIONS},
{ When out_color_space it set to JCS_EXT_RGBX, JCS_EXT_BGRX,
JCS_EXT_XBGR, or JCS_EXT_XRGB during decompression, the X byte is
undefined, and in order to ensure the best performance,
libjpeg-turbo can set that byte to whatever value it wishes. Use
the following colorspace constants to ensure that the X byte is set
to 0xFF, so that it can be interpreted as an opaque alpha
channel. }
JCS_EXT_RGBA, { red/green/blue/alpha }
JCS_EXT_BGRA, { blue/green/red/alpha }
JCS_EXT_ABGR, { alpha/blue/green/red }
JCS_EXT_ARGB { alpha/red/green/blue }
{$ENDIF}
{$ENDIF}
);
В принципе, на этом можно было бы и успокоиться. Десятикратное декодирование фотографии 4000x3000 на Core i3-2100 выполняется примерно за 1150 мс (со старым заголовочником ~1500). Но libjpeg-turbo поддерживает еще и эмуляцию API/ABI libJPEG v7+. Из-за упомянутой двоичной несовместимости, это управляется директивами условной компиляции:
struct jpeg_compress_struct {
...
#if JPEG_LIB_VERSION >= 70
unsigned int scale_num, scale_denom; /* fraction by which to scale image */
JDIMENSION jpeg_width; /* scaled JPEG image width */
JDIMENSION jpeg_height; /* scaled JPEG image height */
/* Dimensions of actual JPEG image that will be written to file,
* derived from input dimensions by scaling factors above.
* These fields are computed by jpeg_start_compress().
* You can also use jpeg_calc_jpeg_dimensions() to determine these values
* in advance of calling jpeg_start_compress().
*/
#endif
...
#if JPEG_LIB_VERSION >= 70
int q_scale_factor[NUM_QUANT_TBLS];
#endif
...
#if JPEG_LIB_VERSION >= 70
boolean do_fancy_downsampling; /* TRUE=apply fancy downsampling */
#endif
...
#if JPEG_LIB_VERSION >= 70
int min_DCT_h_scaled_size; /* smallest DCT_h_scaled_size of any component */
int min_DCT_v_scaled_size; /* smallest DCT_v_scaled_size of any component */
#endif
...
#if JPEG_LIB_VERSION >= 80
int block_size; /* the basic DCT block size: 1..16 */
const int * natural_order; /* natural-order position array */
int lim_Se; /* min( Se, DCTSIZE2-1 ) */
#endif
...
};
(где "..." = "многабукв" :)
Кроме того, libjpeg-turbo содержит более высокоуровневый TurboJPEG API, который тоже хотелось бы иметь возможность использовать в Delphi...
Ладно, предысторию а-ля "что это" и "зачем оно нужно" как-то рассказал :) Что хочу сделать — в следующем сообщении, а то это уже почти 6,5 К.
А что находится ПОД директивами условной компиляции? Там объявлены разные типы данных? Там объявлены разные типы процедур и функций? От этого зависит итоговое решение.
Типы стоит унифицировать. То есть если у нас есть такой колхоз:
Type TData=record A,B:integer;end; // для библиотеки 1 версии
Type TData=record A,B:integer;C:float;end; // для библиотеки второй версии
То стоит породить "общий знаменатель" всех типов и передавать его в процедуры-обёртки. Обёртки, в свою очередь, должны "запаковать" переданные данные (основываясь на версии библиотеки) и "распаковать" их обратно. Ровно также работать с функциями-процедурами-"обёртками" - в зависимости от переданного номера версии, подгружать (динамически) либо один, либо другой вариант функции. Мало того, можно при таком подходе "на лету", не перекомпилируя программу, переключаться между версиями одной и той же библиотеки. Другое дело - надо ли это...
Ну а так - выложите более полный вариант Вашего .h файла. Попробую перевести на паскаль. Не думаю, что это невыполнимая задача.
Да, вы правильно меня поняли. Можно вынести эту константу в отдельный файл и включать его где нужно директивой {$I}, но дело в том, что в Дельфи механизм включения файлом работает иначе чем в С/С++, где можно проверить был ли файл уже включен или нет. Дельфи будет каждый раз включать файл, и наплодит вам много одинаковых констант LIB_VERSION (хотя наверное это совсем не проблема)
Просто во всех попадавшихся мне настроечных INC-файлах, входящих в дельфийские библиотеки, я видел только директивы условной компиляции, констант там нету (если только строковые). Из этого я предположил, что убирать константы во включаемые файлы "не принято", что ли... Компилятору-то, насколько я понимаю, пофиг, там можно хоть классы объявлять (смысла только нет). Тут вопрос дизайна — с одной стороны, удобно запихнуть весь интерфейс библиотеки в один-единственный файл PAS. С другой, настройки режимов компиляции логично сгруппировать в каком-то одном месте (там их достаточно много наберется: разные версии компилятора, разные версии самой DLL, кросплатформенность, совместимость с Free Pascal и т.п.) Это может быть либо "шапка" модуля, либо отдельный файл.
~AQUARIUS~, вы, значит, склоняетесь к варианту вытащить эту константу повыше и написать над ней комментарий, какие значения и зачем ей присваивать, и на что это влияет?
Вариант как в вашем вопросе - практически и есть копия 1 в 1. Если config.h еще где-то используется, его можно сделать отдельным файлом, и потом включить с помощью директивы {$I filename}, если нет - тогда ваш вариант вполне нормальный
VERХХХ - это версия компилятора, она есть всегда, на ее основе определяется версия делфей, а на основе версии - уже логические определения типа DELPHI_9_UP, что означает, что это Делфи 9 и выше.
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.