Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

easily refactoring many non-trivial "static library projects" to "dll projects"

I have 6 static library projects :-

- 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.)    

I succeeded in using 6 projects via "static libraries" technique.

Today, I heard that dynamic library can reduce compilation time (Let's not discuss whether this is true),
so I read these below links and successfully create a small demo.

  • https://learn.microsoft.com/en-us/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=vs-2019 (official)
  • https://www.badprog.com/c-windows-creating-a-dynamic-link-library-dll (alternative)
  • How do I build an import library (.lib) AND a DLL in Visual C++? (troubleshooting)

Here is some code in my test demo :-

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

Now, it is exciting time to apply it to my real projects.

At the first step, I want to export all functions and classes of my 6 libraries.
I assume that I have to add SomeMacro1 (different for each project) to every function in all 6 projects (~100K lines), right?

That is a huge refactoring.
Are there any easier way? Do I miss something very important?

Other notes :-

  • I want to switch my library projects back to static library easily (in case something go wrong).
  • I prefer a cross platform solution. (e.g. no pervasive refactoring need if I want to run in Linux later)
  • I prefer a solution that when I cut-and-paste a source file from one project to another, the cost of refactoring (in code) does not increase from normal.
    (SomeMacro1 is currently specific to a project)

Similar question : How to convert a static library project into a dll project in VS2005

Bounty Reason

Thank Andriy Tylychko's answer that provides useful cautions and suggests that refactoring would be inevitably complicated, but I still believe there are some easy ways to refactor my projects.

Now, I wish to change my library projects to dynamic libraries. (faster compilation)
Then when I ship my product, I will convert them back to static library. (better performance)

Edit: Bounty awards to Robert Andrzejuk because of his link in comment. (https://learn.microsoft.com/en-us/cpp/cpp/using-dllimport-and-dllexport-in-cpp-classes?view=vs-2019)
It may sound simple, but I have never known that I can __declspec(dllexport) at class level.
Although that is not my dream, it makes a lot of things easier.

like image 377
cppBeginner Avatar asked Feb 03 '23 18:02

cppBeginner


2 Answers

Strictly speaking, converting to DLLs will reduce mainly linking time, but you can notice (subtle in most cases) performance degradation because now "whole program optimisation" has much less room for optimisations, e.g. inlining functions across different binaries.

Dynamically linked libraries and statically linked libraries are quite different beasts, DLLs require much more attention. You need to carefully design their public API and define it accordingly (usually by macros like ones you presented). E.g. it's not recommended to use standard library types (and many others) in DLL public API that depend on particular type of C-runtime (like debug and release) - mainly it's about allocating memory in one binary and releasing in another that should be avoided. But this is not always a problem.

It doesn't make much sense to switch back and forth between static and dynamic libs.

Static libraries are usually much simpler and don't care about public API as everything is automatically accessible from outside. E.g. Math libraries are usually static as almost all functionality should be exported anyway and they (usually) don't have any complicated internal "business" logic.

Bigger libraries with "business" logic that you'd like to hide (e.g. to have more freedom to modify it) and with well defined public API bring long-term benefits. E.g. you can update only a small DLL instead of huge monolitic EXE if the API wasn't changed.

As it's hard to carefully plan ahead, the need to convert static lib to DLL (once) happens quite often as the project matures. In this case you don't need to export every class and function but to carefully select what exactly should be exported and ideally refactor existing API to better suit new reality. Luckily it seems you don't have any circular dependencies as they could add a lot of headache.

Refactoring inevitably becomes more complicated across DLLs, but this shouldn't be a big issue. If your lib looks like it should be a DLL - make it a DLL, advantages will be bigger than a disadvantage of a not so easy copy/pasting.

From your example, with very limited knowledge so it's pure guessing, it looks like only Physics and maybe Graphics should be DLLs, probably BaseGame too.

like image 112
Andriy Tylychko Avatar answered Feb 06 '23 12:02

Andriy Tylychko


To reduce the refactoring required by marking all the free functions for export/import, an easier way is when the functions are inside a class, and just the class has to be marked for export/import, then all the functions are exported also.

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


In Visual Studio to configure a project to differ depending on the target build type I suggest to use "Configurations". By default prepared are:

  • Debug
  • Release

It is possible to create new configurations.

For this example let's consider that

  • Debug version should use a dynamic dll,
  • Release should use a static dll

Too make a project be either static lib or dll (let's call that a "mixed-lib") then in the Properties -> General -> Configuration Type, it is possible to select:

  • Dynamic dll (.dll)
  • Static library (.lib)
  • ...

So for the "Debug" project select a "Dynamic Library".

And for "Release" project select a "Static library".

(Please beware - by changing the project type, not all the settings are appropriatly modifed!! Everything has to be double-checked.)

Depending on the selected configuration, there can be different settings. For example on the preprocessor tab, there are different definitions.

So if Debug is selected, a definition "_DEBUG" is available. In Release "NDEBUG" is available. These definitions can be used inside the code:

#ifdef _DEBUG

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

#else

#define SomeMacro1

#endif

SomeMacro1 void someFunction(int someParam);

These Confiugrations have to be appropriately configured in both the mixed-dll and the application.

like image 24
Robert Andrzejuk Avatar answered Feb 06 '23 10:02

Robert Andrzejuk