По моему, все понятно, последовательно и логично изложено, имеются исходники и листинти. От «ученика» необходимо только желание разобраться. Хорошая статья.
К сожалению, наблюдается тенденция к ухудшению поиска в Яндексе. Мне больше нравился Яндеск, но все чаше приходиться пользоваться Гуглом, т.к. поиск в нем оказывается релевантнее.
Библиотеки с открытым кодом, такие как boost или QT, которые считаются неотъемлемой частью разработки и которые используют огромное количество людей, не лишены ошибок. Но все, кто используют библиотеки, являются одновременно огромной армией тестеров, находящих коллизии и баги. Поэтому используя открытые и широко распространенные библиотеки, мы получаем все таки достаточно надежные инструменты для программирования.
Работу с числами с плавающей запятой, обеспечивает математический сопроцессор (FPU). FPU имеет специальные регистры, организованные в кольцевой стек. Вершиной стека является регистр ST(0). Возвращаемое значение функции epsilon() попадaет не в eax, а в ST(0) FPU.
> Я так понимаю, что под Linux, например, бросать исключение, пересекающее границу динамической библиотеки, в случае, если одна библиотека собрана GCC4, а другая GCC3, также опасно, или нет?
Не работал под Linux. Не знаю. Но, имхо, правила применимости те же (возможно, есть еще что).
>А какие в винде проблемы с исключениями?
С какими: SEH или исключения C++?
С SEH проблем нет, а вот с С++ есть (при межмодульном взаимодействии EXE-DLL, DLL-DLL):
1) у каждого компилятора может быть своя реализация механизма исключений;
2) типы данных (в данном случае внутренне представление классов исключений может быть по-разному определено);
3) даже, если компилятор один и внутреннее представление одно и то же, менеджер памяти может быть разным.
Такой вариант рассматривался. И его проще реализовать. Но при таком варианте требуется как-то прикручивать кодогенератор (препроцессор) к среде (настраивать систему сборки). Это, конечно, не проблема, но хотелось чего-то, что будет работать в рамках возможностей языка C++.
Код необычен, это правда. Кто работал с BOOST.PREPROCESSOR поймет почему он такой. Кто не работал, тому он не покажется очень сложным. А кто не хочет — это уж его проблемы. Читайте второй абзац топика — мы делимся уже полученным результатом.
Страшно что? Количество кода или его внешний вид. Количество кода минимально, если у Вас есть варианты сделать тоже самое, но с меньшим количеством кода (без применения дополнительных преобразователей исходного текста, типа moc-компилятора в QT и пр.) приложите, пожалуйста, код. Внешний вид странноват, уже сказано. Но он читаем и понятен.
Основная идея создания данной библиотеки — возможность использования классов в межмодульном взаимодействии. Может в документации это и не отражено, но интерфейс DL_INTERFACE предназначен не только для экспорта классов из DLL, но и как общий интерфейс, который может реализовывать классы EXE и передавать их в DLL. (про плагины — это ответ на вопрос о динамической линковке, читайте внимательно).
Есть категория людей, которая:
а) не использует исключения при написании DLL (или использует, но не знает что, с ними нужно делать на границах DLL-EXE, DLL-DLL);
б) не использует классы в межмодульном взаимодействии;
в) для всего проекта всегда использует один и тот же компилятор, всегда с одними и теми же настройками, и, к тому же, всегда используется динамический рантайм.
г) не использует пространства имен — единственное средство С++, позволяющее хоть как-то получить подобие модулей в программе.
Для такой категории людей библиотека DynLib, действительно, не подходит, а добавляет только дополнительный «оверхед».
DLL, собранные с использованием DL_LIBRARY и DL_EXPORT, всегда в таблице экспорта содержат только одну функцию (_dynamic_library@0 — MSVC и dynamic_library@0 — GCC). При проектировании DynLib одним из ключевых моментов было избавить пользователя библиотеки от лишних (и зачастую рутинных) действий при написании своей DLL, в частности убрать необходимость задавать def-файл. Два разных имени одной и той же функции — следствие реализации разных способов именования функций при экспорте для разных компиляторов без def-файла.
Все функции, которые описаны в DL_LIBRARY находятся в ::dlx::library<dl_library>::ftable.
> Т.е. никакого C-интерфейса нет и кроме как через эту обёртку длл-ку не заиспользовать
Как раз наоборот, все функции библиотеки и интерфейсов передаются через C-совместимые типы через структуры, напоминающие VTable в языке C++.
Но, действительно, подключение и использование таких DLL без DynLib потребует описания необходимых С-структур самостоятельно.
Несложно определить, что для этого используется RTTI для получения имени класса исключения и строка, возвращаемая через метод what(). Это, конечно, не лучший способ общего решения (особенно для тех компиляторов, в которых typeid(...).name() возвращает нечитаемое имя класса).
Естественно, можно позволить пользователю библиотеки писать свою реализацию функции create_error_info через callback-функции или специализацию шаблонной функции (класса). И для этого, конечно, нужно будет пересмотреть состав структуры error_info, хранящую всю необходимую информацию об исключении.
Если это действительно необходимо, то, по возможности, будем улучшать DynLib.
В основном, для возможности использовать несколько DLL, имеющих один и тот же интерфейс, в приложениях, использующих так называемую «плагинную» схему.
Применительно к DLL, созданным с применением DL_LIBRARY и DL_EXPORT — это скрытие информации об экспортируемых функциях. Такие DLL в своей таблице экспорта содержат упоминание только об одной функции dynamic_library.
2. >Чем не устраивает LoadLibrary/GetProcAddress
Устраивает, но вот код с LoadLibrary/GetProcAddress
А вот с использованием DynLib
Попрбуйте на практике, хотя бы повторить пример из статьи, и все встанет на свои места.
Не работал под Linux. Не знаю. Но, имхо, правила применимости те же (возможно, есть еще что).
А можно воспользоваться DL_C_LIBRARY ;)
С какими: SEH или исключения C++?
С SEH проблем нет, а вот с С++ есть (при межмодульном взаимодействии EXE-DLL, DLL-DLL):
1) у каждого компилятора может быть своя реализация механизма исключений;
2) типы данных (в данном случае внутренне представление классов исключений может быть по-разному определено);
3) даже, если компилятор один и внутреннее представление одно и то же, менеджер памяти может быть разным.
Разные компиляторы — разная декорация имен функций.
Страшно что? Количество кода или его внешний вид. Количество кода минимально, если у Вас есть варианты сделать тоже самое, но с меньшим количеством кода (без применения дополнительных преобразователей исходного текста, типа moc-компилятора в QT и пр.) приложите, пожалуйста, код. Внешний вид странноват, уже сказано. Но он читаем и понятен.
Основная идея создания данной библиотеки — возможность использования классов в межмодульном взаимодействии. Может в документации это и не отражено, но интерфейс DL_INTERFACE предназначен не только для экспорта классов из DLL, но и как общий интерфейс, который может реализовывать классы EXE и передавать их в DLL. (про плагины — это ответ на вопрос о динамической линковке, читайте внимательно).
Есть категория людей, которая:
а) не использует исключения при написании DLL (или использует, но не знает что, с ними нужно делать на границах DLL-EXE, DLL-DLL);
б) не использует классы в межмодульном взаимодействии;
в) для всего проекта всегда использует один и тот же компилятор, всегда с одними и теми же настройками, и, к тому же, всегда используется динамический рантайм.
г) не использует пространства имен — единственное средство С++, позволяющее хоть как-то получить подобие модулей в программе.
Для такой категории людей библиотека DynLib, действительно, не подходит, а добавляет только дополнительный «оверхед».
Именно так (и только о компиляторах C++).
Все функции, которые описаны в DL_LIBRARY находятся в ::dlx::library<dl_library>::ftable.
> Т.е. никакого C-интерфейса нет и кроме как через эту обёртку длл-ку не заиспользовать
Как раз наоборот, все функции библиотеки и интерфейсов передаются через C-совместимые типы через структуры, напоминающие VTable в языке C++.
Но, действительно, подключение и использование таких DLL без DynLib потребует описания необходимых С-структур самостоятельно.
в каждой экспортируемой библиотекой функции и в каждом методе интерфейса вставляется следующий код:
Функция create_error_info определена следующим образом:
Несложно определить, что для этого используется RTTI для получения имени класса исключения и строка, возвращаемая через метод what(). Это, конечно, не лучший способ общего решения (особенно для тех компиляторов, в которых typeid(...).name() возвращает нечитаемое имя класса).
Естественно, можно позволить пользователю библиотеки писать свою реализацию функции create_error_info через callback-функции или специализацию шаблонной функции (класса). И для этого, конечно, нужно будет пересмотреть состав структуры error_info, хранящую всю необходимую информацию об исключении.
Если это действительно необходимо, то, по возможности, будем улучшать DynLib.
Применительно к DLL, созданным с применением DL_LIBRARY и DL_EXPORT — это скрытие информации об экспортируемых функциях. Такие DLL в своей таблице экспорта содержат упоминание только об одной функции dynamic_library.