Захотелось отвязаться от явной связи с библиотекой и сделать динамическую загрузку. Для этого сделаны следующие изменения:
...
#include"mysql.h"
...
#if DL_MYSQL
typedef MYSQL * STDCALL (*xmysql_init)(MYSQL *mysql);
... // тут ещё 14 подобных typedef для других нужных функций
...
class CMysql...
{
...
public:
bool Init()... // грузит библиотеку; кладёт адреса нужных функций в члены-статики.static xmysql_init m_pmysql_init;
...
};
#define sph_mysql_init (*CMysql::m_pmysql_init)
...
xmysql_init CMysql::m_pmysql_init = NULL;
...
CMysql MysqlHoder;
bool InitDynamicMysql()
{
return MysqlHoder.Init();
}
#else// !DL_MYSQL#define sph_mysql_init mysql_init
#define InitDynamicMysql() (true)
#endif// DL_MYSQL
...
if (!InitDynamicMysql())
... // что-то пошло не так, ругаемся и вылетаем;
sph_mysql_init ( &m_tMysqlDriver );
...
В общем — ничего волшебного; обычная рутинная загрузка. Макрос DL_MYSQL определяется configure-скриптом, и позволяет задействовать старый статический вариант без накладных расходов.
Но вот с динамическим вариантом... да, всё работает. Но смущает вот это:
...
typedef MYSQL * STDCALL (*xmysql_init)(MYSQL *mysql);
...
В смысле — я залез в файл mysql.h и вытащил оттуда прототип mysql_init. Окружил имя (*x...) и получил указатель на функцию (а потом так же 14 раз для остальных использованных функций).
Выглядит коряво, нелогично и избыточно!
Возник вопрос — а нельзя ли как-нибудь объявить указатель на функцию, не цитируя явно её сигнатуру?
Т.е. у нас есть "чёрный ящик" — хедер, где объявлена функция.
Мы знаем имя нужной функции и хотим объявить указатель, в который можно положить адрес на эту функцию, и потом разыменовывать и использовать его как исходную функцию, НЕ цитируя явно её прототип из хедера.
Возможно ли такое?
Как один из вариантов смотрел на такой "велосипед":
#if DL_MYSQL
#define mysql_init (*xmysql_init)
... // другие подобные строки для других нужных функций#include"mysql.h"
...
Получится, что нужный указатель будет объявлен прямо из хедера, за счёт подстановки стоящего перед ним #define. Но! Это уже не typedef а прямое объявление переменной. В рамках одного исходника работает, но если вынести в хедер и включить несколько раз — получаются множественные объявления.
Также не думал о возможных краевых эффектах (например, что будет, если функция используется в самом хедере, в inline-определении другой функции?).
Что подскажете, господа програмисты? Неужели такой "велосипед" — это единственный способ забороть ситуацию?
Re: Можно ли объявить и использовать указатель на функцию без сигнатуры?
От:
Аноним
Дата:
23.11.13 10:45
Оценка:
Здравствуйте, Klirik, Вы писали:
K>Что подскажете, господа програмисты? Неужели такой "велосипед" — это единственный способ забороть ситуацию?
Если цель — сделать наличие dll-ки опциональным, то проще всего воспользоваться механизмом Delay Loaded DLLs.
Т.е. не надо будет руками грузить указатели — загрзука DLL будет проведена только при первом обращении к функции из хэдера, а не старте приложения.
Естественно проверку наличия DLL (InitDynamicMysql) нужно будет оставить и проводить до первого обращения к функциям.
Re[2]: Можно ли объявить и использовать указатель на функцию без сигнатуры?
Здравствуйте, Аноним, Вы писали: А>Если цель — сделать наличие dll-ки опциональным, то проще всего воспользоваться механизмом Delay Loaded DLLs.
Примерно так. Только в моём случае чаще .so чем .dll (проект кросс-платформенный, и под никсы собирается гораздо чаще, чем под винду). И механизм особенно актуален именно в никсовом случае. MSDN в этом случае, увы, бесполезна.
Re: Можно ли объявить и использовать указатель на функцию без сигнатуры?
Здравствуйте, Klirik, Вы писали:
K>Возник вопрос — а нельзя ли как-нибудь объявить указатель на функцию, не цитируя явно её сигнатуру?
decltype в C++ 11 позволяет объявить что угодно, не цитируя явно тип, включая и указатель на функцию.
#include"stdio.h"// из внешнего заголовкаint LibraryFunction(int a, int b);
// типа GetProcAddress
// (использую такой AnyFunction вместо void* чтобы скомпилирвалось без ворнингов в онлайн компиляторе)typedef void(*AnyFunction)();
AnyFunction GetProcAddress(const char* );
int main()
{
typedef decltype(LibraryFunction) * LibraryFunctionPtr; // вот оно
LibraryFunctionPtr libraryFunction;
libraryFunction = reinterpret_cast<LibraryFunctionPtr>(GetProcAddress("LibraryFunction"));
libraryFunction(2, 5);
}
AnyFunction GetProcAddress(const char* );
int LibraryFunctionImpl(int a, int b)
{
printf("hello hello %d %d\n", a, b);
return 0;
}
AnyFunction GetProcAddress(const char* )
{
return reinterpret_cast<AnyFunction>(LibraryFunctionImpl);
}
Эмуляция decltype в до-С++11 — Boost.Typeof
Русский военный корабль идёт ко дну!
Re[3]: Можно ли объявить и использовать указатель на функцию без сигнатуры?
От:
Аноним
Дата:
24.11.13 18:42
Оценка:
Здравствуйте, Klirik, Вы писали:
K>Примерно так. Только в моём случае чаще .so чем .dll (проект кросс-платформенный, и под никсы собирается гораздо чаще, чем под винду).
упс про никсы ничего скзать не могу но вы погуглите на эту тему, скорее всего есть что-то похожее — у меня вот что нашлось за 15 секунд.
Re: Можно ли объявить и использовать указатель на функцию без сигнатуры?
Здравствуйте, Klirik, Вы писали:
K>Мы знаем имя нужной функции и хотим объявить указатель, в который можно положить адрес на эту функцию, и потом разыменовывать и использовать его как исходную функцию, НЕ цитируя явно её прототип из хедера. K>Возможно ли такое?
Можно похимичить с .lib-файлами.
Т.е. сделать библиотеку, реализующую интерфейс mysql, внутри которой будут санки, полностью подконтрольные тебе, а не загрузчику.
Здравствуйте, Кодт, Вы писали:
К>Можно похимичить с .lib-файлами. К>Т.е. сделать библиотеку, реализующую интерфейс mysql, внутри которой будут санки, полностью подконтрольные тебе, а не загрузчику.
Да, про такое тоже думал. Вся проблема в том, что оно всё как-то нетривиально и многословно. По сути получится вместо дублирования из хедера объявления нужных функций (с указателем вместо имени) писать полностью свой "велосипед", что примерно то же самое.
Хочу найти способ сделать это кратко и красиво . К тому же там не только mysql, а ещё несколько подобных "толстых до зависимостей" либ. И весь смысл фичи — сделать так, чтобы пакет при установке не тянул за собой как зависимости половину из известных либ.
Пока что ничего проще подмены объявления в хедере через предшествующий #define не получилось.
Re[3]: Можно ли объявить и использовать указатель на функцию без сигнатуры?
Здравствуйте, Klirik, Вы писали:
K>Хочу найти способ сделать это кратко и красиво . К тому же там не только mysql, а ещё несколько подобных "толстых до зависимостей" либ. И весь смысл фичи — сделать так, чтобы пакет при установке не тянул за собой как зависимости половину из известных либ.
Хочешь скачивать-устанавливать зависимости при первом обращении? По-моему, это плохая идея.
Даже микрософтовский инсталлятор, который такую фичу поддерживает (я уж не знаю, как именно это реализовано, но видимо, на очень высоком уровне), — больше бесит, чем помогает. Захотел формулу вставить, а вместо этого выпрыгивает мастер установки и начинает мозг канифолить...
Так что отдайся менеджерам пакетов (под виндами — соответствующим редискам), пусть зависимости единожды встанут в систему, и всё.
Перекуём баги на фичи!
Re[3]: Можно ли объявить и использовать указатель на функцию без сигнатуры?
Здравствуйте, Klirik, Вы писали:
K>Хочу найти способ сделать это кратко и красиво . К тому же там не только mysql, а ещё несколько подобных "толстых до зависимостей" либ. И весь смысл фичи — сделать так, чтобы пакет при установке не тянул за собой как зависимости половину из известных либ.
по поводу копирования сигнатур: чем не выход? можно кодогенератором+парсером (на любом скриптовом языке) всех их вытащить из .h и нагенерить в другие .h
Re[4]: Можно ли объявить и использовать указатель на функцию без сигнатуры?
Здравствуйте, Кодт, Вы писали:
К>Хочешь скачивать-устанавливать зависимости при первом обращении? По-моему, это плохая идея.
Не, идея как раз противоположная. Благополучно "взлетать" вне зависимости от наличия конкретных либ, работа с которыми не востребована в конкретном юзкейсе. И при попытке обращения давать вразумительный отлуп на тему "а поставь-ка сперва пакет mysql-client. И попробуй ещё раз". Если юзеру это нафиг не сдалось (вообще он собирался заюзать unixodbc, просто случайно в конфиг затесался кусок про mysql) — он это прочитает и сделает правильно.
Re[5]: Можно ли объявить и использовать указатель на функцию без сигнатуры?
Здравствуйте, Klirik, Вы писали:
K>Не, идея как раз противоположная. Благополучно "взлетать" вне зависимости от наличия конкретных либ, работа с которыми не востребована в конкретном юзкейсе. И при попытке обращения давать вразумительный отлуп на тему "а поставь-ка сперва пакет mysql-client. И попробуй ещё раз". Если юзеру это нафиг не сдалось (вообще он собирался заюзать unixodbc, просто случайно в конфиг затесался кусок про mysql) — он это прочитает и сделает правильно.
Тогда — устроить маленький управляемый dll hell: положить библиотеку-заглушку в дальний угол, чтобы она проигрывала существующей реальной библиотеке, но на безрыбьи попадалась загрузчику, и при вызове её функций ругалась.
Перекуём баги на фичи!
Re: Можно ли объявить и использовать указатель на функцию без сигнатуры?
Во-первых:
K>Как один из вариантов смотрел на такой "велосипед":
K>
K>#if DL_MYSQL
K>#define mysql_init (*xmysql_init)
K>... // другие подобные строки для других нужных функций
K>#include"mysql.h"
K>...
K>
оказался не очень хорош. Во-первых — нужно чтобы хедер был только в одной единице трансляции. Иначе получаются множественные определения. Ради этого уже приходится делать довольно много подготовительной работы.
Во-вторых, posgresql с его немудрёными extern int FooFunction(...) в хедере тут же полностью нокаутировал "велосипед". Покуда когда extern int (*xFoofunction)(...), конечно, позволяет пользоваться xFooFunction в коде, но вот саму переменную, увы, уже не определяет.
Однако благодаря ссылке на Boost.Typeof (спасибо Alexander G!) я посмотрел, как сделано там и раскопал специфичное (для целевой платформы) расширение компилятора.
#if DL_UNIXODBC
#if defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC) || defined(__GNUC__)
// use non-standard compiler extension __typeof__
// it allow to declare pointer to the function without using it's declarationtypedef __typeof__ ( SQLFreeHandle ) *xSQLFreeHandle;
typedef __typeof__ ( SQLDisconnect ) *xSQLDisconnect;
... ещё куча объявлений
#else// compilers which are not known about __typeof__ support
// declarations below are directly copy-pasted from sql.h and sqlext.h,
// and then (*x...) is placed around the function names.
// In mostly cases this code will not be used, and the declarations
// from previous block will be used instead.
#warning Be sure that the unixodbc function signatures are the same \
as in sql.h and sqlext.h Correct the code below if this is not so.
typedef SQLRETURN SQL_API (*xSQLFreeHandle)(SQLSMALLINT HandleType, SQLHANDLE Handle) //NOLINTtypedef SQLRETURN SQL_API (*xSQLDisconnect)(SQLHDBC ConnectionHandle); //NOLINT
... ещё куча копипастов
#endif
#endif
Рассчёт на то, что макросы DL_ в результате работы configure-скрипта будут в ходу только на *nix системах, где практически везде gcc, который поддерживает __typeof__