-
Notifications
You must be signed in to change notification settings - Fork 0
02 Solution Structure
App3Dev solution has three runtime projects...
- SampleProgram - a command line main program, using a custom (windows) user mode cdrom device
- BasicUniversalCppSupport - a dll providing standard infrastructure to support writing and testing programs
- ExtendedUniversalCppSupport - a dll providing kernel mode device access and memory mapped file support
These are supported by three corresponding unit test projects...
- UnitTestSampleProgram - tests for the sample program
- UnitTestBasicUniversalCppSupport - tests for the support library
- UnitTestExtendedUniversalCppSupport - tests for the special extensions to the support library
Business logic is localized in the SampleProgram component.
- should have a clearly specified target environment
- should exhibit a viable reusability strategy
- should have a repeatedly verifiable automated quality objective
All code targets Windows 7 through Windows 11, and builds for both 32- and 64-bit architectures. Design structure and approach to cross-cutting issues promote portability of business logic to other platforms, and enable reuse of the support logic in other business contexts. All code compiles without errors or warnings with code analysis enabled and ruleset Microsoft All Rules applied. All unit tests pass in all configurations (or can be made to pass by re-running individually with appropriate test media).
- should be implemented in a platform independent way
- should not need expertise in multiple technical domains
A common goal of development is to have easily portable business logic, and a business logic domain that can be understood and maintained without the need to first acquire platform implementation expertise. In this sample business logic is the SampleProgram project. This project is built with the '/std:c++latest' compiler setting (ensuring strict adherance to ISO latest working draft). It employs STL and gsl, and follows the utf8 everywhere paradigm. It is supported by a reusable functional library providing a standardized way to perform logging, structured exception handling, and unit testing. Additionally it uses another functional library, offering cdrom device access and a memory mapped file concept.
- should isolate and encapsulate single technical domains
- should offer domain services that are likely to be reusable
- should reflect a balanced approach to structuring the overall solution
The support logic is supplied here as dlls containing objects which are isolated from their clients using the PIMPL idiom. In theory this decouples business logic clients from support logic dll's allowing either to be modified and replaced independently of the other. In this case, no great effort has been taken to test that theory with this code base, but I expect it to hold true for code built with the same tooling and project settings. In any event these measures assist portability by defending the business logic from the impacts of platform differences (strong encapsulation of operating system specific code).
- should rigorously reflect deliverable code structure
- should have platform independent tests for platform independent deliverables
- should provide appropriate test coverage on all target architectures
The tests are structured in dlls that are named after the deliverable components. Within these dlls the test cases are grouped in files that are named to match the deliverable files. The unit tests are named after the methods in the deliverable. utf8::assert allows unit tests to adopt the uft8-everywhere paradigm and this removes an important platform dependency. Test coverage is close to 50%, and 100% pass result is the expected outcome on all architectures.
I have deliberately chosen this wording over the more obvious design objectives to remind developers that such objectives are only attainable through constant attention and discipline. This is particularly important when working in teams. These bullet points offer value which the chief architect (in this case me) has elevated in importance by expressing them explicitly his design. The code-base to date delivers that value, but that happy scenario is always a fragile one. A single beguiling compromise (e.g. choosing the easiest and quickest path to reach a lesser objective) might easily lead to a situation where that value is no longer realised, with an attendant loss that is far greater than the immediate win. For this reason, these bullet targets can best be viewed as constraints on implementation choices, as needed to preserve the design goals.
Note that the principle of encapsulating (windows) platform specific code within "impl" parts of code generally applies, but the problem of isolation of platform dependent code is larger than this factor alone, largely due to convenience of including system headers, and cascade effects. A case in point is the GUID reference for device interfaces (which was a windows type parameter in earlier versions of library API's).
Notice also that the PIMPL approach creates a boundary inside which an implementation can comfortably adopt platform character encoding, provided it adheres to utf8-everywhere at its interface (including the exceptions it throws). This clear and practical programming rule is more acheivable than laboriously wrapping every system call (e.g. in the way that utf8::assert.hpp does).
Note that the sample program component "cd_rom_device" logically straddles two encapsulation boundaries. I originally located it in the app rather than the extension dll, but later accepted that it fits better as part of the extension dll. The stronger argument is that it has internally encapsulated platform dependencies, and these make the component code base 'windows specific' (as do the header files it must include). This source dependency isn't shared by the rest of SampleProject, which is pure ISO c++20, and is the example I provide for writing business logic. Portability was compromised in earlier days also, by using the windows specific GUID_DEVINTERFACE_CDROM in API's directly to identify the device class. There is now a singleton "device_type_directory" that provides a platform agnostic device reference to use in API's, and supports looking up the windows specific GUID (for some arbitrary set of device types including CDROM).
Moving cd_rom_device to the support dll (now done), and wrapping the windows platform GUIDS within a private App3Dev GUID database (also done) are obvious solutions to these limitations, that demonstrate how it is possible to reconsider and incrementally fix residual encapsulation issues.