легко рефакторинг многих нетривиальных «проектов статической библиотеки» в «проекты dll» - PullRequest
9 голосов
/ 29 мая 2019

У меня есть 6 проектов статических библиотек: -

- Math
- ECS             : depends on Math
- Utility         : depends on ECS
- Physics         : depends on Utility         
- Graphics        : depends on Utility     
- BaseGame        : depends on Physics and Graphics         
- Some game (.exe): depends on BaseGame      
(The "depends" here is transitive e.g. BaseGame also depends on ECS.)    

Мне удалось использовать 6 проектов с помощью техники "статических библиотек".

Сегодня я слышал, что динамическая библиотека может сократить время компиляции (давайте не будем обсуждать, так ли это),
, поэтому я прочитал эти ссылки ниже и успешно создал небольшую демонстрацию.

Вот некоторый код в моей тестовой демонстрации: -

#ifdef SomeName1_EXPORTS
#define SomeMacro1 __declspec(dllexport) 
#else
#define SomeMacro1 __declspec(dllimport) 
#endif
SomeMacro1 void someFunction(int someParam);

Теперь это захватывающее времяприменить это к моим реальным проектам.

На первом шаге я хочу экспортировать все функции и классы моих 6 библиотек.
Я предполагаю, что мне нужно добавить SomeMacro1 (разные для каждого проекта) к каждой функции во всех 6 проектах(~ 100К строк), верно?

Это огромный рефакторинг.
Есть ли более простой способ?Я скучаю по чему-то очень важному?

Другие примечания : -

  • Я хочу легко переключить свои библиотечные проекты обратно на статическую (на случай, если что-то пойдет не так).
  • Я предпочитаю кроссплатформенное решение.(например, нет необходимости в повсеместном рефакторинге, если я хочу запустить в Linux позже)
  • Я предпочитаю решение, которое, когда я вырезал и вставлял исходный файл из одного проекта в другой, стоило бы рефакторинга (в коде)не увеличивается от нормального.
    (SomeMacro1 в настоящее время относится к проекту)

Аналогичный вопрос: Как преобразовать проект статической библиотеки в проект dll в VS2005

Причина вознаграждения

Спасибо Ответ Андрея Тиличко , который содержит полезные предостережения и предполагает, что рефакторинг будет неизбежно сложным, но я все еще верюЕсть несколько простых способов реорганизации моих проектов.

Теперь я хочу изменить свои библиотечные проекты на динамические библиотеки.(более быстрая компиляция)
Затем, когда я отправляю свой продукт, я преобразую его обратно в статическую библиотеку.(лучшая производительность)

Редактировать: Награды Роберту Анджеюку за его ссылку в комментарии.(https://docs.microsoft.com/en-us/cpp/cpp/using-dllimport-and-dllexport-in-cpp-classes?view=vs-2019)
Это может показаться простым, но я никогда не знал, что могу __declspec(dllexport) на уровне класса.
Хотя это не моя мечта, это облегчает многие вещи.

Ответы [ 4 ]

4 голосов
/ 31 мая 2019

Строго говоря, преобразование в DLL-файлы в основном сократит связывание времени, но вы можете заметить (в большинстве случаев едва заметное) снижение производительности, поскольку теперь «оптимизация всей программы» имеет гораздо меньше возможностей для оптимизации, например, встроенные функции в разных двоичных файлах.

Динамически связанные библиотеки и статически связанные библиотеки - совершенно разные звери, библиотеки DLL требуют гораздо большего внимания. Вам необходимо тщательно спроектировать их общедоступный API и определить его соответствующим образом (обычно с помощью макросов, подобных представленным вами). Например. не рекомендуется использовать стандартные типы библиотек (и многие другие) в публичном API DLL, которые зависят от конкретного типа среды выполнения C (например, отладки и выпуска) - в основном речь идет о выделении памяти в одном двоичном файле и освобождении в другом, чего следует избегать. Но это не всегда проблема.

Нет особого смысла переключаться между статическими и динамическими библиотеками.

Статические библиотеки обычно намного проще и не заботятся об общедоступном API, поскольку все автоматически доступно извне. Например. Математические библиотеки обычно являются статическими, поскольку почти все функции должны быть экспортированы в любом случае, и они (как правило) не имеют сложной внутренней "бизнес" логики.

Большие библиотеки с «бизнес-логикой», которую вы хотели бы скрыть (например, чтобы иметь больше свободы для ее изменения) и с четко определенным общедоступным API, приносят долгосрочные выгоды. Например. Вы можете обновить только небольшую DLL вместо огромного монолитного EXE, если API не был изменен.

Поскольку сложно тщательно планировать заранее, необходимость конвертировать статическую библиотеку в DLL (один раз) возникает довольно часто по мере развития проекта. В этом случае вам не нужно экспортировать каждый класс и функцию, но нужно тщательно выбирать, что именно нужно экспортировать, и в идеале реорганизовать существующий API, чтобы лучше соответствовать новой реальности. К счастью, кажется, что у вас нет циклических зависимостей, поскольку они могут добавить много головной боли.

Рефакторинг неизбежно усложняется в библиотеках DLL, но это не должно быть большой проблемой. Если ваша библиотека выглядит так, как будто она должна быть DLL - сделайте ее DLL, то преимущества будут больше, чем недостаток нелегкого копирования / вставки.

Из вашего примера, с очень ограниченными знаниями, так что это просто догадки, похоже, что только физика и, возможно, графика должна быть DLL, вероятно, BaseGame тоже.

3 голосов
/ 07 июня 2019

Чтобы уменьшить рефакторинг, необходимый путем пометки всех свободных функций для экспорта / импорта, более простой способ - когда функции находятся внутри класса, и только класс должен быть помечен для экспорта / импорта, тогда все функции экспортируютсятакже.

https://docs.microsoft.com/en-us/cpp/cpp/using-dllimport-and-dllexport-in-cpp-classes?view=vs-2019


В Visual Studio для настройки проекта можно различаться в зависимости от целевого типа сборки, я предлагаю использовать «Конфигурации».По умолчанию подготовлены:

  • Отладка
  • Выпуск

Возможно создание новых конфигураций.

Для этого примера давайте рассмотримчто

  • Отладочная версия должна использовать динамическую dll,
  • В выпуске должна использоваться статическая dll

Слишком сделать проект либо статической lib, либо dll (давайте назовем это «mixed-lib»), тогда в Свойствах -> Общие -> Тип конфигурации можно выбрать:

  • Динамическая DLL (.dll)
  • Статическийбиблиотека (.lib)
  • ...

Поэтому для проекта «Отладка» выберите «Динамическая библиотека».

А для проекта «Выпуск» выберитеa «Статическая библиотека».

(Пожалуйста, будьте осторожны - при изменении типа проекта не все настройки должным образом модифицируются !! Все должно быть перепроверено.)

В зависимости от выбранногоКонфигурация, могут быть разные настройки.Например, на вкладке препроцессора есть разные определения.

Таким образом, если выбран вариант Debug, доступно определение "_DEBUG".В Релизе "NDEBUG" доступен.Эти определения могут использоваться внутри кода:

#ifdef _DEBUG

#ifdef SomeName1_EXPORTS
#define SomeMacro1 __declspec(dllexport) 
#else
#define SomeMacro1 __declspec(dllimport) 
#endif

#else

#define SomeMacro1

#endif

SomeMacro1 void someFunction(int someParam);

Эти конфигурации должны быть соответствующим образом сконфигурированы как в mixed-dll, так и в приложении.

2 голосов
/ 04 июня 2019

Вам не нужно добавлять макрос в определения функций, просто все объявления, сделанные в заголовочных файлах, которые используются другими модулями компиляции.Для функций, определенных в заголовочных файлах, функцию не нужно экспортировать, так как исходный источник уже доступен в заголовке.

Это самый простой и независимый от платформы способ.Как уже упоминалось, файл .def также может работать в Visual Studio, но вы должны перечислить каждое имя функции дважды, так что это не страшно СУХОЙ.

Честно говоря, 100К строк кода не так уж и плохо.Возможно, вам удастся использовать регулярное выражение для выполнения большей части работы за вас.

Я вижу, что есть способ использовать dumpbin для создания файла .def для вас. В этом комментарии упоминается способ создания файла .def для всех определений функций в ваших файлах .o .

Вам нужно только добавить макрос в объявления функций, а не только в определения.Вам также нужно будет решить, как экспортировать классы.

Как уже упоминалось, до тех пор, пока у вас есть отдельные модули компиляции для проекта, а не просто включены все источники в один модуль компиляции (файлы * .o создаются на каждую единицу компиляции), dll не будет улучшать компиляцию.время выполнения вообще.

1 голос
/ 06 июня 2019

Теперь я хочу изменить свои библиотечные проекты на динамические библиотеки.(более быстрая компиляция) Затем, когда я отправляю свой продукт, я преобразую его обратно в статическую библиотеку.(лучшая производительность)

Библиотеки динамических ссылок отличаются от статических библиотек и на самом деле не дают преимущества для "более быстрой компиляции", кроме потенциального выигрыша во времени компоновки, поскольку вы, по сути, собираетесь предположительносвязать меньшие модули (n # dlls + 1 exe), а не 1 большой exe.Если вы в любом случае планируете статически связать .lib с вашим exe-файлом, то это в значительной степени сводит на нет цель преобразования вещей в dll, поскольку вы все равно не планируете использовать dll.

Если вы хотелиРазбить проект на части, чтобы вы могли заменить отдельные модули в проекте, тогда это аргумент для перехода к DLL.Например, вы хотите изменить Graphics без необходимости повторного связывания всего исполняемого файла, вы можете обернуть «Graphics» в Graphics.dll, сделать .exe зависимым от Graphics.dll, а затем, когда вы вносите изменения в Graphics, вы можете просто собратьи замените Graphics.dll вместо всего исполняемого файла локально, чтобы сократить время, необходимое для изменения / проверки чего-либо.Это может быть большим приростом производительности, если exe требует вечных ссылок.

Для Windows конкретно:

Учитывая ваши ссылки, я делаю следующие предположения:

  • Вы разрабатываете для Windows
  • Вы используете какую-то версию Visual Studio и компилятор Microsoft C.

Если вы используете GL или LTCG помечают над exe, тогда есть небольшой аргумент, чтобы не создавать dll, так как gl / ltcg применяется к модулю, а объединение всех ваших статических ссылок libs даст вам больший выигрыш с GL / LTCGхотя для связывания требуется больше времени (не рекомендуется для сборок типа отладки).

Если вы хотите обернуть библиотеки в dll, вы можете изучить создание dll с помощью C ++ / WinRT , которая поддерживаетв современных версиях Visual Studio или если по какой-либо причине вы не можете запустить среду выполнения Windows, вы можете посмотреть COM dll .

...