cplus-plus.ru logo
Мы переехали на cplus-plus.ru
Главная страница В закладкиО сайтеКарта сайта
Хостинг от uCoz
Добавить в закладки

Меню сайта

Полезные ссылки

Наша рассылка
Подписаться на рассылку
"C++ : cplus-plus.ru :
Рассылка статей C++"


Друзья сайта
alsproject.ru Выбор выходного разделительного конденсатора

Приветствую Вас, Гость · rss 26-Сен-2017, 11:42
Главная » 2011 » Январь » 26 » Обертка для вызова функций по их адресу
18:06
Обертка для вызова функций по их адресу

Обертка для вызова функций по их адресу
Доброго времени суток!
Было дело — делал я интерфейс для работы с модулями для USB от FTDI. Пришлось изрядно повозиться с подключением DLL-интерфейса. Разочаровавшись в возможностях автоматической линковки Microsoft Visual Studio 2008 (UPD: потом я разобрался с этой темой), я решил это делать вручную. По ходу дела задолбался  очень надоело вручную подключать несколько десятков функций. И тогда я обратился к Google, C++ и шаблонам. И если подключение DLL в стиле C++ вопросов не вызвало, то удобный вызов подключенных функций в стиле «Error = FT_Open (Num, &_Handler)», где FT_Open- объект, удался не сразу. Итог (для таких вот функций) — под катом. Если вкратце — я сделал обертку вокруг указателя на функцию. 

Постановка задачи

Сразу оговорюсь — я работаю в Windows XP Prof, Visual Studio. Это принципиально для получения адреса функции. Впрочем, при работе с указателями на функции это не важно.

Ну так вот, для тех, кто не в теме, вот последовательность для нахождения той самой функции FT_Open из FTD2XX.dll средствами WinAPI:

#include "FTD2XX.h" // библиотека от FTDI
typedef FT_STATUS (*pFT_Open) (int, FT_HANDLE *); // тип данных "функция FT_OPEN"

// ...

HMODULE hMod = LoadLibrary ("FTD2XX.dll"); // загрузка библиотеки - д. б. не ноль
pFT_Open pOpen = GetProcAddress (hMod, "FT_Open"); // получили адрес функции - также д. б. не ноль

// ...

FT_STATUS st = pOpen (0, &hDev); // вызываем функцию

// ...

FreeLibrary (hMod); // закрыли библиотеку

Это не беда, когда функция у вас одна, но в этой самой библиотеке я насчитал 51 функцию. И для каждой мне нужно сделать следующее:

typedef FT_STATUS (*pFT_Open) (int, FT_HANDLE *); // тип данных "указатель на функцию"
pFT_Open pOpen; // переменная "указатель на функцию"
pFT_Open pOpen = GetProcAddress (hMod, "FT_Open"); // получение адреса функции "FT_Open"

Особенно раздражает необходимость генерить кучу typedef. Да, я знаю, можно писать и без typedef, но это выглядеть будет ОМЕРЗИТЕЛЬНО!

Посему хочется как-то упростить себе жизнь:

Funct2<FT_STATUS, int, FT_HANDLE *> Open; // тип данных "функция 2х аргументов"
Open = GetProcAddress (hMod, "FT_Open"); // получение адреса функции "FT_Open"

// ...

FT_STATUS st = Open (0, &hDev); // вызов функции
Решение

В ходе экспериментов и кипения мозгов я получил такой вот шаблон класса:

template <typename Ret, typename Arg1, typename Arg2,
typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue>
class Funct2
{
public:
typedef Ret (*tfPtr) (Arg1, Arg2);
tfPtr fPtr;

public:
Funct2 (tfPtr Ptr = 0): fPtr (Ptr) {}

Funct2 &operator= (void *Ptr) { fPtr = reinterpret_cast<tfPtr> (Ptr); return *this; }
Ret operator () (Arg1 A1, Arg2 A2) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1, A2); }
}; // class Funct2

Думаю, тут все элементарно, но все-таки для непосвященных объясню.

Создается шаблон Funct2, которому первым параметром задается тип Ret, возвращаемый функцией. Следующими двумя параметрами — Arg1 и Arg2 — задаются типы аргументов функции. С целью универсиализации обработки ошибок задается тип исключения Except и его значение Value (параметры по умолчанию задаются #define FunctPtrExceptionType и #define FunctPtrExceptionDefValue).

В теле шаблона класса задается тип tfPtr «указатель на функцию с двумя параметрами» и сам указатель fPtr.

Конструктор по умолчанию задает нулевой указатель или конкретный адрес, если он задан. Также адрес может быть задан через перегруженный operator= (void *Ptr). Почему void * — потому что GetProcAddress () возвращает именно void *. Нет нужды перегружать его сигнатурой operator= (tfPtr Ptr) — компилятор и так понимает, о чем речь.

Ну и, наконец, перегружая operator (), мы добиваемся использования класса как функтора, а для пользователя класса — так и вообще простого вызова функции.

Удобно? Очень! Смотрите:

Результат

#include < Windows.h > // для GetProcAdress
#include < stdio.h > // для printf

// **
// ** Настройка исключений по умолчанию
// **

#define FunctPtrExceptionType int // тип данных для исключения по умолчанию
#define FunctPtrExceptionDefValue 0 // значение исключения по умолчанию

// **
// ** Указатель на функцию без аргументов
// **

template < typename Ret = void, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue >
class Funct0
{
public:
typedef Ret (*tfPtr) (void);
tfPtr fPtr;
public:
Funct0 (tfPtr Ptr = 0): fPtr (Ptr) {}
Funct0 &operator= (tfPtr Ptr) { fPtr = Ptr; return this; }
Ret operator () (void) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (); }
};

// **
// ** Указатель на функцию с 1 аргументом
// **

template < typename Ret, typename Arg1, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue >
class Funct1
{
public:
typedef Ret (*tfPtr) (Arg1);
tfPtr fPtr;
public:
Funct1 (tfPtr Ptr = 0): fPtr (Ptr) {}
Funct1 &operator= (void *Ptr) { fPtr = reinterpret_cast<tfPtr> (Ptr); return *this; }
Ret operator () (Arg1 A1) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1); }
};

// **
// ** Указатель на функцию с 2 аргументами
// **

template < typename Ret, typename Arg1, typename Arg2, typename Except = FunctPtrExceptionType, Except Value = FunctPtrExceptionDefValue >
class Funct2
{
public:
typedef Ret (*tfPtr) (Arg1, Arg2);
tfPtr fPtr;
public:
Funct2 (tfPtr Ptr = 0): fPtr (Ptr) {}
Funct2 &operator= (void *Ptr) { fPtr = reinterpret_cast<tfPtr> (Ptr); return *this; }
Ret operator () (Arg1 A1, Arg2 A2) throw (Except) { if (!fPtr) throw Except (Value); return fPtr (A1, A2); }
};

// **
// ** Примеры вызова функций
// **

int add (const int A, const int *B)
{ int C; C = A + *B; printf (" int add (const int %d, const int %d) = %d\n", A, *B, C); return C; }

void prn (void)
{ printf (" void prn (void)\n"); }

// **
// ** Точка входа
// **

void main (void)
{
int i, i2;
double d;
long l;
Funct0<> prner (prn);
Funct1< double, long *, int, 2 > longer;
Funct2< int, const int, const int * > adder;

adder = add;
longer = GetProcAddress (0, "Longer");

try
{
prner ();
i2 = 6;
i = adder (5, &i2);
d = longer (&l);
}
catch (int val)
{
switch (val)
{
case 2: printf (" *** не удалось определить адрес функции!\n"); break;
default: printf (" *** ошибка вызова функции!\n"); break;
}
}

Итог

Дизассемблер в режиме Release показал, что накладные расходы при вызове такой функции — проверка 0-го значение и в связи с этим еще один call. Я думаю, для современных PC это не беда.

Для совершенства тут можно как-то доработать тему исключений — было бы хорошо туда передавать текстовые строки, свои произвольные классы ошибок и т. п. Но лень я недостаточно хорошо знаю шаблоны, чтобы это реализовать.

Ну и, понятное дело, надо наклепать разных вариантов Funct для 3х, 4х и т. д. аргументов. Хорошо бы придумать какой-то макрос, который бы их генерил…

Ну и, еще более понятное дело, надо все это вынести в отдельный .H-файл.

Я надеюсь, кому-то сэкономил время. Буду благодарен за конструктивные комментарии!

P. S. По ходу эксплуатации вскрылась такая неприятная вещь, как соглашение о вызовах. Похоже, надо делать Funct0Stdcall, Funct0Cdecl; Funct1Stdcall, Funct1Cdecl…
Источник: http://habrahabr.ru
Категория: Новости | Просмотров: 1389 | Добавил: FazaNaka | Рейтинг: 5.0/1
Всего комментариев: 4
4  
Это просто бесподобное сообщение wink

---

Курсы валют ЦБ http://control-ss.ru/index.php?option=com_k2&view=itemlist&task=user&id=32071 - здесь

3  
Какие нужные слова... супер, замечательная идея

---

VIPpromocodeRU http://xn----8sbf5aibmcxe2m.xn--p1ai/component/k2/itemlist/user/23573 - тут

2  
http://stroistandart.by/index.php?option=com_k2&view=itemlist&task=user&id=232
http://www.daskolias.gr/index.php?option=com_k2&view=itemlist&task=user&id=566
http://www.mobila.md/index.php?option=com_k2&view=itemlist&task=user&id=570

1  
http://vetcarekenya.co.ke/index.php?option=com_k2&view=itemlist&task=user&id=133
http://social.lviv.ua/index.php?option=com_k2&view=itemlist&task=user&id=538
http://appletreegroup.ir/index.php?option=com_k2&view=itemlist&task=user&id=705

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]