Skip to content

03 BasicUniversalCppSupport

JackHeeley edited this page Jul 27, 2023 · 12 revisions

Basic Support Library (less is more)

Maximizing use of ISO C++20 with STL/gsl is a huge step towards being able to write reasonably portable and reusable applications across a range of platforms and architectures.

You might also want to consider Boost. However today's improved STL now includes much of what Boost used to provide, and if no other factors apply, avoiding multiple redundant coding options, is a persuasive argument against explicitly including Boost headers as well these days.

A small support library will allow practical ISO C++20 solutions to be built, and can be combined with additional home-grown support libraries that target technologies that the solution uses. This small support library is what the BasicUniversalCppSupport dll aims to provide.

Features

Various character encoding 'standards' have been offered to C/C++ programmers at historical points in time and for different platforms and environments. This issue affects all manner of language features. A cross-cutting concern of having common ways to process system error, exception message, and logging message texts has arisen, and is the factor that determines most of the content of this library. This issue has been resolved here using the utf8 everywhere paradigm. The objects error_context, logger_factory, file_logger, null_logger, logger_interface and system_error exist because of this need.

When applying the utf8 everywhere paradigm on a platform (such as windows) that uses a different native character set, native system calls have to be wrapped and encapsulated and translation utilities are needed. For these purposes spimpl and utf8_convert objects are provided.

Spimpl has a wider purpose and has been applied widely in this solution to isolate (platform dependent) implementation code from client code, allowing both or either to change independently one from the other.

Utf8_assert is supplied so that utf8 everywhere programmers can make use of Microsoft's standard unit test framework. It is an example of a delegation pattern.

Finally utc_timestamp is provided to support file logging with a consistent timezone treatment.

The source code comments explain these library elements in more detail, and provide links to reference material.

Limitations

The file_logger provided has no size limit and can grow to consume disk space. Alternate logging strategies, for example to (windows) system application log, have not been provided.

Improvements

A significant design limitation existed earlier, in that no strategy was initially provided for client program to configure a logger (for the program and its attendant dll's to use). Earlier dll code was limited instead to throwing from component dll's, with logging only and exclusively occurring in the client code's structured exception handling. This was a limiting strategy, because it leaves dll programmers with a dilemma. Should they break the guidance not to throw from constructors and destructors, or leave these parts of the code without any logging at all? That issue in turn meant that any problems with cleanup in component dlls were difficult to detect/diagnose. This early logging limitation could be mitigated (e.g. using gsl expects, asserts and ensures to extend the 'development-time' checks, or writing to stderr etc.) but these mitigations were initially not applied either.

Current versions of App3Dev fully address this problem using the factory design pattern and an Interface to provide a logger_factory and an extendable set of concrete loggers (file_logger, and null_logger) presenting a common logger_interface to client code.

Logging implementation details are hidden behind the logger macro CREATE_LOGGER which assigns a logger for the program, and macros LOG_ERROR, LOG_WARNING etc. which use the pre-assigned logger, all with convenient parameter frames. These logger macros are available for use by all components (main program and dll's) and by all threads, and are designed to optimize efficiently when logging is disabled (by the filtering options provided). Logging falls back to using null_logger in the event that no logger was assigned, or to emitting the logger text on std::cerr if logging was attempted but failed. Now potential constructor and destructor issues, can be logged (see cd_rom_device for an example).

Clone this wiki locally