Приветствую всех.
Немного предыстории. По специфике моей работы мне довольно часто приходится вручную загружать библиотеки, резолвить множество функции. Отчасти потому, что требуется обеспечить совместимость с различными версиями виндовс, отчасти потому, что так бывает удобнее. Конечно, всегда хочется это автоматизировать. С одной стороны, есть поддержка компилятора\линкера в виде Delay Load, с другой стороны, бытует мнение, что использовать этот метод является дурным тоном (и наверное, это так. Несколько раз попав на странное поведение этого механизма, мы от него отказались). Существует достаточно много уже написанных велосипедов, но меня они по ряду причин не устроили. Объяснять все долго, вкратце — некоторые сделаны достаточно "тупо" и неоптимально — т.е. вызывают GetProcAddr и LoadLibrary на каждом шагу, другие не позволяют кастомизировать процесс резолвинга и т.д. и т.п (тот же пример на RSDN, на мой згляд, весьма показателен). Ну да ладно, это все хорошо, а что предлагается.
Предлагается следующее:
1. Гибкая структура определения функций. Синтаксис достаточно прост и расширяем.
2. Возможность задания своих policy на ошибки загрузки функции.
3. Минимальный оверхед — создается _единая_ на все модули таблица указателей на функции, при первом вызове используется прокси с поиском функции, в дальнейшем — прямой вызов. Единая таблица загруженных модулей.
4. Привычный синтаксис вызова — нет никаких FunResolver<Type>()(params), все внешне выглядит обычным вызовом функции из неймспейса (да так оно на самом деле и есть).
Итак, приступим. Сам код достаточно на мой взгляд понятен и адаптирован под VC6 и будет приведен в слудующем постинге. При желании можно портировать и под другие компиляторы.
Немного пояснений:
Минимальный размер таблицы указателей и их кросс-модульность обеспечивается синглтоном CDynFunction.
Основная проблема, решаемая в приведенном коде — невозможность в С++ инстанцироваться строковым литералом. Приходится извращаться числовыми литералами.
Многое из кода сделано именно так под VC6. Иначе там либо сложно, либо нельзя (ну или я не знаю как).
Публикуется только код для функций с одним параметром. Думаю, при помощи копи\пасте или макросов не составит труда значительно расширить количество параметров.
На самом деле, конечно, у этого решения тоже есть концептуальные недостатки. Например, если в разных модулях для одной функции задать разные policy, то сформируется несколько указателей (сколько policy) + для каждого будет свой прокси и вызываться GetProcAddr. Был и другой вариант кода, где эта проблема была решена отделением CDynFunction от прокси. Фактически, формировалась единая глобальная таблица указателей, а для каждого модуля была своя локальная копия со своими же прокси. Мне почему то показалось, что такой оверхед это слишком и что обычно в одном приложении следуют одинаковой стратегии на обработку ошибок, поэтому текущий подход в общем случае вызовет меньший оверхед в плане использования памяти.
Все это интересно пообсуждать, если будут дельные предложения — поправить\улучшить, поскольку сейчас реализована только концепция. Если получится хорошо — можно все причесать (например, добавить поддержку многопотоковости) и попробовать выложить в статьи как альтернативу CDynaLinkResolver.
AA>то можно будет писать так
A>USE_MODULE_BEGIN(kernel, *"*/ 'kernel32','.dll' /*"*) A> DECLARE_FUN_P1_THROW(GetModuleHandle, *"*/ 'GetModul','eHandleA' /*"*, HMODULE, LPCSTR) A> DECLARE_FUN_P1(GetModuleHandleW, *"*/ 'GetModul','eHandleW' /*"*, HMODULE, LPCWSTR) A>USE_MODULE_END
A>Что ИМХО удобнее. А вот поддерживает ли VC6 __int64 или long long я уже и не помню
А вы подумайте, что я первым делом попробовал Правильно, именно это. Нет, числовые литералы (по крайней мере в VC6) работают только для int (иначе — error C2015: too many characters in constant)
Надо подумать — похоже, так действительно лучше. Хотя, конечно, это мало что меняет. Но в любом случае — спасибо за поправки.
__>Это можно переделать так : __>
А хотя нет, так расходы памяти выше — в любом случае будет статически жить массив размером sizeof(DWORD)*8. В этом случае тогда можно постпуить еще проще — сразу макросом формировать соотв. строку статически и возвращать ее. В случае программного решения "большими" получаются только ненулевые цифры (т.е. <= push DWORD_PTR = 5 байт), а нулевые push BYTE_PRT = 2 байта). Вероятно, это все-таки выгоднее (медленнее, но выгоднее).
Вот бы как-нибудь избавиться от этого оверхеда... я пока вижу только один вариант — списки типов и Const2Type, в этом случае размер строки будет с точностью до sizeof(DWORD), да и избавимся от ограничений на длину строки. Но как тогда формировать строку из списка типов, непонятно... Есть мысли?
__>>Это можно переделать так : __>>
Здравствуйте, Andrew S, Вы писали:
AS>А хотя нет, так расходы памяти выше — в любом случае будет статически жить массив размером sizeof(DWORD)*8. В этом случае тогда можно постпуить еще проще — сразу макросом формировать соотв. строку статически и возвращать ее. В случае программного решения "большими" получаются только ненулевые цифры (т.е. <= push DWORD_PTR = 5 байт), а нулевые push BYTE_PRT = 2 байта). Вероятно, это все-таки выгоднее (медленнее, но выгоднее).
AS>Вот бы как-нибудь избавиться от этого оверхеда... я пока вижу только один вариант — списки типов и Const2Type, в этом случае размер строки будет с точностью до sizeof(DWORD), да и избавимся от ограничений на длину строки. Но как тогда формировать строку из списка типов, непонятно... Есть мысли?
Если их не будет это плохо
AS>>Вот бы как-нибудь избавиться от этого оверхеда... я пока вижу только один вариант — списки типов и Const2Type, в этом случае размер строки будет с точностью до sizeof(DWORD), да и избавимся от ограничений на длину строки. Но как тогда формировать строку из списка типов, непонятно... Есть мысли? __>Если их не будет это плохо
Ну, я не про те мысли. Я еще подумал, что хорошо сделать вариант с частичной специализацией, точнее то, что от нее оставляет VC.
В релизе это все оптимизируется. А также это позволяет при помощи частичной специализации оптимизировать длину массива, не изменяя внешнего интерфейса.
Вот это фокус ну ни в какие ворота не лезет... Совершенно не переносим и работает исключительно на особенностях мелкомягкого препроцессора.
Посмотри boost preprocessor думаю тебя устроят макросы из серии BOOST_PP_ARRAY* или BOOST_PP_SEQ*
#define SEQ (B)(O)(O)(S)(T)
BOOST_PP_SEQ_ENUM(SEQ) // expands to B, O, O, S, T
#define SEQ (a)(b)(c)
BOOST_PP_SEQ_SIZE(SEQ) // expands to 3
WH>Вот это фокус ну ни в какие ворота не лезет... Совершенно не переносим и работает исключительно на особенностях мелкомягкого препроцессора. WH>Посмотри boost preprocessor думаю тебя устроят макросы из серии BOOST_PP_ARRAY* или BOOST_PP_SEQ* WH>
WH>#define SEQ (B)(O)(O)(S)(T)
WH>BOOST_PP_SEQ_ENUM(SEQ) // expands to B, O, O, S, T
WH>
WH>[ccode] WH>#define SEQ (a)(b)(c)
Не хотелось бы тянуть сюда еще буст. Может, есть еще какие варианты?
А еще хочется немного оптимизировать выделяемую статически память под NameId. Теперь тот кусок кода выглядит так: http://www.rsdn.ru/Forum/Message.aspx?mid=751529&only=1
Так получается, что всегда выделяется по 33 байта, что неинтересно. Очевидно, частичной специализацией можно было бы легко это соптимизировать, но под VC6 ее, как известно, нет, а как использовать эмуляцию в этом случае я никак не могу придумать, кроме как наплодить множество вложенных структур, что неинтересно, да и много N=sum(1..8). Есть мысли?
Здравствуйте, Andrew S, Вы писали:
AS>Так получается, что всегда выделяется по 33 байта, что неинтересно. Очевидно, частичной специализацией можно было бы легко это соптимизировать, но под VC6 ее, как известно, нет, а как использовать эмуляцию в этом случае я никак не могу придумать, кроме как наплодить множество вложенных структур, что неинтересно, да и много N=sum(1..8). Есть мысли?
Нет, не то. Проинициализируйте буфер статически. Иначе в чем смысл, затраты кода будут больше неиспользуемой части буфера. Я привел вариант, но он ужасен, но там все в компиле-тайм. Другого я придумать не смог .
AS>>Так получается, что всегда выделяется по 33 байта, что неинтересно. Очевидно, частичной специализацией можно было бы легко это соптимизировать, но под VC6 ее, как известно, нет, а как использовать эмуляцию в этом случае я никак не могу придумать, кроме как наплодить множество вложенных структур, что неинтересно, да и много N=sum(1..8). Есть мысли? WH>
Здравствуйте, Andrew S, Вы писали:
AS>Нет, не то. Проинициализируйте буфер статически. Иначе в чем смысл, затраты кода будут больше неиспользуемой части буфера. Я привел вариант, но он ужасен, но там все в компиле-тайм. Другого я придумать не смог .
Немного правок из предложенных выше и своих + маленькие багфиксы.
Определения макросов и прокси для числа параметров > 1 не приведены из принципа
Как обычно — жду предложений\поправок.
Здравствуйте, Andrew S, Вы писали:
AS>Немного правок из предложенных выше и своих + маленькие багфиксы. AS>Определения макросов и прокси для числа параметров > 1 не приведены из принципа AS>Как обычно — жду предложений\поправок.
AS>
<skip>
AS>
Пока что придирки к CNameIdHolder.
Вы действительно считаете что такая специализация выглядит красиво ?
Лучше уж сделать собственный велосипед на макросах, чем городить это все, вдобавок код у вас не расширяем, т.е. если я захочу, чтобы было не 7, а 8 параметров то мне надо очень много дописывать.
__>Пока что придирки к CNameIdHolder. __>Вы действительно считаете что такая специализация выглядит красиво ? __>Лучше уж сделать собственный велосипед на макросах, чем городить это все, вдобавок код у вас не расширяем, т.е. если я захочу, чтобы было не 7, а 8 параметров то мне надо очень много дописывать.
Параметров и так 8, а CNameIdHolder и не должен быть расширяем — расширяем CNameId, именно его и надо менять, если надо более длинное имя. Специализация вполне красивая, хотя и не маленькая (впрочем, можно и без нее, но так экономится размер бинарника), и макрос тут тоже лучше, чем набор отдельных классов — "не плодите сущности без необходимости", их уже и так много
Имхо, главная проблема не в этом, а в том, как удобнее пользовать основные макросы, где требуется переменное число параметров + как красивее определять макросы для различного числа параметров функций.
А вообще, откровенно говоря, мне не понятна логика комитета, который не предусмотрел специализацию строковыми литералами.
Здравствуйте, Andrew S, Вы писали:
__>>Пока что придирки к CNameIdHolder. __>>Вы действительно считаете что такая специализация выглядит красиво ? __>>Лучше уж сделать собственный велосипед на макросах, чем городить это все, вдобавок код у вас не расширяем, т.е. если я захочу, чтобы было не 7, а 8 параметров то мне надо очень много дописывать.
AS>Параметров и так 8, а CNameIdHolder и не должен быть расширяем — расширяем CNameId, именно его и надо менять, если надо более длинное имя. Специализация вполне красивая, хотя и не маленькая (впрочем, можно и без нее, но так экономится размер бинарника), и макрос тут тоже лучше, чем набор отдельных классов — "не плодите сущности без необходимости", их уже и так много AS>Имхо, главная проблема не в этом, а в том, как удобнее пользовать основные макросы, где требуется переменное число параметров + как красивее определять макросы для различного числа параметров функций.
Зато в моем варианте не требуется переписывать для каждой специализации код.
AS>А вообще, откровенно говоря, мне не понятна логика комитета, который не предусмотрел специализацию строковыми литералами.
Потому что массивом нельзя специализировать шаблон, а строки это массив символов.
Видимо реализация этого довольно сложна.
__>Зато в моем варианте не требуется переписывать для каждой специализации код.
Ваш вариант вообще работать не будет, он определяет массив из 8-ми TCHAR Забудьте про эту мысль, она плохая, уверяю вас, вы РОВНЫМ счетом ничего не съэкономите, а наоборот.
AS>>А вообще, откровенно говоря, мне не понятна логика комитета, который не предусмотрел специализацию строковыми литералами. __>Потому что массивом нельзя специализировать шаблон, а строки это массив символов. __>Видимо реализация этого довольно сложна.
Здравствуйте, Andrew S, Вы писали:
__>>Зато в моем варианте не требуется переписывать для каждой специализации код.
AS>Ваш вариант вообще работать не будет, он определяет массив из 8-ми TCHAR Забудьте про эту мысль, она плохая, уверяю вас, вы РОВНЫМ счетом ничего не съэкономите, а наоборот.
AS>>>А вообще, откровенно говоря, мне не понятна логика комитета, который не предусмотрел специализацию строковыми литералами. __>>Потому что массивом нельзя специализировать шаблон, а строки это массив символов. __>>Видимо реализация этого довольно сложна.
AS>Как раз массивом специализировать шаблон можно.
AS>А вот сроковым литералом, который имеет internal linkage и тип const TCHAR[] — нельзя.
Потому что это внешняя линковка, а все что внутреняя нельзя использовать.
__>>И не будет проблем с разложением DWORD.
AS>Проблем и так нет, это вы их придумали А запись имени получится в 2.5 раза длиннее и еще непонятнее.
Я люблю придумывать проблемы и героически их решать
AS>>А вот сроковым литералом, который имеет internal linkage и тип const TCHAR[] — нельзя. __>Потому что это внешняя линковка, а все что внутреняя нельзя использовать.
Это все понятно.
__>Зато можно сделать такое :
В VC6 — нельзя. Можно только без спецификатора const. __>
Будут проблемы другого плана — в каждом модуле линковки будет своя таблица (поскольку специализации, в данном случае адреса массивов), будут различаться. А нам то нужно как раз обратно — обеспечить кросс-модульность.
__>Или может лучше сделать через Traits ?
__>
Аналогичная проблема, только, за исключением того, что придется делать трейты для всех функций, библиотеку и т.п. что используются в программе, выносить их в отдельный заголовочный файл, а это убивает изначальную гибкость конструкции.
Что делает CNameIdHolder ?
Создает строку из нескольких двойных слов, вместо этого можно использовать обычные символы, и тогда не нужен будет макрос FLATTEN_STR, зато писать надо немного утомительно :
А в С++ так всегда — когда кажется, что проблем нет, то это только кажется.
__>А теперь сначала все
__>Что делает CNameIdHolder ? __>Создает строку из нескольких двойных слов, вместо этого можно использовать обычные символы, и тогда не нужен будет макрос FLATTEN_STR, зато писать надо немного утомительно :
Нет, он статически инициализирует массив _подходящей_ длины. В вашем случае вам придется делать 32 специализации, иначе получите большой оверхед по длине массива. да и запись каждого символа по-отдельности это совсем некрасиво.
Здравствуйте, Andrew S, Вы писали:
__>>Проблемы одни
AS>А в С++ так всегда — когда кажется, что проблем нет, то это только кажется.
__>>А теперь сначала все
__>>Что делает CNameIdHolder ? __>>Создает строку из нескольких двойных слов, вместо этого можно использовать обычные символы, и тогда не нужен будет макрос FLATTEN_STR, зато писать надо немного утомительно :
AS>Нет, он статически инициализирует массив _подходящей_ длины. В вашем случае вам придется делать 32 специализации, иначе получите большой оверхед по длине массива.
Оверхед, да будет, но не настолько большой.
Допустип ограничимся 32 символами, на 100 классов будет максимальный оверхед в 32*100 = 3,2кБ, я не думаю что для программы, размер которой несколько сот кБ это критично. AS>да и запись каждого символа по-отдельности это совсем некрасиво.
А в оригинале разве не так ?
В крайнем случае сделаем макросы аналоги в boost-е и воспользуемся ими
__>Допустип ограничимся 32 символами, на 100 классов будет максимальный оверхед в 32*100 = 3,2кБ, я не думаю что для программы, размер которой несколько сот кБ это критично.
Может и не критично, но тогда в чем смысл всей этой городни с синглтонами и т.п. Если есть возможность делать оптимальнее при разработке библиотеки — это надо сделать.
AS>>да и запись каждого символа по-отдельности это совсем некрасиво. __>А в оригинале разве не так ?
Нет, не так Некрасиво — да, но все-таки не по-одному, а пачками по четыре Хотя, конечно, уродство все это
__>В крайнем случае сделаем макросы аналоги в boost-е и воспользуемся ими
Мне — почти никак, он на VC6 компилится не будет, как я понял — там у вас частичная специализация?
В любом случае, это почти ничего не меняет, ну, кроме конечно размеров исходников, тем более, как я говорил, мне мысль использовать односимвольные числовые литералы почему то не нравится, хотя начиналось все именно так.
На самом деле, наверное, лучше подумать над более глобальной проблемой — как удобнее сделать макросы
USE_MODULE_BEGIN и DECLARE_FUN_P1 в части задания имени. То, что есть сейчас, явно неудобно...
Здравствуйте, Andrew S, Вы писали:
__>>Как вам такой вариант :
AS>[skipped]
AS>Мне — почти никак, он на VC6 компилится не будет, как я понял — там у вас частичная специализация?
Забыл про VC6
А чем вам оверхед мешает ?
Зато все строки будут одинаковой длинны AS>В любом случае, это почти ничего не меняет, ну, кроме конечно размеров исходников, тем более, как я говорил, мне мысль использовать односимвольные числовые литералы почему то не нравится, хотя начиналось все именно так.
Односимвольные литералы лучше, так как позволяют избежать проблемы вроде этой :
CModule<'some','tex','t'>
Мне кажется что стоит сделать какой-нибудь класс для преобразования строки в односимвольные литералы, и тогда проблем не будет.
template<TCHAR c0,TCHAR c1, ... >
struct CNameID
{...};
template<typename T,TCHAR str[]>
class str_to_char
{
typedef T<str[0],str[1],...> type; // как правильно не знаю :xz:
};
Надеюсь идея ясна.
AS>На самом деле, наверное, лучше подумать над более глобальной проблемой — как удобнее сделать макросы AS>USE_MODULE_BEGIN и DECLARE_FUN_P1 в части задания имени. То, что есть сейчас, явно неудобно...
Если решить проблемы выше то это отпадет само собой.
Ну, и подумайте, что вы предлагаете Хинт — при инстанцировании класса CNameID параметрами должны быть символьные литералы (то бишь константы).
AS>>На самом деле, наверное, лучше подумать над более глобальной проблемой — как удобнее сделать макросы AS>>USE_MODULE_BEGIN и DECLARE_FUN_P1 в части задания имени. То, что есть сейчас, явно неудобно... __>Если решить проблемы выше то это отпадет само собой.
Да, если решить проблемы Я (и не только я) пробовал, не получилось. Точнее, получилось то, что получилось.
Здравствуйте, Andrew S, Вы писали:
__>>Мне кажется что стоит сделать какой-нибудь класс для преобразования строки в односимвольные литералы, и тогда проблем не будет.
__>>
__>>Надеюсь идея ясна.
AS>Ну, и подумайте, что вы предлагаете Хинт — при инстанцировании класса CNameID параметрами должны быть символьные литералы (то бишь константы).
Инстанирование будет литералами, а извне это будет в виде массива.
AS>>>На самом деле, наверное, лучше подумать над более глобальной проблемой — как удобнее сделать макросы AS>>>USE_MODULE_BEGIN и DECLARE_FUN_P1 в части задания имени. То, что есть сейчас, явно неудобно... __>>Если решить проблемы выше то это отпадет само собой.
AS>Да, если решить проблемы Я (и не только я) пробовал, не получилось. Точнее, получилось то, что получилось.
Как только доберусь нормально до компьютера попробую что-нибудь сотворить
AS>>Ну, и подумайте, что вы предлагаете Хинт — при инстанцировании класса CNameID параметрами должны быть символьные литералы (то бишь константы). __>Инстанирование будет литералами, а извне это будет в виде массива.
Не получится. Надо инстанцировать константами, а вы предлагаете инстанцировать фактически содержимым ячеек памяти, что на этапе компиляции компилеру не известно (ведь темплайт в данном случае инстанцируется адресом, к сожалению, а не содержимым).
AS>>>>На самом деле, наверное, лучше подумать над более глобальной проблемой — как удобнее сделать макросы AS>>>>USE_MODULE_BEGIN и DECLARE_FUN_P1 в части задания имени. То, что есть сейчас, явно неудобно... __>>>Если решить проблемы выше то это отпадет само собой.
AS>>Да, если решить проблемы Я (и не только я) пробовал, не получилось. Точнее, получилось то, что получилось. __>Как только доберусь нормально до компьютера попробую что-нибудь сотворить
Здравствуйте, Andrew S, Вы писали:
AS>>>Ну, и подумайте, что вы предлагаете Хинт — при инстанцировании класса CNameID параметрами должны быть символьные литералы (то бишь константы). __>>Инстанирование будет литералами, а извне это будет в виде массива.
AS>Не получится. Надо инстанцировать константами, а вы предлагаете инстанцировать фактически содержимым ячеек памяти, что на этапе компиляции компилеру не известно (ведь темплайт в данном случае инстанцируется адресом, к сожалению, а не содержимым).
Вы меня не так поняли
Ну ладно это не важно, я попробую переработать это для совместимости с VC 6 и выложу на форум.
Главное ведь результат
AS>>>>>На самом деле, наверное, лучше подумать над более глобальной проблемой — как удобнее сделать макросы AS>>>>>USE_MODULE_BEGIN и DECLARE_FUN_P1 в части задания имени. То, что есть сейчас, явно неудобно... __>>>>Если решить проблемы выше то это отпадет само собой.
AS>>>Да, если решить проблемы Я (и не только я) пробовал, не получилось. Точнее, получилось то, что получилось. __>>Как только доберусь нормально до компьютера попробую что-нибудь сотворить
Большое спасибо _nn_ за проявленную инициативу, в связи с чем выкладываю обновление до v 1.03
Специализация выполнена на макросах, максимальная длина строки увеличена до 64 символов, можно без особых изменений в коде и больше.
На макросы определения функций терпения не хватило, а common вариант не придумался — если есть мысли, будет интересно выслушать.
И, как обычно — если есть какие предложения (ну, кроме использования буста, тут это как из пушки по воробьям) — u are welcome.
Здравствуйте, Andrew S, Вы писали:
AS>Большое спасибо _nn_ за проявленную инициативу, в связи с чем выкладываю обновление до v 1.03 AS>Специализация выполнена на макросах, максимальная длина строки увеличена до 64 символов, можно без особых изменений в коде и больше. AS>На макросы определения функций терпения не хватило, а common вариант не придумался — если есть мысли, будет интересно выслушать.
Есть идея, но она мне кажется неосуществима.
Что-то такое :
FunProxyThrowPolicy использует тоже CString, однако он выкидывает это как исключение.
Поэтому пользователю придется заботиться об уничтожении выделенной памяти или же сделать обертку для этого.
А зачем это предлагается, я что то не понял.
__>Вместо _T('\0') лучше писать просто 0, меньше проблем, вдобавок макрос _T определяется в commctrl.h, а если он не будет подключен то будут проблемы.
Нет, это в tchar.h, и проблем не будет
__>Есть еще предложение убрать CString из CDynFunException, таким образом сделать так чтобы можно было использовать код в программах WinAPI. __>
new может сам вызывать эксепшен — начинаем тихо форматировать диск Конечно, лучше использовать malloc\free.
__>FunProxyThrowPolicy использует тоже CString, однако он выкидывает это как исключение. __>Поэтому пользователю придется заботиться об уничтожении выделенной памяти или же сделать обертку для этого.
Там можно вообще выделять буфер не динамически, а на стеке — максимальный размер строки с именем функции известен в FunProxyThrowPolicy::Dummy::MakeReturn через DynFunction::name_type во время компиляции, размер буфера под целое тоже. Просто лень было (да и важнее проблемы были и есть) — я политики для исключений сделал больше просто для того, дабы показать, что очень просто расширить предлагаемый функционал.
Здравствуйте, Andrew S, Вы писали:
AS>А зачем это предлагается, я что то не понял.
Да так тупая затея
__>>Вместо _T('\0') лучше писать просто 0, меньше проблем, вдобавок макрос _T определяется в commctrl.h, а если он не будет подключен то будут проблемы.
AS>Нет, это в tchar.h, и проблем не будет
Да, там тоже есть, а если он не подключен что делать ?
Так что 0, а не _T('\0').
__>>Есть еще предложение убрать CString из CDynFunException, таким образом сделать так чтобы можно было использовать код в программах WinAPI. __>>
<skip>
__>>
AS>new может сам вызывать эксепшен — начинаем тихо форматировать диск Конечно, лучше использовать malloc\free.
Проверку исключения я убрал, так как суть не в нем.
А вообще malloc подойдет конечно
Будет такое :
__>>FunProxyThrowPolicy использует тоже CString, однако он выкидывает это как исключение. __>>Поэтому пользователю придется заботиться об уничтожении выделенной памяти или же сделать обертку для этого.
AS>Там можно вообще выделять буфер не динамически, а на стеке — максимальный размер строки с именем функции известен в FunProxyThrowPolicy::Dummy::MakeReturn через DynFunction::name_type во время компиляции, размер буфера под целое тоже. Просто лень было (да и важнее проблемы были и есть) — я политики для исключений сделал больше просто для того, дабы показать, что очень просто расширить предлагаемый функционал.
Ясно.
Пофикшен баг с FLATTEN_STR
Изменены размножающие макросы — теперь удобнее и больше возможностей
С учетом возвращаемого типа void переработаны политика исключений и прокси вызова
В соответствии с пожеланиями телезрителей CString отправлен в dev\null, его в проекте больше нет
Немного причесан код для большего соответствия ANSI, где это возможно
To do
Компилеро-независимый способ задания строк, сейчас работает только на линейке VC
Автоматический генератор макросов DECLARE_FUN_XXX
Что то еще?
Как обычно — ждем сообщений о багах, предложений по фичам и прочее-прочее.
Здравствуйте, Andrew S, Вы писали:
AS>Собственно, версия 1.04
AS>Список изменений:
AS>
AS>В соответствии с пожеланиями телезрителей CString отправлен в dev/null, его в проекте больше нет AS>
Наверное все же стоит в структуре CDynFunException член m_sMessage сделать приватным, мало ли что будет
AS>Компилеро-независимый способ задания строк, сейчас работает только на линейке VC AS>Автоматический генератор макросов DECLARE_FUN_XXX AS>Что то еще? AS>
Компиляторонезависимый будет так : (при потере функциональности )
Есть идея, только не знаю если она переносима :
Если написать #define X(a0,a1,a2....an), по при использовании X(a) в a1, a2 будет пусто.
Это на VC++, если на других компиляторах также то проблем нет.
>>Компилеро-независимый способ задания строк, сейчас работает только на линейке VC
По следам буста изменил способ задания строк. Теперь декларации выглядят так:
Но в этом случае непонятно, как быть с юникодом... А хотя, есть мысль — ведь если в этом случае писать kernel::GetModuleHandle, то препроцессор это на основе стандартных хидеров фактически сам преобразует в нужный вариант — либо kernel::GetModuleHandleA, либо kernel::GetModuleHandleW. Фактически бесплатная поддержка юникода. Как господам такая мысль? Если понравится, сделаем так. В результате плюсы — сокращенная запись + полуавтоматический юникод.
Да, из todo остается: >>Автоматический генератор макросов DECLARE_FUN_XXX
Как можно заметить код разделяется на два куска — это макросы для повоторения и сам код для загрузки библиотек.
Может стоит выделить для макросов отдельную ветку ?
Все же стоит заменить DWORD, на TCHAR, так нельзя будет ошибиться в написани, вдобавок читать приятней
__>Как можно заметить код разделяется на два куска — это макросы для повоторения и сам код для загрузки библиотек. __>Может стоит выделить для макросов отдельную ветку ?
Может и стоит. У вас есть предложения по макросам? У меня — есть. Например, было бы интересно придумать макрос для разбиения идентификатора на квартеты (или отдельные символы). Тогда можно было бы вообще в нативном синтаксисе писать декларации функций. Конечно, это невозможно скорее всего, но тем не менее. Ну, и конечно, хорошо бы автоматизировать процесс задания макросов декларации функций. Я не смог придумать, как это сделать.
__>Все же стоит заменить DWORD, на TCHAR, так нельзя будет ошибиться в написани, вдобавок читать приятней
Мы уже это обсуждали, мое мнение, что не стоит — слишком длинная запись. Впрочем, это не суть важно.
Меня вот что инетерсует — ваше мнение по поводу деклараций функций в сокращенном виде — делать или нет? Мне думается, что в сокращенном виде особой функциональности не теряется, писать текста меньше почти в 2 раза.
Здравствуйте, Andrew S, Вы писали:
__>>Как можно заметить код разделяется на два куска — это макросы для повоторения и сам код для загрузки библиотек. __>>Может стоит выделить для макросов отдельную ветку ?
AS>Может и стоит. У вас есть предложения по макросам? У меня — есть. Например, было бы интересно придумать макрос для разбиения идентификатора на квартеты (или отдельные символы). Тогда можно было бы вообще в нативном синтаксисе писать декларации функций. Конечно, это невозможно скорее всего, но тем не менее. Ну, и конечно, хорошо бы автоматизировать процесс задания макросов декларации функций. Я не смог придумать, как это сделать.
Идея :
#define DECLARE_FUN(Name, NameId, R, P1, COMMA1, P2, COMMA2, P3, COMMA3, ...) \
static R (WINAPI *&Name)(P1) = CDynFunction<module_type, CNameId<DL_SEQ_ENUM(NameId)>, FUN_PROXY(1)<R, P1 COMMA1 P2 COMMA2 ...> >::GetProxy(); // повторение здесь можно через макрос оформить.
использование :
// до
DECLARE_FUN_P1(GetModuleHandle, (GetM)(odul)(eHan)(dleA), HMODULE, LPCTSTR)
// после
DECLARE_FUN(GetModuleHandle, (GetM)(odul)(eHan)(dleA), HMODULE, LPCTSTR, EMPTY, EMPTY ... )
// EMPTY.. можно тоже через макрос оформить - REPEAT_N((сколько надо),EMPTY)
Не очень переносимый вариант :
DECLARE_FUN(GetModuleHandle, (GetM)(odul)(eHan)(dleA), HMODULE, LPCTSTR)
// будет много предупреждений, однако код сработает нормально
__>>Все же стоит заменить DWORD, на TCHAR, так нельзя будет ошибиться в написани, вдобавок читать приятней
AS>Мы уже это обсуждали, мое мнение, что не стоит — слишком длинная запись. Впрочем, это не суть важно.
А что насчет проблемы упомянутой выше ? AS>Меня вот что инетерсует — ваше мнение по поводу деклараций функций в сокращенном виде — делать или нет? Мне думается, что в сокращенном виде особой функциональности не теряется, писать текста меньше почти в 2 раза.
Не понял про что вы.
А почему бы не сделать так:
#define DECLARE_FUN(name1,name2,name3....) \
CAT(name1,CAT(name2,CAT(name3... ))) // ну или через макрос как-то
Изменены порождающие макросы. Теперь для задания функци с любым (DL_MAX_REPEAT) числом параметров используется один тип макросов — DL_DECLARE_FUN_XXX
Изменены названия порождающих макросов
Изменено количество параметров в макросах DL_DECLARE_FUN_XXX — теперь параметр Name убран за ненадобностью
To do
Поддержка MT.
Поддержка соглашений о вызовах кроме __stdcall.
Поддержка функций с переменным числом параметров.
Что то еще?
Как обычно — ждем сообщений о багах, предложений по фичам и прочее-прочее.
Зачем, скажи мне, тут вообще потребовались шаблоны для генерации имён ? Ведь макросы сплошные — сделай тогда уж всё на макросах. Я бы генерил макросом функцию, с указаной сигнатурой и именем, а внутри бы имел статический указатель, который бы и заполнял в момент первого вызова. Небольшой оверхед будет, конечно, но зато можно будет писать GetProcAddress вместо (GetP)(rocA)(ddre)(ss).
CAM>Зачем, скажи мне, тут вообще потребовались шаблоны для генерации имён ? Ведь макросы сплошные — сделай тогда уж всё на макросах. Я бы генерил макросом функцию, с указаной сигнатурой и именем, а внутри бы имел статический указатель, который бы и заполнял в момент первого вызова. Небольшой оверхед будет, конечно, но зато можно будет писать GetProcAddress вместо (GetP)(rocA)(ddre)(ss).
Наверное, вам надо посмотреть хотя бы немного первый вариант кода (который практически без макросов) . Там ясно видно — почему и как. Макросы в дальнейшем появились _только_ для удобства записи.
CAM>>Зачем, скажи мне, тут вообще потребовались шаблоны для генерации имён ? Ведь макросы сплошные — сделай тогда уж всё на макросах. Я бы генерил макросом функцию, с указаной сигнатурой и именем, а внутри бы имел статический указатель, который бы и заполнял в момент первого вызова. Небольшой оверхед будет, конечно, но зато можно будет писать GetProcAddress вместо (GetP)(rocA)(ddre)(ss).
AS>Наверное, вам надо посмотреть хотя бы немного первый вариант кода (который практически без макросов) . Там ясно видно — почему и как. Макросы в дальнейшем появились _только_ для удобства записи.
А хотя, вкупе с предложение WolfHound можно попробовать и так... и оверхеда не будет. Вот только кросс-модульность. Сейчас попробуем.
WH>У меня тут такая мысль возникла а что если в место
[skipped]
Super! А кросс-модульность будет работать? Т.е. будем ли мы получать один прокси в результате специализаций одинаковыми NameId из разных модулей в этом случае?
(P.S. только что попробовал — все работает, как и следовало ожидать).
Здравствуйте, Andrew S, Вы писали:
AS>Super! А кросс-модульность будет работать? Т.е. будем ли мы получать один прокси в результате специализаций одинаковыми NameId из разных модулей в этом случае?
1)Что ты понимаешь под модулем?
2)По идеи этот вариант должен работать также как и твой.
... << RSDN@Home 1.1.4 rev. 142 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
AS>>Super! А кросс-модульность будет работать? Т.е. будем ли мы получать один прокси в результате специализаций одинаковыми NameId из разных модулей в этом случае? WH>1)Что ты понимаешь под модулем?
Различные единицы компиляции. Уже проверил — все нормально, одинаковые прокси из разных единиц компиляции находятся после линка по одним адресам.
WH>2)По идеи этот вариант должен работать также как и твой.
Так и работает. Еще раз спасибо — самое очевидно решение я и не заметил. А ведь в первоначальном виде (без темплайтов) у нас идентификатор имени именно так и определялся — при помощит подобных макросов
В общем, похоже мы нашли вариант специализировать шаблоны константной строкой, правда, при маленькой помощи препроцессора
Убраны ненужные теперь макросы DL_SEQ_ELEM и многие другие — макромагии стало меньше, совместимость с препроцессорами выше.
To do
Поддержка MT.
Поддержка соглашений о вызовах кроме __stdcall.
Поддержка функций с переменным числом параметров.
Что то еще?
Да, хорошо бы проверить все это на VC7-7.1... У меня, к сожалению, этих компиляторов нет.
Наверняка, если и не будет компилироваться, можно будет легко устранить причину — ничего особо страшного в коде нет.
В принципе вся "опасная" часть находится внутри InitFunction (я ничего не пропустил?). Самое простое это обернуть её в CriticalSection. Это тормознёт все потоки — что плохо. С другой стороны такой казалось бы легковесный объект как event отожрал у меня около 16 тыс тиков.
for (int index = 0; index < 100000; index++)
{
CloseHandle(CreateEvent(NULL, FALSE, FALSE, _T("EVENT")));
}
Mutex отнял приблизительно столько же времени. На этом фоне очень привлекательны критические секции. Они судя по всему раза в 4 быстрее.
Хотя вобщем-то не известно какие потоки запускает DllMain и как отразиться на программе то, что LoadLibrary вызван из критической секции.
Поддержка соглашений о вызовах кроме __stdcall.
Поддержка функций с переменным числом параметров.
ИМХО только с привлечением ассемблера. Надо будет ProxyFun переписать.
Собственно релиз в VC7.1 как раз и представляет собой то, что надо (параметры шаблонов опущены)
call CDynFunction<>::InitFunction (401730h)
test eax,eax
je error:
jmp dword ptr [`CDynFunction<>::GetProxy'::`2'::proxy]
error:
call FunProxyThrowPolicy<>::MakeReturnImpl
Как видишь этот код не завязан ни на количество параметров ни на конвенцию вызова.
P.S. Вызов через такую обёртку даже быстрее вызова через таблицу импорта. Вот я и думаю, а не создать ли продвинутый файл windows (как string.h -> string) в котором все функции будут раскиданы по пространствам имён? Как побочный эффект своего рода антиотладка (передаю привер шароварщикам ) Эдакая МЕГАИДЕЯ
FARPROC GetProcAddress(
HMODULE hModule, // handle to DLL module
LPCSTR lpProcName // function name
);
HMODULE LoadLibrary(
LPCTSTR lpFileName // file name of module
);
(C)MSDN
... << RSDN@Home 1.1.4 rev. 142 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
A>Поддержка MT.
A>В принципе вся "опасная" часть находится внутри InitFunction (я ничего не пропустил?). Самое простое это обернуть её в CriticalSection. Это тормознёт все потоки — что плохо. С другой стороны такой казалось бы легковесный объект как event отожрал у меня около 16 тыс тиков.
Верно, но не совсем. На самом деле там 2 места в одной функции — собственно, сама функция и синглтон модуля. Т.е. надо будет по одному объекту синхронизации на модуль + по одному на каждую функцию. На первый взгляд, печально — но на самом деле, все несколько лучше — можно это все обернуть в lightweight mutex'ы — пример приводил WolfHound. Это обеспечит помимо простоты синхронизации еще и всего 4 байта оверхеда на функцию и модуль в MT версии, что имхо немного. Можно вообще LW мутекс привязать только к модулю или же к классу функций (например, сделать его параметр статическим синглтоном без конструктора, как и прокси, по параметру CDynFunction::Name или еще как — надо подумать. В общем, варианты есть, и не такие уж сложные\медленные, тем более, что все это только один раз вызываться будет).
Вообще, на крайний случай — можно засинхронизировать только синглтон модулей, а сама функция на самом деле вполне реенернабельна.
A>Поддержка соглашений о вызовах кроме __stdcall. A>Поддержка функций с переменным числом параметров.
Тут придется поправить макросы. Но я подумал — что то расхотелось мне это делать. Не так и много целевых функций имеет отличные от STDCALL конвенции, к тому же в основном это _cdecl с переменным числом параметров, а их все равно легально кроме как через V версии не передать — а они все WINAPI. Т.е. практическая необходимость сомнительна. Поэтому я (пока?) решил на это забить.
Ага. Я решил во избежание дублирования анси и не анси строк в юникодной версии (наверное, компиляторы выкинут ненужный код, но лучше не рисковать) сделать так:
A>Я тут взял на себя наглость прошёлся по коду regex'пами и проч. Изменения под Unicode и под VC7.1 внесены.
Не, мне такое форматирование не нравится Не по стандарту (нашему, по крайней мере). Одни отсупы {} чего стоят. Да и переименование макроса CAT в MERGE тоже не по стандарту — в бусте именно CAT и хочется этому хоть как то соответствовать. Опять же, у меня еще несколько изменений касательно авто-поддержки юникода (да, да, именно авто поддержки, на основе заголовков от обычных, статически линкуемых бибилотек), которые я тут еще не отражал. В общем, думаю, пока что не стоит форматировать сурсы (по крайней мере публично — локально можно конечно делать что угодно ) — бесполезный труд, у меня еще слишком много чего есть, и конкатенировать после таких изменений сложно Но в любом случае — спасибо за попытку!
Здравствуйте, Andrew S, Вы писали:
AS>Да и переименование макроса CAT в MERGE тоже не по стандарту — в бусте именно CAT и хочется этому хоть как-то соответствовать.
Это совершенно не соответствует англо-русскому словарю В доке к ## preprocessor operator так и написано
The double-number-sign or "token-pasting" operator (##), which is sometimes called the "merging" operator, is used in both object-like and function-like macros.
А о чём думали в бусте я не знаю Cat это кошка Я кошек люблю, но здесь они не к месту.
AS>Опять же, у меня еще несколько изменений касательно авто-поддержки юникода (да, да, именно авто поддержки, на основе заголовков от обычных, статически линкуемых бибилотек), которые я тут еще не отражал.
Что ещё туда добавлять? Func на FuncA или FuncW будет заменятся вне зависимости он нашего желания. Со структурами тоже самое. В unicode я это дело уже компилировал (1.06б) всё ОК.
Я что-то упустил?
AS>В общем, думаю, пока что не стоит форматировать сурсы (по крайней мере публично — локально можно конечно делать что угодно ) — бесполезный труд, у меня еще слишком много чего есть, и конкатенировать после таких изменений сложно Но в любом случае — спасибо за попытку!
AS>>Да и переименование макроса CAT в MERGE тоже не по стандарту — в бусте именно CAT и хочется этому хоть как-то соответствовать.
A>Это совершенно не соответствует англо-русскому словарю В доке к ## preprocessor operator так и написано
Соответствует. A>А о чём думали в бусте я не знаю Cat это кошка Я кошек люблю, но здесь они не к месту.
Это сокращение от concatenate и достаточно точно отражает смысл, как мне кажется В отличие от MERGE — которое значит совмещать, но не соединять. Вообше, merge на мой взгляд больше применим к множествам, а конкатинация — к последовательностям.
AS>>Опять же, у меня еще несколько изменений касательно авто-поддержки юникода (да, да, именно авто поддержки, на основе заголовков от обычных, статически линкуемых бибилотек), которые я тут еще не отражал.
A>А что ещё? Там же макросы типа A>
A>Что ещё туда добавлять? Func на FuncA или FuncW будет заменятся вне зависимости он нашего желания. Со структурами тоже самое. В unicode я это дело уже компилировал (1.06б) всё ОК. A>Я что-то упустил?
Конечно. В текущей версии DECLARE_NAME_ID(NameId, #NameId) раскроет LoadLibrary именно в "LoadLibrary" вне зависимости от нашего (не)желания. AS>>В общем, думаю, пока что не стоит форматировать сурсы (по крайней мере публично — локально можно конечно делать что угодно ) — бесполезный труд, у меня еще слишком много чего есть, и конкатенировать после таких изменений сложно Но в любом случае — спасибо за попытку!
A>Отступы только делай табуляцией.
Дык я вроде ей и делаю (точнее, VAssist делает). Если где что и упустил — или по своему недосмотру, или спостил изменения с форума. Или ты имеешь ввиду отсуп между типом и именем определения (ну или директивой\именем)? Там да, я по-привычке ставлю пробелы. Попробую поправить.
Здравствуйте, Andrew S, Вы писали:
AS>Это сокращение от concatenate и достаточно точно отражает смысл, как мне кажется В отличие от MERGE — которое значит совмещать, но не соединять.
Скорее объединять.
AS>Вообше, merge на мой взгляд больше применим к множествам, а конкатинация — к последовательностям.
Ну я за имя не дерусь, используется этот макрос всё равно только в служебных целях. Просто для меня оно показалось непонятным.
AS>Конечно. В текущей версии DECLARE_NAME_ID(NameId, #NameId) раскроет LoadLibrary именно в "LoadLibrary" вне зависимости от нашего (не)желания.
Ну и? У одних функций есть A/W версии у других нету. ИМХО лучше сделать
DL_USE_MODULE_BEGIN(kernel, "kernel32.dll")
DL_DECLARE_FUN_DUAL(GetModuleHandle, HMODULE, (LPC??что здесь писать??STR))
DL_USE_MODULE_END
A>>Отступы только делай табуляцией. AS>Дык я вроде ей и делаю (точнее, VAssist делает).
Нет, это я зря ругаюсь. Если копировать с сайта, то по любому пробелы, даже если в исходном сообщении табуляция.
Единственный шанс получить текст с табуляцией это копировать из цитирования в форме ответа
Зато если в студии выделить всё и нажать Tab, Shift+Tab то всё будет как надо но это в 7.1, а в 6ке не знаю как.
A>Ну и? У одних функций есть A/W версии у других нету. ИМХО лучше сделать A>
A>DL_USE_MODULE_BEGIN(kernel, "kernel32.dll")
A> DL_DECLARE_FUN(GetModuleHandleA, HMODULE, (LPCSTR))
A> DL_DECLARE_FUN(GetModuleHandleW, HMODULE, (LPCWSTR))
A>DL_USE_MODULE_END
A>
чем A>
A>DL_USE_MODULE_BEGIN(kernel, "kernel32.dll")
A> DL_DECLARE_FUN_DUAL(GetModuleHandle, HMODULE, (LPC??что здесь писать??STR))
A>DL_USE_MODULE_END
A>
А здесь писать
DL_USE_MODULE_BEGIN(kernel, "kernel32.dll")
DL_DECLARE_FUN_DUAL(GetModuleHandle, HMODULE, (LPCTSTR))
DL_USE_MODULE_END
В этом то и фишка. На самом деле, за нас все сделает препроцессор, маленький трюк, как и с CAT, на самом деле — заставить его вывести макрос до дальнейшего препроцессирования.
A>>>Отступы только делай табуляцией. AS>>Дык я вроде ей и делаю (точнее, VAssist делает).
A>Нет, это я зря ругаюсь. Если копировать с сайта, то по любому пробелы, даже если в исходном сообщении табуляция. A>Единственный шанс получить текст с табуляцией это копировать из цитирования в форме ответа A>Зато если в студии выделить всё и нажать Tab, Shift+Tab то всё будет как надо но это в 7.1, а в 6ке не знаю как.
У меня вроде как с табами копируется, а если целевой редактор понимает — то даже и RTF. VAssist — великая штука.
Здравствуйте, Andrew S, Вы писали:
AS>Список изменений:
AS>[list] AS>Изменены порождающие макросы (спасибо WolfHound). Теперь синтаксис стал гораздо более очевидным и нативным: AS>
Я не вижу проблемы с MT. Запись/чтение выравненного двойного слова атомарна, поэтому худшее, что может быть — лишний вызов GetProcAddress.
Ещё у меня предложение — вместо разных макросов, для разных способов обработки ошибок позволить в макросе указывать функцию-обработчик, с таким же возвращаемым значенем, как и генерируемая функция. Таким образом будет выбор, или возвратить что-то инвалидное, или сделать throw / RaiseException. Кроме того, исчезнет зависимость от sprintf'а. Конечно, указывать в каждой функции обработчик ошибок — накладно, пока могу предложить только "глобальный" обработчик, который будет указываться в DL_USE_MODULE_BEGIN.
Кстати, я вижу, что ошибки с LoadModule никак не обрабатываются, и единственный способ узнать, что dll'ки нет — это самостоятельно проверить CModule::GetModule на 0.
...
CAM>Кстати, я вижу, что ошибки с LoadModule никак не обрабатываются, и единственный способ узнать, что dll'ки нет — это самостоятельно проверить CModule::GetModule на 0.
AS>>To do
AS>>Поддержка MT.
CAM>Я не вижу проблемы с MT. Запись/чтение выравненного двойного слова атомарна, поэтому худшее, что может быть — лишний вызов GetProcAddress.
А я вижу. Читайте соотв. пост
CAM>Ещё у меня предложение — вместо разных макросов, для разных способов обработки ошибок позволить в макросе указывать функцию-обработчик, с таким же возвращаемым значенем, как и генерируемая функция. Таким образом будет выбор, или возвратить что-то инвалидное, или сделать throw / RaiseException. Кроме того, исчезнет зависимость от sprintf'а. Конечно, указывать в каждой функции обработчик ошибок — накладно, пока могу предложить только "глобальный" обработчик, который будет указываться в DL_USE_MODULE_BEGIN.
Политики удобнее. Это не только мое менение.
CAM>Кстати, я вижу, что ошибки с LoadModule никак не обрабатываются, и единственный способ узнать, что dll'ки нет — это самостоятельно проверить CModule::GetModule на 0.
Обрабатываются — вызывается соотв. политика ошибки. В ней есть доступ как к типу модуля, так и к типу функции, чего вполне досточно для формирования _любого_ сообщения об ошибке.
Здравствуйте, Andrew S, Вы писали:
AS>>>To do
AS>>>Поддержка MT.
CAM>>Я не вижу проблемы с MT. Запись/чтение выравненного двойного слова атомарна, поэтому худшее, что может быть — лишний вызов GetProcAddress.
AS>А я вижу. Читайте соотв. пост
Пожалуйста объясни, какие ты видишь проблемы с MT в функции InitFunction.
CAM>>Ещё у меня предложение — вместо разных макросов, для разных способов обработки ошибок позволить в макросе указывать функцию-обработчик, с таким же возвращаемым значенем, как и генерируемая функция. Таким образом будет выбор, или возвратить что-то инвалидное, или сделать throw / RaiseException. Кроме того, исчезнет зависимость от sprintf'а. Конечно, указывать в каждой функции обработчик ошибок — накладно, пока могу предложить только "глобальный" обработчик, который будет указываться в DL_USE_MODULE_BEGIN.
AS>Политики удобнее. Это не только мое менение.
Во-первых, они у тебя "зашиты", поэтому толку от них — 0. Я как раз и предлагаю позволить их указывать в макросе. Потому-что я, например, не хочу ловить твой CDynFunException, или вообще, у меня EH отключен, и я хочу кинуть виндовое исключение.
Во-вторых, даже если ты их позволишь указывать, то функции будут удобнее, потому-что "объектность" политики, наличие у неё состояния, как у объекта — не нужно. Функции тут самое то. Кроме того, меньше писать.
Насчёт полезности политик — я согласен, но в данном случае они лишние.
CAM>>Кстати, я вижу, что ошибки с LoadModule никак не обрабатываются, и единственный способ узнать, что dll'ки нет — это самостоятельно проверить CModule::GetModule на 0.
AS>Обрабатываются — вызывается соотв. политика ошибки. В ней есть доступ как к типу модуля, так и к типу функции, чего вполне досточно для формирования _любого_ сообщения об ошибке.
Даа, _любое_ сообщение можно сформировать. Вот только в MakeReturn непонятно, _что_ не так — то ли LoadLibrary не удался, и GetModule().m_hModule вернула 0, то ли функция не найдена и GetProcAddress вернула 0.
...
CAM>>Кстати, я вижу, что ошибки с LoadModule никак не обрабатываются, и единственный способ узнать, что dll'ки нет — это самостоятельно проверить CModule::GetModule на 0.
AS>>Обрабатываются — вызывается соотв. политика ошибки. В ней есть доступ как к типу модуля, так и к типу функции, чего вполне досточно для формирования _любого_ сообщения об ошибке.
Блин, опять что-то забыл.
Я имел в виду, что, например, если dllка не найдена (т.е. CModule::m_hModule==0), то может потребоваться вообще показать мессадж бокс и завершиться.
Кстати, как насчёт переименовать CModule::GetModule() в CModule::GetInstance(), и добавить
CAM>Пожалуйста объясни, какие ты видишь проблемы с MT в функции InitFunction.
Синглтон майерса не потокобезопасен. В данном случае он используется в CModule. Там можно немного и по-другому, но проблему это не снимет.
CAM>>>Ещё у меня предложение — вместо разных макросов, для разных способов обработки ошибок позволить в макросе CAM>Во-первых, они у тебя "зашиты", поэтому толку от них — 0. Я как раз и предлагаю позволить их указывать в макросе. Потому-что я, например, не хочу ловить твой CDynFunException, или вообще, у меня EH отключен, и я хочу кинуть виндовое исключение. CAM>Во-вторых, даже если ты их позволишь указывать, то функции будут удобнее, потому-что "объектность" политики, наличие у неё состояния, как у объекта — не нужно. Функции тут самое то. Кроме того, меньше писать.
CAM>Насчёт полезности политик — я согласен, но в данном случае они лишние.
Верно, насчет макроса, который позволит задавать политики, я как то забыл. Будет просто еще один макрос.
CAM>>>Кстати, я вижу, что ошибки с LoadModule никак не обрабатываются, и единственный способ узнать, что dll'ки нет — это самостоятельно проверить CModule::GetModule на 0.
AS>>Обрабатываются — вызывается соотв. политика ошибки. В ней есть доступ как к типу модуля, так и к типу функции, чего вполне досточно для формирования _любого_ сообщения об ошибке.
CAM>Даа, _любое_ сообщение можно сформировать. Вот только в MakeReturn непонятно, _что_ не так — то ли LoadLibrary не удался, и GetModule().m_hModule вернула 0, то ли функция не найдена и GetProcAddress вернула 0.
Анализируйте GetLastError(). Но я не думаю, что есть большая разница между ненахождение функции и незагрузкой модуля — технически это _одно_ неверное действие — невозможность отрезолвить функцию. Впрочем, вероятно, мысль сделать политики ошибки загрузки модуля тоже неплохая — если общественность поддержит, сделаю. Хотя, в этом случае будет возникать ситуация вызова аж 2-х политик в случае ошибки загрузки модуля, что на мой взгляд, неправильно. (технически это исправить несложно, но это возникает именно _логически_)
CAM>>?
CAM>>Мне кажется kernel::CModule::GetHandle() выглядит красивее, чем kernel::CModule().GetModule().m_hModule.
AS>Думаю, не стоит. Зачем посторонним доступ к хандлу модуля А вот сделать метод GetProcAddress и вызывать именно его — это то, что надо, на мой взгляд.
Я всё пытаюсь тебе сказать, что нужен способ узнать, что DLL не загрузилась. Если ты не хочешь сделать policy на этот случай — остаётся только проверка хэндла ручками. И по-моему, kernel::CModule::GetHandle() будет нагляднее.
Хотя даже сделай ты policy — всё равно нужно дать возможность проверять ручками, потому-что обрабатывать ошибку отсутствия DLLки скорее всего придётся штатно, в какой-нибудь InitInstance.
Здравствуйте, Andrew S, Вы писали:
CAM>>Пожалуйста объясни, какие ты видишь проблемы с MT в функции InitFunction.
AS>Синглтон майерса не потокобезопасен. В данном случае он используется в CModule. Там можно немного и по-другому, но проблему это не снимет.
Синглтон майерса — умное слово. У тебя есть HMODULE, который суть void*, и есть функции его проверки и установки. Они атомарны. Худшее, что может быть, опять — последовательный вызов LoadLibrary.
...
AS>Анализируйте GetLastError(). Но я не думаю, что есть большая разница между ненахождение функции и незагрузкой модуля — технически это _одно_ неверное действие — невозможность отрезолвить функцию. Впрочем, вероятно, мысль сделать политики ошибки загрузки модуля тоже неплохая — если общественность поддержит, сделаю. Хотя, в этом случае будет возникать ситуация вызова аж 2-х политик в случае ошибки загрузки модуля, что на мой взгляд, неправильно. (технически это исправить несложно, но это возникает именно _логически_)
Возможно, ты прав, на счёт небольшой разницы этих двух ситуаций, однако по-моему предпочтительнее дать пользователям возможность решать, что для них важно и нужно. Когда есть возможность, ей можно и не пользоваться, а вот когда её нет, и она нужна, то возникают проблемы.
CAM>>>Пожалуйста объясни, какие ты видишь проблемы с MT в функции InitFunction.
AS>>Синглтон майерса не потокобезопасен. В данном случае он используется в CModule. Там можно немного и по-другому, но проблему это не снимет.
CAM>Синглтон майерса — умное слово. У тебя есть HMODULE, который суть void*, и есть функции его проверки и установки. Они атомарны. Худшее, что может быть, опять — последовательный вызов LoadLibrary.
Ага, а регистрировать функцию для выполнения FreeLibrary (AtExit) вы будете ручками.
В общем, нет, все не то. Почитайте топик про синглтон майерса в форуме cpp и проникнитесь. До этого обсуждать что-либо про MT нет смысла.
AS>>Анализируйте GetLastError(). Но я не думаю, что есть большая разница между ненахождение функции и незагрузкой модуля — технически это _одно_ неверное действие — невозможность отрезолвить функцию. Впрочем, вероятно, мысль сделать политики ошибки загрузки модуля тоже неплохая — если общественность поддержит, сделаю. Хотя, в этом случае будет возникать ситуация вызова аж 2-х политик в случае ошибки загрузки модуля, что на мой взгляд, неправильно. (технически это исправить несложно, но это возникает именно _логически_)
CAM>Возможно, ты прав, на счёт небольшой разницы этих двух ситуаций, однако по-моему предпочтительнее дать пользователям возможность решать, что для них важно и нужно. Когда есть возможность, ей можно и не пользоваться, а вот когда её нет, и она нужна, то возникают проблемы.
НИКАКОЙ проблемы нет. Если m_hModule == 0 — значит, ошибка относится к загрузке модуля, иначе — к функции. Ну где здесь проблемы, не понимаю? Макрос, позволяющий задавать свою policy, я уже сделал.
__>Мне кажется в CNameId будет немного неправильные значения.
__>Решение 1 :
Если так написать, код просто не откомпилируется
error C2440: 'return' : cannot convert from 'unsigned short [13]' to 'const char *'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
Здравствуйте, Andrew S, Вы писали:
CAM>>>>Пожалуйста объясни, какие ты видишь проблемы с MT в функции InitFunction.
AS>>>Синглтон майерса не потокобезопасен. В данном случае он используется в CModule. Там можно немного и по-другому, но проблему это не снимет.
CAM>>Синглтон майерса — умное слово. У тебя есть HMODULE, который суть void*, и есть функции его проверки и установки. Они атомарны. Худшее, что может быть, опять — последовательный вызов LoadLibrary.
AS>Ага, а регистрировать функцию для выполнения FreeLibrary (AtExit) вы будете ручками. AS>В общем, нет, все не то. Почитайте топик про синглтон майерса в форуме cpp и проникнитесь. До этого обсуждать что-либо про MT нет смысла.
Фу, как позорно облажался !
Хотя, зная, что такое синглтон майерса, я бы сделал так :
И по прежнему этот вариант лучше, чем любой с CriticalSection/Mutex'ами.
....
CAM>>Возможно, ты прав, на счёт небольшой разницы этих двух ситуаций, однако по-моему предпочтительнее дать пользователям возможность решать, что для них важно и нужно. Когда есть возможность, ей можно и не пользоваться, а вот когда её нет, и она нужна, то возникают проблемы.
AS>НИКАКОЙ проблемы нет. Если m_hModule == 0 — значит, ошибка относится к загрузке модуля, иначе — к функции. Ну где здесь проблемы, не понимаю? Макрос, позволяющий задавать свою policy, я уже сделал.
__>>Мне кажется в CNameId будет немного неправильные значения.
__>>Решение 1 :
AS>Если так написать, код просто не откомпилируется AS>
AS>error C2440: 'return' : cannot convert from 'unsigned short [13]' to 'const char *'
AS> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
AS>
CAM>И по прежнему этот вариант лучше, чем любой с CriticalSection/Mutex'ами.
И опять мимо.
Вот в этом месте
static Destroyer destroyer;
будет вызван конструктор Destroyer, но не только — еще и будет зарегистрирован деструктор дестроера. Причем вызывать или нет саму процедуру регистрации деструктора определяется состоянием флага.
Проще показать нап примере. На самом деле, этот код эквивалентен примерно такому:
static char destroyer[sizeof(destroyer)];
if (!instance_flag)
{
new (&destroyer[0])Destroyer();
atexit(destroyer_instance_deleter);
}
Думаю, дальше объяснять не надо. В общем — читайте Александреску, там про эти проблемы подробно.Ну и + форум, конечно.
PS MT я уже сделал, немного потестирую и выложу
Здравствуйте, CAMAD, Вы писали:
CAM>Худшее, что может быть, опять — последовательный вызов LoadLibrary.
Тогда нужен будет и лишний вызов FreeLibrary. Иначе — утечка.
Можно сделать тупо на атомарных функциях:
if (!m_hModule)
{
HMODULE hModule = ::LoadLibrary(...);
if (::InterlockedCompareExchangePointer(&m_hModule, hModule, NULL))
{
// Somebody did it already
::FreeLibrary(hModule);
}
}
Здравствуйте, Andrew S, Вы писали:
__>>Стоит доработать это.
AS>Зачем? Все правильно. В юникодном проекте строка с L откомпилится вполне нормально.
Вы меня не поняли.
Есть 4 ситуации —
не юникод проект . не юникодная строка — будет работать нормально
не юникод проект . юникодная строка — не будет работать
юникод проект . не юникодная строка — не будет работать
юникод проект . юникодная строка — будет работать нормально
Не, я понял.
__>не юникод проект . юникодная строка — не будет работать __>юникод проект . не юникодная строка — не будет работать
Вот эти 2 ситуации на мой взгляд могут присутствовать в 0.0001% случае и должны быть отправлены в dev\null
Это примерно как вызвать LoadLibrary в неюникодном проекте с юникодной строкой и возмущаться — почему это не компилируется
AS>будет вызван конструктор Destroyer, но не только — еще и будет зарегистрирован деструктор дестроера. Причем вызывать или нет саму процедуру регистрации деструктора определяется состоянием флага.
Мда, лучше бы мне подумать, прежде чем писать half-baked решение.
Мало того, что два раза может быть вызван ~Destroyer (это ты имел в виду ?), так ещё и FreeLibrary нужно вызывать для каждого LoadLibrary... Кстати, там Павел Блудов предложил решение второй проблемы, на Interlocked функциях — если и в деструкторе Destroyer сделать то же, т.е. менять хенлд с 0, и потом его грохать, то вроде всё должно быть ок (наконец-то !).
CAM>Мда, лучше бы мне подумать, прежде чем писать half-baked решение. CAM>Мало того, что два раза может быть вызван ~Destroyer (это ты имел в виду ?), так ещё и FreeLibrary нужно вызывать для каждого LoadLibrary... Кстати, там Павел Блудов предложил решение второй проблемы, на Interlocked функциях — если и в деструкторе Destroyer сделать то же, т.е. менять хенлд с 0, и потом его грохать, то вроде всё должно быть ок (наконец-то !).
Я видел, это все очевидно. Но зачем — когда на тех же Interlocked легко делается LW mutex и все проблемы идут лесом буквально 3-мя строчками кода
Добавлена поддержка MT. Отключается определением макроса DL_NO_MT.
Немного изменен интерфейс модуля
Добавлен макрос, позволяющий задавать custom policy для функций
Автоматическая поддержка юникода на основе макросов хедера от статически линкуемой библиотеки
Мелкие багфиксы
To do
Багфиксы
Уменьшение размера прокси в MT варианте
Что то еще?
По поводу MT — реализация, на мой взгляд, достаточно спорная, можно и нужно обсуждать.
Особенно это касается результирующих размеров прокси — в MT варианте 230-240 байт на функцию,
в ST порядка 80-90 (там все инлайнится буквально в plain code). В MT остается таки один
уровень не инлайновости, но это не спасает — все-таки 240 байт на функцию это прилично.
Конечно, думаю, не больше, чем у других, но все-таки значительно больше ST варианта.
Мысли?
Здравствуйте, Andrew S, Вы писали:
AS>Не, я понял.
__>>не юникод проект . юникодная строка — не будет работать __>>юникод проект . не юникодная строка — не будет работать
AS>Вот эти 2 ситуации на мой взгляд могут присутствовать в 0.0001% случае и должны быть отправлены в dev\null AS>Это примерно как вызвать LoadLibrary в неюникодном проекте с юникодной строкой и возмущаться — почему это не компилируется
Ну для этого LoadLibraryA есть
Предусмотреть все возможные проблемы заранее надо, или тогда написать чтобы не делать такое
Здравствуйте, Andrew S, Вы писали:
__>>Ну для этого LoadLibraryA есть __>>Предусмотреть все возможные проблемы заранее надо, или тогда написать чтобы не делать такое
AS>А что — кто то уже кроме нас пробовал?
Еще видимо нет, а то так бы написали =)
btw — может, лучше выкладывать новые версии отдельным архивом в файловую область? А то не всем, наверное, интересно получать довольно приличные по размеру постинги
Уменьшение размера прокси в MT варианте. Теперь на VC6 размер прокси 36-44 байта для MT. Для ST размер прокси на VC6 такой же, как и раньше. В случае VC7.1 прокси оптимизируется куда качественней — процентов на 30-40 меньше, чем аналогичный на VC6.
Библиотека помещена в отдельный наймспайс — dl. Синтаксис декларации модулей и функций остался прежним.
To do
Багфиксы
Что то еще?
По поводу размеров прокси — думается, оптимизировать больше некуда, получаемый код выглядит оптимальным как в случае VC6, так и в случае VC7. Ждем новых предложений.
Здравствуйте, Andrew S, Вы писали:
AS>btw — может, лучше выкладывать новые версии отдельным архивом в файловую область? А то не всем, наверное, интересно получать довольно приличные по размеру постинги
AS>Что то еще? AS>
Ну, например, вариант с GetModuleHandle вместо LoadLibrary/GetProcAddress.
Во первых работает быстрее.
Во вторых — не нужно делать FreeLibrary.
В третьих, можно вообще не синхронизировать.
Для заведомо загруженных библиотек типа NTDLL, Kernel32, USER и GDI будет лучше.
БП>Ну, например, вариант с GetModuleHandle вместо LoadLibrary/GetProcAddress. БП>Во первых работает быстрее. БП>Во вторых — не нужно делать FreeLibrary. БП>В третьих, можно вообще не синхронизировать.
Да, я уже про это думал. Вероятно, будет удобным сделать стратегии вида:
и уже их испольовать вместо непосредственных вызовов LoadLibrary и иже с ними.
С одной стороны, это даст возможность не писать большого количества однообразного кода для другого CModule, с другой стороны — даст возможность пользователю управлять процессом загрузки библиотек, например, изменять пути по умолчанию, пути к файлам и т.п.
Как вернусь из отпуска, попробую сделать нечто подобное.
A>P.S. Вызов через такую обёртку даже быстрее вызова через таблицу импорта. Вот я и думаю, а не создать ли продвинутый файл windows (как string.h -> string) в котором все функции будут раскиданы по пространствам имён? Как побочный эффект своего рода антиотладка (передаю привер шароварщикам ) Эдакая МЕГАИДЕЯ
Очень слабая защита, поскольку в первую очередь break ставится на первую инструкцию функции.
Защиту получше описал Крис Касперски:
Управление передается не на первую инструкцию импортируемой функции, а на 3-й или 4-й байт, при этом недостающие байты (обычно стандартный пролог) выполняются клиентом.
Добавлены стратегии загрузки\выгрузки модулей. Соотв, добавлены 2 макроса: DL_USE_MODULE_NON_LOAD_BEGIN и DL_USE_MODULE_LOAD_POLICY_BEGIN. Первый использует GetModuleHandle для загрузки модуля, второй вызывает соотв. функции переданой ему стратегии.
Багфиксы в макросах.
P.S. Как уже писалось ранее, есть мысль выложить все это в файлы в качестве отдельной библиотеки или функционала (с соотв. маленьким описанием классов, макросов и примером использования). Если общественность в виде титулованных rsdn'енеров поддержит это (т.е. сочтет данную библиотеку как минимум полезной) — можно будет устроить и это.
__>Почему-то это учтено выше, но в функции не учтено.
Нет, это почему то в функции не учтено, а что учтено выше — вполне очевидно. Спасибо. Fixed.
__>Предложение по обработке DL_MT :
Мне текущий вариант нравится, он весьма лаконичен. __>И не надо будет определять макрос DL_MT
Макрос DL_MT определяется просто для красоты — на самом деле, хватило бы и DL_NO_MT
__>P.S. __>Стиль как-то не очень совпадает, пространство имен называет dl, а имена классов в другом стиле, да еще начинаются на C. __>Есть предложение изменить стиль на стиль STL
Ну, у нас тоже есть некоторые свои требования на оформление кода, данный вариант является компромисным ( в данном случае я стремился к тому, чтобы типы назывались в стиле stl, а классы — в стиле MFC, что на мой взгляд более читабельно). Тем не менее внес несколько изменений, дабы было более однозначно. __>Может пространство имен сменить на delayload, а то dl не очень уникально может быть.
Да, dl не слишком уникально (учитывая встроенный ассемблер). delayload тоже, я полагаю. Есть еще мысли по названию неймспейса?
Здравствуйте, Andrew S, Вы писали:
__>>Предложение по обработке DL_MT :
AS>Мне текущий вариант нравится, он весьма лаконичен. __>>И не надо будет определять макрос DL_MT
AS>Макрос DL_MT определяется просто для красоты — на самом деле, хватило бы и DL_NO_MT
Я имел ввиду немного другое.
В данной реализации невозможно использовать в проекте многопоточном немногопоточный класс и наоборот.
А таким образом можно использовать класс, который в зависимости от настроек будет другои, а можно и явно задать что использовать.
__>>P.S. __>>Стиль как-то не очень совпадает, пространство имен называет dl, а имена классов в другом стиле, да еще начинаются на C. __>>Есть предложение изменить стиль на стиль STL
AS>Ну, у нас тоже есть некоторые свои требования на оформление кода, данный вариант является компромисным ( в данном случае я стремился к тому, чтобы типы назывались в стиле stl, а классы — в стиле MFC, что на мой взгляд более читабельно). Тем не менее внес несколько изменений, дабы было более однозначно.
Мне кажется что классы в стиле MFC это самое хучшее что может быть,IMHO
Зачем надо это "C" вначале, можно без него обойтись.
__>>Может пространство имен сменить на delayload, а то dl не очень уникально может быть.
AS>Да, dl не слишком уникально (учитывая встроенный ассемблер). delayload тоже, я полагаю. Есть еще мысли по названию неймспейса?
delayload довольно уникально по сравнению с dl.
Вообще надо определиться со стилем и тогда название может быть delayload, DelayLoad. delay_load.
P.S.
Стиль STL мне кажется лучше всего подходит здесь.
P.P.S.
Небольшое добавление :
struct CLWMutex
{
void Lock()
{
while(::InterlockedExchange(&m_pFlag, TRUE))
::Sleep(1); // почему здесь не было оператора видимости ?
}
};
__>Я имел ввиду немного другое. __>В данной реализации невозможно использовать в проекте многопоточном немногопоточный класс и наоборот.
Отнюдь. Как раз первое — задается макросом DL_NO_MT. А второе просто не нужно — подумайте сами, зачем в немногопоточном приложении использовать явно лишний многопоточный вариант.
AS>>Ну, у нас тоже есть некоторые свои требования на оформление кода, данный вариант является компромисным ( в данном случае я стремился к тому, чтобы типы назывались в стиле stl, а классы — в стиле MFC, что на мой взгляд более читабельно). Тем не менее внес несколько изменений, дабы было более однозначно.
__>Мне кажется что классы в стиле MFC это самое хучшее что может быть,IMHO __>Зачем надо это "C" вначале, можно без него обойтись.
Для понятности, что это класс имплементации. Класс интерфейса начинается на I. Мне наоборот нравится, хотя, может, просто привычка.
__>>>Может пространство имен сменить на delayload, а то dl не очень уникально может быть.
AS>>Да, dl не слишком уникально (учитывая встроенный ассемблер). delayload тоже, я полагаю. Есть еще мысли по названию неймспейса?
__>delayload довольно уникально по сравнению с dl. __>Вообще надо определиться со стилем и тогда название может быть delayload, DelayLoad. delay_load.
Ок, тогда пусть будет delayload. Сейчас поправим...
__>P.P.S. __>Небольшое добавление : __>
__>struct CLWMutex
__>{
__> void Lock()
__> {
__> while(::InterlockedExchange(&m_pFlag, TRUE))
__> ::Sleep(1); // почему здесь не было оператора видимости ?
__> }
__>};
__>
Здравствуйте, Andrew S, Вы писали:
__>>Я имел ввиду немного другое. __>>В данной реализации невозможно использовать в проекте многопоточном немногопоточный класс и наоборот.
AS>Отнюдь. Как раз первое — задается макросом DL_NO_MT. А второе просто не нужно — подумайте сами, зачем в немногопоточном приложении использовать явно лишний многопоточный вариант.
А если я хочу использовать многопоточный класс тоже.
Получается что я не могу это сделать.
А было бы так:
#include <delayload.h>
// CModule - зависит от настройки
// CModuleMT - многопоточный
// CModuleNoMT - не многопоточный
// DL_USE_MODULE_LOAD_POLICY_BEGINtypedef delayload::CModule<NAME_ID(DL_CAT(_MODULE_, nmspace)), load_policy> module_type;
// задаем явно
// DL_USE_MODULE_NOMT_LOAD_POLICY_BEGINtypedef delayload::CModuleNoMT<NAME_ID(DL_CAT(_MODULE_, nmspace)), load_policy> module_type;
AS>>>Ну, у нас тоже есть некоторые свои требования на оформление кода, данный вариант является компромисным ( в данном случае я стремился к тому, чтобы типы назывались в стиле stl, а классы — в стиле MFC, что на мой взгляд более читабельно). Тем не менее внес несколько изменений, дабы было более однозначно.
__>>Мне кажется что классы в стиле MFC это самое хучшее что может быть,IMHO __>>Зачем надо это "C" вначале, можно без него обойтись.
AS>Для понятности, что это класс имплементации. Класс интерфейса начинается на I. Мне наоборот нравится, хотя, может, просто привычка.
Насчет интерфейса это согласен.
Я делаю так :
Классы которые относятся к программе делаю в стиле MFC это если программа на MFC или WTL , а классы моей библиотеки делаю не в стиле MFC, так отличить легко что куда =)
Здравствуйте, Andrew S, Вы писали:
__>>P.S. __>>Что-то у вас нет единого стиля в коде. __>>
__>>return Policy::template FunctionTrait<DynFunction>::MakeReturn(); // есть template
__>>//...
__>>delayload::FUN_PROXY(DL_SEQ_SIZE(p))<r, DL_SEQ_ENUM(p), pl > >::GetProxy() // нет template
__>>
__>>Может стоить добавить template где надо, хотя и без него компилируется
AS>Разве это относится к стилю? Если вы про спецификатор template после наймспейса — то в данном случае он не нужен. И комеау со мной тут согласен.
Ой, delayload это пространство имен
Я забыл что вы переименовали его, прощу прощения
__>А что насчет многопоточных и немногопоточных классов в одной программе сразу ?
Многопоточность в данном случае не сильно влияет и на размер кода, и на производительность (на размер почти не влияет, а на производительность во временнОм понятии — вообще). Макрос DL_NO_MT в свое время появился из-за того, что размер кода сильно возрос после реализации MT. Однако после выноса части прокси в отдельную функцию оказалось, что размер кода MT ~~== размеру кода ST. Так что это не принципиально. Я сначала думал сделать стратегии для поддержки многопоточности, но потом решил, что это уже слишком, да и не нужно никому.
Здравствуйте, Andrew S, Вы писали:
__>>А что насчет многопоточных и немногопоточных классов в одной программе сразу ?
AS>Многопоточность в данном случае не сильно влияет и на размер кода, и на производительность (на размер почти не влияет, а на производительность во временнОм понятии — вообще). Макрос DL_NO_MT в свое время появился из-за того, что размер кода сильно возрос после реализации MT. Однако после выноса части прокси в отдельную функцию оказалось, что размер кода MT ~~== размеру кода ST. Так что это не принципиально. Я сначала думал сделать стратегии для поддержки многопоточности, но потом решил, что это уже слишком, да и не нужно никому.
Я не про размер говорю.
Я имею ввиду что стоит сделать 2 версии классов CModule и CDynFunction.
__>Я не про размер говорю. __>Я имею ввиду что стоит сделать 2 версии классов CModule и CDynFunction.
Нет, по приведенным ранее причинам.
__>P.S. __>Поговорим насчет стиля ?
Да ну, у каждого свои понятия красоты кода
Вот если таки хватит сил или общественность поддержит инициативу официального выкладывания этой поделки на RSDN — тогда уже будем все там править, что соответствовало тому, что принято на RSDN.
Впрочем, в любом случае — использоваться в 99% случаях будут только макросы, так что стиль оформления кода внутри наймспейса не так и важен, только из эстетических соображений. А мне текущий вариант более (или менее) нравится
Лучше еще что-нибудь по улучшению функционала придумать... Да вот не думается ничего
Здравствуйте, Andrew S, Вы писали:
__>>Я не про размер говорю. __>>Я имею ввиду что стоит сделать 2 версии классов CModule и CDynFunction.
AS>Нет, по приведенным ранее причинам.
__>>P.S. __>>Поговорим насчет стиля ?
AS>Да ну, у каждого свои понятия красоты кода
Само собой. AS>Вот если таки хватит сил или общественность поддержит инициативу официального выкладывания этой поделки на RSDN — тогда уже будем все там править, что соответствовало тому, что принято на RSDN. AS>Впрочем, в любом случае — использоваться в 99% случаях будут только макросы, так что стиль оформления кода внутри наймспейса не так и важен, только из эстетических соображений. А мне текущий вариант более (или менее) нравится
Если макросы будут, то это конечно не важно
Задача чтобы от них избавиться.
AS>Лучше еще что-нибудь по улучшению функционала придумать... Да вот не думается ничего
Уменьшит количество макросов.
__>Если макросы будут, то это конечно не важно __>Задача чтобы от них избавиться.
Боюсь, что не получится. Во-первых, не поддерживается специализация строковым литералом. Приходится с макросами извращаться — никак иначе. Во-вторых — макросы заворачивают ненужные и нудные телодвижения (например, декларация глобального для объявления модуля в единице трансляции типа модуля). В данном случае без них было бы кисло.
AS>>Лучше еще что-нибудь по улучшению функционала придумать... Да вот не думается ничего __>Уменьшит количество макросов.
Вариантов не вижу. Их вроде и так минимально возможное количество
Здравствуйте, Andrew S, Вы писали:
__>>Если макросы будут, то это конечно не важно __>>Задача чтобы от них избавиться.
AS>Боюсь, что не получится. Во-первых, не поддерживается специализация строковым литералом. Приходится с макросами извращаться — никак иначе. Во-вторых — макросы заворачивают ненужные и нудные телодвижения (например, декларация глобального для объявления модуля в единице трансляции типа модуля). В данном случае без них было бы кисло.
Согласен они упрощают код, скрывая лишнее.
Однако если есть возможность(надеюсь что есть) без них то лучше без них.
Здравствуйте, Andrew S, Вы писали:
__>>DL_USE_MODULE_END стоит изменить на:
AS>А в чем смысл, ну кроме необходимости ставить еще и () для макроса без параметров?
Однообразие.
DL_MODULE_BEGIN(...)
DL_MODULE_END()
Например карты сообщений в MFC и WTL сделанны так и оно смотрится лучше.
Я очень был удивлен что скобок не надо ставить.
__>>Например карты сообщений в MFC и WTL сделанны так и оно смотрится лучше.
AS>Это сильный аргумент Ок, подумаю.
Кстати, тут поступило предложение изменить макросы DL_DECLARE_FUN_xxx(name, ret, ...) на DL_DECLARE_FUN_xxx(ret, name, ...). Как общественность смотрит на это? С одной стороны, это вроде как больше походит на обычное С определение функции, с другой стороны, мне почему то более логичным кажется сначала указание имени импортируемой функции, а потом уже типов (может, просто привычка, конечно). Какие будут мнения? Если положительные, то последствия для тех мест, где используется старый вариант, я надеюсь, вы представляете
В общем, высказывайтесь. Я в сомнениях.
Здравствуйте, Andrew S, Вы писали:
AS>Итак, версия 1.09.
AS>Список изменений:
...
AS>Исходники доступны по адресу ниже. AS>http://www.rsdn.ru/File/8583/delayimphlp.zip
AS>P.S. Как уже писалось ранее, есть мысль выложить все это в файлы в качестве отдельной библиотеки или функционала (с соотв. маленьким описанием классов, макросов и примером использования). Если общественность в виде титулованных rsdn'енеров поддержит это (т.е. сочтет данную библиотеку как минимум полезной) — можно будет устроить и это.
"Так они шептались целую неделю
и заодно делали большой железный сундук. Над ним трудились и
папа Карло, и Ферручино, и Буратино, который понемногу научился
мастерить. Артемон приносил различные железные предметы и ловко
смахивал хвостом металлические стружки. Мальвина шлифовала
готовые детали маленьким напильничком. Пьеро постукивал,
побрякивал, подтачивал, закручивал гаечки"
(c)Александр Кумма, Сакко Рунге. Вторая тайна золотого ключика
Милейший, если у вас есть какие то конкретные предложения по библиотеке, либо по функционалу — вперед. Если же нет — просьба не засорять подобными сообщениями эту ветку, для них есть специальные форумы. Заранее спасибо.
Fuz>
Fuz>"Так они шептались целую неделю
Fuz>и заодно делали большой железный сундук. Над ним трудились и
Fuz>папа Карло, и Ферручино, и Буратино, который понемногу научился
Fuz>мастерить. Артемон приносил различные железные предметы и ловко
Fuz>смахивал хвостом металлические стружки. Мальвина шлифовала
Fuz>готовые детали маленьким напильничком. Пьеро постукивал,
Fuz>побрякивал, подтачивал, закручивал гаечки"
Fuz>(c)Александр Кумма, Сакко Рунге. Вторая тайна золотого ключика
Здравствуйте, Andrew S, Вы писали:
AS>Милейший, если у вас есть какие то конкретные предложения по библиотеке, либо по функционалу — вперед. Если же нет — просьба не засорять подобными сообщениями эту ветку, для них есть специальные форумы. Заранее спасибо.
Не хотите, не буду... да и правила вроде не разрешают... просто расслабляться иногда тоже нужно.
Извините, если задел.
Выяснился один небольшой недостаток — отсутствие поддержки функций без параметров.
Как реализовать это в рамках текущих макросов, я придумать не смог, в связи с чем пришлось добавить макросы с индексом 0... Если есть какие предложения по другой реализации всего этого — с интересом обсудим.
Итак, версия 1.1.0.
Список изменений:
Добавлены макросы для поддержки функций без параметров:
DL_DECLARE_FUN_0_ERR_POLICY
DL_DECLARE_FUN_0
DL_DECLARE_FUN_0_THROW
DL_DECLARE_FUN_0_ERR
Синтаксис и назначение аналогичны таковым для остальных функций, за исключением, ессно, отсутствия списка параметров.
Здравствуйте, Andrew S, Вы писали:
AS>Гм. Неожиданное обновление.
AS>Выяснился один небольшой недостаток — отсутствие поддержки функций без параметров.
Есть еще как минимум один...
Поддержка UNICODE.
Я уже давно писал в этой теме, что GetProcAddress не принимает UNICODE строк.
Попробуйте в тестовом проекте поставить UNICODE вместо _MBCS.
AS>>Гм. Неожиданное обновление.
AS>>Выяснился один небольшой недостаток — отсутствие поддержки функций без параметров.
MS>Есть еще как минимум один... MS>Поддержка UNICODE.
MS>Я уже давно писал в этой теме, что GetProcAddress не принимает UNICODE строк. MS>Попробуйте в тестовом проекте поставить UNICODE вместо _MBCS.
Так никто туда его и не передает. По крайней мере, не думал передавать — обратите внимание на макрос DECLARE_NAME_ID_A. Другое дело, что DL_GetProcAddressImpl пользовала LPCTSTR — это уже по недосмотру при оптимизации. Это я поправил, спасибо, а вот остальное вроде все работает. Так что ваше давнишнее замечание к этому багу никакого отношения не имеет.
Хм. Еще одно неожиданное обновление предыдущего обновления.
>>Выяснился один небольшой недостаток — отсутствие поддержки функций без параметров. >>Как реализовать это в рамках текущих макросов, я придумать не смог, в связи с чем пришлось добавить макросы с индексом 0... Если есть какие >>предложения по другой реализации всего этого — с интересом обсудим.
Немного подумав, как обойтись, придумать смог
В процессе придумывания было найдено несколько новых багов 6-ки, так что если непонятно, почему сделано именно, так, а не проще — см. комменты.
Итак, версия 1.1.1.
Список изменений:
1. Функции без параметров представлены в виде функций с одим параметром типа void. То бишь объявлять их надо примерно так DL_DECLARE_FUN(имя, ret_type, (void))
2. Убраны макросы для поддержки функций без параметров за ненадобностью:
DL_DECLARE_FUN_0_ERR_POLICY
DL_DECLARE_FUN_0
DL_DECLARE_FUN_0_THROW
DL_DECLARE_FUN_0_ERR
Здравствуйте, Andrew S!
Есть вопрос по новой версии вашей библиотеки.
При компиляции в VC++ 7.1 на 4 уровне предупреждений мне постоянно генерируются
варнинги C4702 и C4100. Причем первый по нескольку раз, он сильно забивает трей, что немного раздражает.
Единственный способ который я нашол, это "закрыть" подключение библиотеки в прагмы:
Для меня это не выход, т.к. библиотека используюется в очень многих файлах. Можете что-то подсказать как избавиться от этих предупреждени ? Возможно, я неправильно формирую описание функций ?
23W>Для меня это не выход, т.к. библиотека используюется в очень многих файлах. Можете что-то подсказать как избавиться от этих предупреждени ? Возможно, я неправильно формирую описание функций ?
Я в другой ветке ответил. У меня в 2003-м генерится только один варнинг (4702) на код
Судя потому, что там написано, return действительно лишний. Без него VC++ 7.1 компилирует это код без ошибок и предупреждений. Я понимаю что библиотека универсальная. Но может следует внести некоторые изменения, например так:
И сново здравствуйте!
Андрей, Вы будете смеяться, но дождавшись наконец-то полного ребилдинга проекта
я заметил еще один варнинг (он тоже из области 4-го уровня).Это C4512!
Проявляется он далеко не всегда, напримерм у меня в проекте есть несеколько модулей оптимизированные под
высокую производительность, поэтому они не искрользуют никаких библиотек, кроме некоторых шаблонов STL, и
в настройки этих подпроэктов выкручены "по максимуму".
Так вот в этих модулях, зачем-то компилятор начинает меня предупреждать что
для структуры CLWMutex и шаблона CAutoLock<T> невозможно автоматически сгенерировать оператор присваивания.
При этом сам оператор в явном виде нигде не вызывается. Я уже слышал про такую особенность VC++ 7.1 и встерчался
с ней в своем коде. Дело в том что этот "умный" компилятор хочет в некоторых местах "соптимизировать" использование
структур и не может это сделать т.к. не может автоматически сгенерировать этот самый оператор (из-за наличия ссылок внутри класса или структуры). После этого он работает как положено, т.е. ошибкой или потенциальной ошибкой это не
считается. Но осадок остался
Во многих форумах встречал такой способ отучить компилятор от глупостей — объявить в приватной секции класса или шаблона конструктор копирования и оператор присваивания, но не реализовывать их. Причем этот способ не влияет на другие компиляторы и дает возможность компилятору "догадатся" что для данного класса или структуры запрещено явное и неявное копирование. В своем коде я делал такое не раз — работает.
В соответствии с этим я, набравшись наглости, модифицировал Ваши классы так:
23W>И сново здравствуйте! 23W>Андрей, Вы будете смеяться, но дождавшись наконец-то полного ребилдинга проекта 23W>я заметил еще один варнинг (он тоже из области 4-го уровня).Это C4512!
Ок, спасибо за поправки, я их интегрировал (в слегка изменнном виде) и выложил результат в файлы по старой ссылке.
A>Библиотека классная. A>Есть одно очень МАЛЕНЬКОЕ замечание A>в строке 478 формируется сообщение "Can'n resolve procedure <%s>: %d". A>Мне кажется что должно быть так "Can't resolve procedure <%s>: %d".
Ага, точно. Этот баг фиксился несколько раз, но, как феникс, он постоянно возрождался от версии к версии Вот и сейчас.
Выложил в файлы исправленный вариант. http://gzip.rsdn.ru/File/8583/delayimphlp.zip
Спасибо за замечание.
Здравствуйте, Andrew S, Вы писали:
AS>Ага, точно. Этот баг фиксился несколько раз, но, как феникс, он постоянно возрождался от версии к версии Вот и сейчас.
Еще вопросик в тему: в вашей статье нигде не упоминается про лицензию? есть какие-нибудь ограничения на использования вашей библиотеки в коммерческих проектах?
AS>>Ага, точно. Этот баг фиксился несколько раз, но, как феникс, он постоянно возрождался от версии к версии Вот и сейчас.
A>
A>Еще вопросик в тему: в вашей статье нигде не упоминается про лицензию? есть какие-нибудь ограничения на использования вашей библиотеки в коммерческих проектах?
Иначе был бы смысл выкладывать ее в общий доступ... Конечно, нет ограничений, по крайней мере, с моей стороны — пользуйте, изменяйте и улучшайте на здоровье. Если какие баги\улучшательства найдете — пишите, поправим\добавим в новые версии.
Здравствуйте, Andrew S, Вы писали:
A>>Еще вопросик в тему: в вашей статье нигде не упоминается про лицензию? есть какие-нибудь ограничения на использования вашей библиотеки в коммерческих проектах? AS>Иначе был бы смысл выкладывать ее в общий доступ... Конечно, нет ограничений, по крайней мере, с моей стороны — пользуйте, изменяйте и улучшайте на здоровье. Если какие баги\улучшательства найдете — пишите, поправим\добавим в новые версии.
1. Немного изменены макросы DL_REPEAT_XXXX Теперь нумерация параметров начинается с 0, а не с 1. Для совместимости с бустом. На внешнем интерфейсе библиотеки это никак не отразилось.
2. Расширен список поддерживаемых компиляторов: добавлена возможность вместо предлагаемой библиотекой реализации использовать порождающие макросы от boost::preprocessor. Для этого надо в свойствах проекта или же перед включением заголовка библиотеки определить макрос
#define DL_USE_BOOST_PP
По крайней мере, теперь макросы не будут проблемой в случае, если нативная реализация не устраивает целевой препроцессор.
1. Генерируемые макросами типы, надобные для внутренних нужд библиотеки, находятся теперь в наймспейсе internal_types. Т.о., чтобы добраться до типа модуля, надо теперь писать нечто вроде module_name::internal_types::module_type. Решение, в принципе, спорное — если есть аргументы за\против, можно пообсуждать.
2. Добавлены макросы DL_UNLOAD_MODULE(nmspace) и DL_RESET_FUN(nmspace, name_id). Первый выгружает модуль, а второй приводит функцию в состояние до загрузки (т.е. при следующем ее вызове опять будет вызван прокси-объект). Понадобится это может, например, если необходимо выгрузить и опять загрузить библиотеку, хотя и будет выглядеть довольно муторно. Если есть лучшие предложения — можно обсудить.
3. Багфиксы юникодной конфигурации (за это отдельное спасибо Вечяславу).
4. Новый пример.
Здравствуйте, Andrew S, Вы писали:
AS>Версия 1.2.0
Скачал новую версию. Компилирую. Запускаю прогу. При загрузке функции получаю Debug Error:
...
File: delayimphlp.h
Line: 611
Run-Time Check Failure #0 — The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling conversion with a function pointer declared with a different calling conversion.
Abort Ignore Retry
Вашу библиотеку использую так:
DL_USE_MODULE_BEGIN(foo, _T("foo.dll"))
DL_DECLARE_FUN_THROW(
Bar
, result
, (char const*)(short)(char const*)(char const*)(char const*)(HWND)(char const*)
)
DL_USE_MODULE_END()
где то в коде...
result bar_result = foo::Bar( // на этой строчке вываливается message box Debug Error'а
str1_.c_str()
, short1_
, str2_.c_str()
, str3_.c_str()
, str4_.c_str()
, m_hWnd
, str5_.c_str()
);
Где грабли?
P.S. Если делаю по простому LoadLibrary, GetProcAddress — все ОК.
Грабли в соглашениях о вызове. У вас, скорее всего, используется __сcall. В библиотеке — __stdcall, и добавлять поддержку других конвенций не планируется.
ssi>P.S. Если делаю по простому LoadLibrary, GetProcAddress — все ОК.
А кто бы сомневался. Там-то вы функцию определяете как __ccall.
Здравствуйте, Andrew S, Вы писали:
ssi>>Где грабли?
AS>Грабли в соглашениях о вызове. У вас, скорее всего, используется __сcall. В библиотеке — __stdcall, и добавлять поддержку других конвенций не планируется.
ssi>>P.S. Если делаю по простому LoadLibrary, GetProcAddress — все ОК.
AS>А кто бы сомневался. Там-то вы функцию определяете как __ccall.
Как выйдти из положения? Dll-ка не моя, поэтому использовать другие соглашения о вызове не могу, в то же время хочу использовать Вашу библиотеку, имхо очень удобная.
Здравствуйте, Аноним, Вы писали:
А>Как выйдти из положения? Dll-ка не моя, поэтому использовать другие соглашения о вызове не могу, в то же время хочу использовать Вашу библиотеку, имхо очень удобная.
Ну так просто замени везде в библиотеке WINAPI на на что надо. Правда её тогда нельзя будет использовать с системными функциями.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Аноним, Вы писали:
А>>Как выйдти из положения? Dll-ка не моя, поэтому использовать другие соглашения о вызове не могу, в то же время хочу использовать Вашу библиотеку, имхо очень удобная.
A>Ну так просто замени везде в библиотеке WINAPI на на что надо. Правда её тогда нельзя будет использовать с системными функциями.
Библиотека (.dll) не моя, менять ничего не могу. Даже хедера нет, есть только дока с описанием контекста функций.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, ssi, Вы писали:
ssi>>Библиотека (.dll) не моя, менять ничего не могу. Даже хедера нет, есть только дока с описанием контекста функций.
A>В библиотеке которой посвящён этот топик, а не в той которую ты хочешь подключить
А>>Как выйдти из положения? Dll-ка не моя, поэтому использовать другие соглашения о вызове не могу, в то же время хочу использовать Вашу библиотеку, имхо очень удобная.
A>Ну так просто замени везде в библиотеке WINAPI на на что надо. Правда её тогда нельзя будет использовать с системными функциями.
Ну ладно, ладно, уговорили Не надо так прям вот все менять.
Итак, версия 1.3.0 бета.
Список изменений:
1. Добавлена поддержка конвенций вызова. Непосредственно в библиотеке поддерживаются WINAPI и __cdecl.
Итак, как все это работает. За поддержку встроенных конвенций отвечают макросы
#define DL_NO_STDCALL // не инастанцировать поддержку winapi соглашений о вызовах#define DL_USE_CDECL // инстанцировать поддержку __cdecl (по умолчанию отключено)
За поддержку вообще любых конценций отвечают макросы
Назначение и параметры макросов DL_DECLARE_FUN_xxx_CC аналогичны DL_DECLARE_FUN_xxx, за исключением того, что первым параметром добавляется конвенция о вызове функции. Т.о., на самом деле это generic интерфейс к библиотеке, которым пользуются все остальные макросы.
Так фактически будет повторение синтаксиса определения функции в С... Как вам это, обсудим? Последствия очевидны — обломинго при компиляции старых исходников.
И еще вопрос: если функция возвращает сишную структуру, то почему я не могу использовать макросы DL_DECLARE_FUN и DL_DECLARE_FUN_ERR? Компилятор выдает ошибки С2993 и С2975 на класс CFunProxyValuePolicy?
Получается, что в этом случае единственный способ узнать об ошибке это использовать DL_DECLARE_FUN_THROW, что ИМХО не очень удобно — ставить вокруг каждого вызова функции из dll-ки блок try-catch.
А>Раз уж мы определили дефайном DL_USE_CDECL, зачем использовать DL_DECLARE_FUN_CC(__cdecl, ...) вместо обычного DL_DECLARE_FUN()?
DL_DECLARE_FUN подразумевает использование соглашений WINAPI. DL_DECLARE_FUN_CC позволяет явно указать соглашения. Често говоря, не понимаю недоразумения.
А>или так нельзя: А>
Можно, для этого DL_DECLARE_FUN_CC и нужно. дефайн DL_USE_CDECL не изменяет конвенций по умолчанию, он лишь добавляет поддержку __cdecl в DL_DECLARE_FUN_CC. Думаю, этот код говорит сам за себя:
А>И еще вопрос: если функция возвращает сишную структуру, то почему я не могу использовать макросы DL_DECLARE_FUN и DL_DECLARE_FUN_ERR? Компилятор выдает ошибки С2993 и С2975 на класс CFunProxyValuePolicy?
В параметры шаблона стратегии эта штука передается как template<class R, R value = R()>. Т.е., допустимы только интегральные типы и указатели — все то, что принимает значения константы. Единственная проблема со стратегией CFunProxyValuePolicy — это то, что для, например, HANDLE, нельзя передать -1 как значение ошибки. В случае HANDLE это решается тем, что оно в вин32 не может также принимать и значение 0, хотя это, конечно, и не очень красиво получается. Если кто-нибудь предложит красивое решение проблемы — я буду только рад...
А вообще, если често.. Возвращать из экспортируемой функции структуру по значению... ну, я бы за это кастрировал, если только этому нет _очень_ веских оснований. И это меньшее, что можно сделать, нельзя допускать, чтобы такие люди размножались
А>Получается, что в этом случае единственный способ узнать об ошибке это использовать DL_DECLARE_FUN_THROW, что ИМХО не очень удобно — ставить вокруг каждого вызова функции из dll-ки блок try-catch.
Ровно так же это единственный способ узнать об ошибке в случае void функций — так что других вариантов тут нет. Инстанцировать шаблон с типизированым параметром можно только интегральным литералом, указателем на объект с external linkage (например, функцию), либо NULL для указателя. Если у вас есть какие-то лучшие предложения (с реализацией, конечно, напредлагать просто так и я сам могу) — u are welcome.
А>Получается, что в этом случае единственный способ узнать об ошибке это использовать DL_DECLARE_FUN_THROW, что ИМХО не очень удобно — ставить вокруг каждого вызова функции из dll-ки блок try-catch.
Совсем забыл сказать — у вас еще есть вариант использовать DL_DECLARE_FUN_ERR_POLICY(name_id, r, p, pl), где задать свою политику, допускающую возвращение структур. Например, такую:
Штука понравилась, но не смог сослаться на функцию с переменным количеством параметров, в следствие чего не стал использовать для решения одной задачи. Обыдна... Может я чего не досмотрел???
Здравствуйте, Plague, Вы писали:
P>Штука понравилась, но не смог сослаться на функцию с переменным количеством параметров, в следствие чего не стал использовать для решения одной задачи. Обыдна... Может я чего не досмотрел???
STDCALL использовавшийся до последнего времени в библиотеке не поддерживает переменное число аргументов. Сейчас есть поддержка нескольких конвенций. Переменное число аргументов поддерживает CDECL
P>>Штука понравилась, но не смог сослаться на функцию с переменным количеством параметров, в следствие чего не стал использовать для решения одной задачи. Обыдна... Может я чего не досмотрел???
Нет, все верно. stdcall традиционно не поддерживает переменное число агрументов, поскольку стек очищает сама функция. cdecl может поддерживать переменное число агрументов, однако в библиотеке переменное число агрументов не поддерживается вообще. Используйте va функции для передачи переменного числа агрументов, если оные, конечно, имеются. Еще где-то в исходниках были еще варианты динамической загрузки — может там что поможет.
A>STDCALL использовавшийся до последнего времени в библиотеке не поддерживает переменное число аргументов. Сейчас есть поддержка нескольких конвенций. Переменное число аргументов поддерживает CDECL
См. выше, это не так. Переменное число агрументов не может быть поддержано библиотекой архитектурно — для этого простыми методами не обойтись — надо подменять стек.
Здравствуйте, Andrew S, Вы писали:
AS>Нет, все верно. stdcall традиционно не поддерживает переменное число агрументов, поскольку стек очищает сама функция. cdecl может поддерживать переменное число агрументов, однако в библиотеке переменное число агрументов не поддерживается вообще. Используйте va функции для передачи переменного числа агрументов, если оные, конечно, имеются. Еще где-то в исходниках были еще варианты динамической загрузки — может там что поможет.
Просто есть DLL одной малоизвесной SQL базы данных, и было желание написать "враппер", т.к. вся работа шла через одну DLL. Так там экспортировались функции с переменным количеством аргументов , было б неплохо хотябы "подпорку" написать, что для всех как обычно функций, а для таких "с извратами". Чтоб для можно было использовать для _любых_ С-функций.
AS>>Нет, все верно. stdcall традиционно не поддерживает переменное число агрументов, поскольку стек очищает сама функция. cdecl может поддерживать переменное число агрументов, однако в библиотеке переменное число агрументов не поддерживается вообще. Используйте va функции для передачи переменного числа агрументов, если оные, конечно, имеются. Еще где-то в исходниках были еще варианты динамической загрузки — может там что поможет.
P>Просто есть DLL одной малоизвесной SQL базы данных, и было желание написать "враппер", т.к. вся работа шла через одну DLL. Так там экспортировались функции с переменным количеством аргументов , было б неплохо хотябы "подпорку" написать, что для всех как обычно функций, а для таких "с извратами". Чтоб для можно было использовать для _любых_ С-функций.
Не получится. Проблема в прокси — там требуется знать количество параметров при определении. Ну и сама идеология "ленивой" загрузки не позволяет это делать. Впрочем, все ясно из кода...
Здравствуйте, Andrew S, Вы писали:
AS>Не получится. Проблема в прокси — там требуется знать количество параметров при определении. Ну и сама идеология "ленивой" загрузки не позволяет это делать. Впрочем, все ясно из кода...
В принципе некоторое количество ассемблера решило бы проблему
AS>>Не получится. Проблема в прокси — там требуется знать количество параметров при определении. Ну и сама идеология "ленивой" загрузки не позволяет это делать. Впрочем, все ясно из кода...
A>В принципе некоторое количество ассемблера решило бы проблему
Некоторое количество машинных команд может решить любую программистскую проблему. Вот только вопрос в том, как правильно собрать эти команды. В рамках библиотеки использовать ассемблерные вставки, очевидно, смысла нет — 99% задач решается и без них, для остального есть поддерживаемый компилятором делейлоад, либо резолвинг ручками. Смысла терять в переносимости библиотеки из-за сомнительной поддержки переменного числа параметров я не вижу Написать ручками обертку на загружаемую библиотеку один раз — имхо не проблема.
Поскольку сразу через 2 версии, то в порядке очереди.
Итак, версия 1.3.0. (предыдущая)
Список изменений:
1. Добавлена поддержка конвенций вызова. Непосредственно в библиотеке поддерживаются WINAPI и __cdecl.
Итак, как все это работает. За поддержку встроенных конвенций отвечают макросы
#define DL_NO_STDCALL // не инстанцировать поддержку winapi соглашений о вызовах#define DL_USE_CDECL // инстанцировать поддержку __cdecl (по умолчанию отключено)
За поддержку вообще любых конценций отвечают макросы
Назначение и параметры макросов DL_DECLARE_FUN_xxx_CC аналогичны DL_DECLARE_FUN_xxx, за исключением того, что первым параметром добавляется конвенция о вызове функции. Т.о., на самом деле это generic интерфейс к библиотеке, которым пользуются все остальные макросы.
1. Добавлена поддержка числовых идентификаторов функций (by ord), а также возможность переименования (алиаса) для функций. Для этого определен расширенный набор макросов DL_DECLARE_FUN_...._EX. Список параметров аналогичен обычному набору, за исключением необходимости задавать и имя , и идентификатор функции (ижентификатор это то, что получает GetProcAddr).
Например,
2. Добавлена политика ошибок CFunProxyDefPolicy — в отличие от CFunProxyValuePolicy, позволяет работать не только со встроенными типами. Данная политика используется в макросах DL_DECLARE_FUN(_EX) вместо CFunProxyValuePolicy<ret_type>.