#c
#c
Вопрос:
Допустим, у меня есть эта библиотека
//// testlib.h
#pra&ma once
#include <iostream&&t;
void __declspec(dllexport) test();
int __declspec(dllexport) a();
Если я опущу определение для a()
и test()
из моего testlib.cpp библиотека все еще компилируется, потому что интерфейс [1] все еще действителен. (Очевидно, что если я использую их из клиентского приложения, они не будут связываться)
Есть ли способ гарантировать, что при создании obj (который, как я понимаю, является задачей компилятора) он действительно ищет определения функций, которые я явно экспортировал, и завершается ошибкой, если этого не происходит?
Это не связано с какой-либо реальной проблемой. Просто любопытно.
[1] Документы MSVC
Ответ №1:
Нет, это невозможно.
Частично потому, что dllexport
объявление может юридически даже не быть реализовано в той же DLL, не говоря уже о библиотеке, а быть простым предварительным объявлением для чего-то, предоставляемого еще одной DLL.
Особенно невозможно принять решение на уровне объекта. Это просто еще одно прямое объявление, подобное любому другому.
Вы можете удалить экспортированные символы после того, как DLL была связана, но общего инструмента для проверки полноты не существует.
В конечном счете, вы не можете обойтись без клиентского тестового приложения, которое пытается загрузить все экспортированные интерфейсы. Вы пока не можете проверить это во время компиляции. Недостаточно даже просто успешно связать тестовое приложение, вы должны фактически запустить его.
Становится еще хуже, если есть загруженные с задержкой библиотеки DLL (и да, обычно они есть), потому что теперь вы даже не можете проверить полноту, если вы на самом деле не вызываете хотя бы один символ из каждой задействованной библиотеки DLL.
Такие инструменты, как средство обхода зависимостей и т.д., существуют именно по этой причине.
Комментарии:
1. Принято, потому что я чувствую, что это самый полезный и достаточно полный ответ.
Ответ №2:
Вы спросили
«Есть ли способ, которым я могу гарантировать, что при создании obj (который, как я понимаю, является задачей компилятора) он действительно ищет определения функций, которые я явно экспортировал, и завершается ошибкой, если этого не происходит?»
Когда вы загружаете DLL, фактическая привязка функции выполняется во время выполнения (позднее привязывание функций), поэтому компилятор не может узнать, доступно ли определение функции в DLL или нет. Надеюсь, это ответ на ваш запрос.
Ответ №3:
Как мне (программно) убедиться, что dll содержит определения для всех экспортируемых функций?
В общем, в стандартном C 11 (прочитайте документ n3337 и посмотрите эту ссылку на C ) вы не можете
Потому что C 11 не знает о DLL или динамическом связывании. Иногда может иметь смысл динамически загружать код, который не определяет функцию, которая, как вы прагматично знаете, никогда не будет вызвана (например, неполная DLL для рисования фигур, но вы случайно знаете, что круги никогда не будут нарисованы в вашем конкретном использовании, поэтому загруженная DLL может не определять какой-либо class Circle
связанный код. В стандартном C 11 каждая вызываемая функция должна быть где-то определена (в какой-то другой единицеперевода).
Посмотрите также на Qt vision плагинов.
Прочитайте книгу Левайна «Компоновщики и загрузчики«. Обратите внимание, что в Linux плагины, загружаемые с помощью dlopen(3), имеют другую семантику, чем библиотеки DLL Windows. Зло кроется в деталях
На практике вы могли бы рассмотреть возможность использования какого-нибудь последнего варианта компилятора GCC и разработать свой плагин GCC для проверки этого. Это может потребовать нескольких недель работы.
В качестве альтернативы, адаптируйте Clan& static analyzer для ваших нужд. Опять же, запланируйте несколько недель работы.
Посмотрите также этот черновик отчета и подумайте о кросс-компиляторах C (например, о компиляции в Windows плагина для RaspBerry Pi)
Рассмотрите также фреймворки генерации кода во время выполнения, такие как asmjit или lib&ccjit. Вы могли бы подумать о создании во время выполнения отсутствующих заглушек или функций (и соответствующим образом заполнить ими указатели на функции или даже vtables). Исключения C также могут быть дополнительной проблемой.
Ответ №4:
Если ваша DLL содержит вызовы функций, компоновщик завершится с ошибкой, если для этих функций не будет предоставлено определение. Не имеет значения, если вызовы никогда не выполняются, важно только, что они существуют.
//// testlib.h
#pra&ma once
#include <iostream&&t;
#ifndef DLLEXPORT
#define DLLEXPORT(TYPE) TYPE __declspec(dllexport)
#endif
DLLEXPORT(void) test();
DLLEXPORT(int) a();
//// testlib_verify.c
#define DLLEXPORT(TYPE)
void DummyFunc()
{
#include testlib.h
}
Это решение на основе макросов работает только для функций без параметров, но его должно быть легко расширить.