LINUX.ORG.RU

Одна библиотека с разным API - как правильно сделать костыль?

 


0

1

Есть библиотека librtmp, у нее грубо говоря есть 2 версии: релиз и транк. Релиз был еще в 2010 году, поэтому транк переделан хоть и сильно (с поломаным апи), но #define RTMP_LIB_VERSION 0x020300 в обоих версиях одинаковый.

Грубо говоря, в одной версии: someFunc(int a, int b, int c);

А в другой версии someFunc(int a, int b, int someNewParam, int c);

Как бы мне правильнее сделать поддержку обоих версий? Можно конечно попробовать найти левый #define который есть в новой версии, но это совсем костыль.



Последнее исправление: cetjs2 (всего исправлений: 1)

Можно привязаться к версии и делать макросы со всеми нужными параметрами и в макросах отбрасывать неиспользуемое. Можно сделать __inline__ функции-обёртки, которым будешь передавать все параметры, а в функциях макросами определять какую функцию и с какими параметрами библиотеки вызывать (так делают HAL обёртки в драйверах с проприетарной частью, например madwifi).

Оба варианта пользуют макросы, самый производительный первый вариант, но можно нехило нарваться на нечитаемость, если функций дофейхуа. Если важна читабельность, то реализуй второй вариант.

nickleiten ★★★
()

Если вопрос конкретно в определении версии библиотеки, то искать уникальные дефайны и привязываться к ним, иначе никак (если о простом решении речь). Можно ещё делать проверку на формат функции в Makefile и в нём же определять define для компиляции твоих исходников, а что в исходниках - отписался выше.

nickleiten ★★★
()
Ответ на: комментарий от nickleiten

Ну привязываться к версии библиотеки - это опять картинка «сегодня компиляется, а завтра не компиляется», именно этого хотелось бы избежать, именно поэтому привязка к дефайнам - сделка с дьяволом.

За проверку в мейкфайле спасибо, наверное так и сделаю.

А можно подробнее за инлайн-обертку? Ведь хидеры разные, оно же просто не соберется.

pejuto
() автор топика
Ответ на: комментарий от anonymous

потому что такой тег есть, а просто «С» - нет

pejuto
() автор топика

Проверять в системе сборки, какая функция есть, а какой нет

UPD: тред не читал, тут это уже насоветовали

buddhist ★★★★★
()
Последнее исправление: buddhist (всего исправлений: 1)
Ответ на: комментарий от buddhist

AC_CHECK_MEMBER(struct RTMP_LNK.usherToken, [AC_DEFINE(HAVE_LIBRTMP_USHER,1,[We have usher field?])],[],[#include <librtmp/rtmp.h>])

Но я все равно принимаю ответы

pejuto
() автор топика

Для общего понимания: твоя программа может работать как с release, так и с trunk. Правильно?

trunk находится всё ещё в pre-alpha стадии и для него скорей всего нет нигде пакетов, а есть только для release?

Так зачем тогда все эти свистопляски? Указываешь в зависимостях librtmp-release и неспеша готовишь в соседнем branch librtmp-next-release.

PS: подумай о пользователях. Я например очень не люблю, когда программы зависят от неопакеченых blooding-edge-trunk версий библиотек.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от pejuto

Например,

/* File rtmp-wrapper.h */
#include <librtmp/librtmp.h>

inline int somefunc_wrap(int arg1, int arg2, int arg3){
#ifdef ISVERSION_BLABLA
      somefunc(arg1,arg3);
#else
      somefunc(arg1,arg2,arg3);
#endif
}

Это для наглядности пример был. Вообще же, имеет смысл хранить данные в структуре, и в somefunc_wrap передавать указатель на структуру, а внутри уже работать с данными этой структуры - в итоге не придётся перелопачивать все врапперы для добавления аргументов, а просто их добавлять в структуру. inline в данном случае должен вставлять содержимое функции в тело функции, вызывающей её. А дефайны ещё на уровне препроцессора развернутся в нужный вид, в итоге в твою функцию попадёт именно вызов нужной версии. Отдельно замечу - это сделано именно в заголовке, чтоб препроцессор добавлял содержимое перед компиляцией в текст твоей программы, тогда inline сработает. Ну и естественно SOMEFUNC_BLABLA устанавливается флагом компилятора/препроцессора gcc -DSOMEFUNC_BLABLA=1 (не помню, насчёт обязательности единицы, но на всякий случай пусть будет) в самом Makefilе, в нём уже парсишь заголовки librtmp и регекспами, например, проверяешь что там функция просит.

nickleiten ★★★
()
Ответ на: комментарий от pejuto

Насчёт привязки к дефайнам - никто и не заставляет, с помощью Makefile'ов хоть md5sum заголовочных файлов считай и сравнивай :) Но с trunk это всегда так, тебе придётся постоянно играть роль догоняющего разрабатываемой ветки. То есть следить, что изменилось и добавлять в свою программу дополнительные костыли совместимости (ведь мы об этом говорим, наша основная задача иметь совместимость с разными версиями библиотеки).

Насчёт inline, желательно в таком случае вообще вместо inline поставить аттрибут __always_inline, чтоб даже без оптимизаций компилятор делал вставку кода в твои функции.

А вообще, видел в fcrackzip бешенную (я бы сказал упоротую) реализацию поддержки нескольких оптимизаций, которые реализованы формированием исходного текста программы скриптом перед компиляцией. Возможно, тебе будет проще сделать что-то подобное. То есть, на perl/python/php/java/(да на чём угодно) делать разбор строки прототипов вызываемых функций и по ним уже формировать динамически свою программу. Просто в случае с wrapper'ом, это будет выглядеть более эстетично. Но в любом случае, придётся быть готовым к нежданчикам вида, кому-то захотелось вместо somefunc сделать SomeFunc (ну меркурий в ретрограде и у автора обострение, тем более, как заметили выше, trunk - это всегда pre-alpha, измениться может что-угодно) - то есть отслеживать всё-равно вручную.

nickleiten ★★★
()

транк переделан хоть и сильно (с поломаным апи), но #define RTMP_LIB_VERSION 0x020300 в обоих версиях одинаковый.

Как бы мне правильнее сделать поддержку обоих версий?

отправить вполне обоснованный patch в транк?

может не ты один так мучаешься - вместо изобретения персональных костыликов поможешь сообществу :)

MKuznetsov ★★★★★
()
Ответ на: комментарий от beastie

Самое «смещное» в том, что транковую версию положили в новый стабильный дебиан. В результате оно собирается на старом, но не новом дебиане, в обоих случаях librtmp поставлен из репозитария.

pejuto
() автор топика
Ответ на: комментарий от nickleiten

Собственно, вопрос был как задефайнить этот флаг, по которому можно сделать ifdef. Я использовал автоконф и его AC_CHECK_MEMBER, но это не совсем то: тут проверяется наличие поля в структуре, в моем случае этого достаточно (это поле как раз и заполняется через вызов функции), т.е. в конкретно моем случае это хороший вариант, но на будущее, когда функция может быть разной и структуры не будет - не очень понятно. Парсить регекспами конечно можно, но думаю это еще более кривой вариант чем остальное. Может попробовать осилить cmake или что-то подобное?

pejuto
() автор топика
Ответ на: комментарий от pejuto

Но в данном случае это тебе всё таки «надо». Да и всего-то надо только «бампнуть» RTMP_LIB_VERSION.

beastie ★★★★★
()

Написать свою библиотеку обёртку над ними которая будет дёргать то что тебе надо. Тупо продублируй API и всё.

blogdron
()
Ответ на: комментарий от pejuto

Насколько мне известны разные системы сборок, каждая из них даёт тот или иной заменитель регекспов (читай ручного разбора заголовочных файлов). Так что тут твой вариант самый оптимальный, т.к. в любом из альтернативных придётся отслеживать ручками версии и корректировать собственный wrapper. Увы, но таков наш жестокий социальный мир с укуренными разработчиками сторонних библиотек :) . Это одна из причин, почему я стараюсь всё делать в идеале статичным, то есть в программу сразу всё встроено, а в реальности приходится жёстко ограничивать требования к версиям сторонних библиотек. Иначе, не хватит человеко-часов на разработку и поддержку проектов, особенно, когда за это никто не платит (а вот когда платят - можно и ценник подкорректировать за обезьянью работу).

nickleiten ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.