Original Post
[caution] Bit of a lengthy post, but I'd really appreciate your time/thoughts! Evening all, Simply put I've got a large monolithic C++ project/solution that I really need to break up into components. Currently its around 30,000 lines spread over about a 100 files - with the next phase of development likely to triple or quadruple that. I'll fully admit to lazy/bad design, but its starting to get a bit too big, and with more developers due to start work on it soon I can only forsee more problems [headshake] Thankfully the game has a pretty simple architecture - mostly modular/procedural with a few classes thrown in where appropriate. The worst part is that the "engines" are represented as singletons which I've heard aren't too compatable with DLL's - but I'm pretty sure I can get rid of this without much hassle. So, I know theres an awful lot of very experienced programmers kicking around - this is a simple call for comments/experiences. I'm pretty sure I've evaluated all possibilities, but I don't really want to get most of the way through a conversion to find I've missed something obvious and wasted my time. Okay, basic outline is to group each module into its own solution/project (I'm using VC++ 2005 if it matters) and setting it to save/output shared files to a common location. Example:
\Game\
\Components\
\Graphics\
Graphics.sln
Graphics.vcproj
Graphics.cpp
\LogFile\
LogFile.sln
LogFile.vcproj
LogFile.cpp
\GUI\
GUI.sln
GUI.vcproj
GUI.cpp
\Config\
Config.sln
Config.vcproj
Config.cpp
\Utilities\
Utils.sln
Utils.vcproj
Utils.cpp
\Include\
Graphics.h
LogFile.h
GUI.h
Config.h
Utils.h
\Lib\
Graphics.lib
LogFile.lib
GUI.lib
Config.lib
Utils.lib
\Bin\
Graphics.dll
LogFile.dll
GUI.dll
Config.dll
Utils.dll
Game.exe
Game.sln
Game.vcproj
Game.cpp
The real thing will have many more files, but thats the rough idea. For the core 'Game' solution, each part that relies on one of the componentized modules could have the following directives: #include "Include\\Graphics.h"
#pragma comment( lib, "Lib\\Graphics.lib" ) If the main game as well as all the DLL's are output to the \Bin\ sub-folder then they should all happily find each other on start-up. I've not thought about it extensively, but I'd imagine that all other files (data, config, art, audio...) would be located as a sub-folder of this. Arguably not too clean, but it should work with minimal changes to the current code-base and thats more important to me right now [wink] I've run a few tests with throwing data between a simple console .exe and a .dll to make sure things work and so far I've had no problems with: - Simple function calling
- Simple variable sharing
- Class instantiation
- Simple function pointer / callback usage
- Complex functor / member-function callback usage
- Passing STL containers around - I use std::wstring quite a bit.
class IBaseControl { ... };
class IButton : public IBaseControl { ... };
bool CreateButton( IButton *p ); And the actual implementation (not visible outside of the DLL) is: class CBaseControl : public IBaseControl { ... };
class CButton : public CBaseControl, public IButton { ... };
bool CreateButton( IButton *p )
{
if( NULL != p ) delete p;
p = new CButton( ... );
} So far everything has worked just fine, no compile errors/warnings and no runtime problems - desired results all round [grin] But its only a simple proof of concept. I'm not sure if it's so simple as to be hiding any complexities that'll only be revealed when I try and refactor the main codebase. One BIG concern I have (which I'll try testing in a minute) is where components start "talking" to each other. Example scenario: The main game creates and initializes the graphics engine and builds up a GUI by instantiating the various controls imported from the DLL. The application then wants the GUI to be displayed on screen, so calls things like IBaseControl::Draw(). The GUI imports the functionality from the graphics engine (by including the public header and linking to the lib) and makes various calls to render itself appropriately. So we have the main .exe talking to a DLL as well as a DLL talking to a DLL. How many copies of the graphics engine exist? Do I end up with two - one for the main EXE to use and one for the GUI DLL to use? I obviously only want *one* graphics engine, and I want the GUI library to use it when rendering, but to leave ownership/management to the core Game EXE. [smile] - Any comments on what I've proposed - good or bad?
- Have I missed anything? Any situations that this will explode?
- Could I do better? (Please note that its too late to completely restart my codebase!)