This file contains the documentation for the UlibCpp library. Note that the source code shown may not represent the actual source code. Please refer to the actual files when details are unclear.
Please use the table of contents to navigate the document:
Configuration
Core header
Logging
Stream
Threading
Win32 API
These header files simplify the configuration of project types, e.g. WTL projects.
#include <ulib/config/Common.hpp>
Defines the following:
-
macro
UNUSED(x)
: Can be used in function signatures to suppress the warning about unused parameters, e.g.:void DoSomething(int arg1, CString arg2) { UNUSED(arg2); }
-
sizeof_array(x)
: determines the size of the array x, in number of elements:int allItems[] = { 1, 2, 3, 4, 5 }; for (size_t index = 0; index < sizeof_array(allItems); index++) ...
Common.hpp
also includes:
#include <ulib/config/CompileTimeWarningError.hpp>
Defines the following:
-
macro
ULIB_COMPILE_WARN(text)
: Outputs a warning in the compiler output; use it like this:#pragma ULIB_COMPILE_WARN("Check warning here")
-
macro
ULIB_COMPILE_ERROR(text)
: Outputs an error in the compiler output; use it like this:#pragma #pragma ULIB_COMPILE_ERROR("C9999", "Don't forget to code here!")
#include <ulib/config/Win32.hpp>
Includes all sorts of Win32 API headers. This is the base of Atl.hpp
. Check
the file contents for details.
#include <ulib/config/Atl.hpp>
Includes for an ATL based project. This is the base of Wtl.hpp
. Check the
file contents for details.
#include <ulib/config/Wtl.hpp>
Includes for a WTL based project. Most WTL projects generated by the WTL
wizard can just include this file instead of the many ATL and WTL files in the
generated stdafx.h
file. The necessary order of ATL and WTL headers are
ensured. Check the file contents for details.
#include <ulib/config/BoostAsio.hpp>
Includes the Boost header <boost/asio.hpp>
and prevents default linking to
some Boost libraries; also disables warnings from the included headers. The
header also prevents auto-generating a WinSock-init class, which automatically
pulls in a dependency to the winsock2.dll
.
#include <ulib/config/BoostDateTime.hpp>
Includes the Boost header <boost/date_time.hpp>
, disables warnings and
also prevents linking to the Boost.DateTime
library.
#include <ulib/config/Android.hpp>
Defines some types like LPCTSTR
and macros like ATLASSERT
, ATLTRACE
,
etc., to be able to compile code that uses ATL or WTL data types under an
Android C++ project type. This header is experimental and currently misses a
CString
implementation.
The root ulib include folder contains some often used classes.
#include <ulib/ulib.hpp>
This include file includes all other headers. Only use this if you use almost all of the library. Instead include single headers when you use specific classes.
#include <ulib/CrashReporter.hpp>
Provides a single static function that initializes the crash reporter:
static void CrashReporter::Init(
const CString& appName, const CString& basePath,
T_fnShowCrashDialog fnShowCrashDialog = T_fnShowCrashDialog());
The crash reporter catches any unhandled C++ exceptions and calls to
std::terminate()
and writes a minidump crash dump file (extension .mdmp) to
a folder. The minidump file is stored in the basePath
folder and the
appName
is used as prefix for the filename. An optional function with the
following signature can be passed:
void ShowCrashDialog(LPCTSTR filename);
The function is called after writing the minidump crash dump file in order to show the user a dialog. Depending on the cause of the crash, the function may not be called at all (e.g. due out of memory errors).
#include <ulib/DynamicLibrary.hpp>
Provides a RAII helper class to load dynamic libraries (DLLs) and get function pointers exported by it:
class DynamicLibrary
{
public:
/// ctor; loads module
DynamicLibrary(LPCTSTR moduleFilename);
/// dtor; frees module again
~DynamicLibrary();
/// checks if library is loaded
bool IsLoaded() const;
/// checks if function with given name is available
bool IsFunctionAvail(LPCSTR functionName) const;
/// returns function with given function name and given function signature
template <typename Signature>
Signature GetFunction(LPCSTR functionName) const;
};
#include <ulib/Exception.hpp>
This header contains an Exception class that is used whenever ulib throws an
exception and is derived from std::exception
.
class Exception : public std::exception
{
public:
Exception(LPCSTR sourceFile, UINT sourceLine);
Exception(const CString& message, LPCSTR sourceFile, UINT sourceLine);
CString Message() const;
CString SourceFile() const;
UINT SourceLine();
};
The exception message can also be retrieved with the base classes what()
method.
#include <ulib/FileFinder.hpp>
The class FileFinder
helps with enumerating files. It has a static
convenience method that does everything:
static std::vector<CString> FindAllInPath(
const CString& path, const CString& fileSpec, bool findFolders, bool recursive);
The fileSpec
may contain wildcards, like *.*
, *.png
or IMG????.jpg
.
The flag findFolders
indicates if folders should be found instead of files.
The flag recursive
specifies if folders should be recursively searched.
The class can also be used manually:
class FileFinder
{
public:
FileFinder(const CString& baseFolder, const CString& fileSpec);
/// indicates if any files were found
bool IsValid() const;
/// returns if the current file entry is a dot file, "." or ".."
bool IsDot() const;
/// returns if current file entry is a file
bool IsFile() const;
/// returns if current file entry is a folder
bool IsFolder() const;
/// returns complete filename of current file entry
CString Filename() const;
/// retrieves next file entry
bool Next();
};
Constructing the class already starts the search, so it's best to use it in a
do-while
loop:
FileFinder finder{ baseFolder, "*.*" };
if (finder.IsValid())
do
{
if (finder.IsDot())
continue;
ATLTRACE(_T("path: %s\n"), finder.Filename().GetString());
} while (!finder.Next());
#include <ulib/IoCContainer.hpp>
This class provides an IoC (inversion of control) container that stores references to objects in a central place.
class IoCContainer
{
public:
/// returns current instance of IoC container
static IoCContainer& Current();
/// registers reference for class
template <typename TClass>
void Register(std::reference_wrapper<TClass> ref);
/// resolves class to object
template <typename TInterface>
TInterface& Resolve();
};
At the start of the app, Register()
any objects that should be globally
available, and resolve them using Resolve()
at a later time.
#include <ulib/Observer.hpp>
This header provides an implementation of the Observer pattern, much like the
C# event functionality. The Subject
is the source of an event being sent to
zero, one or more observer functions.
template <typename T>
class Subject
{
public:
/// function type to be called
typedef std::function<T> T_fnSubject;
/// adds new observer; new ID is returned
int Add(std::function<T> fnObserver);
/// adds new observer
Subject<T>& operator+=(std::function<T> fnObserver);
/// removes an observer by ID
void Remove(int id);
/// calls subject with zero arguments
void Call();
/// calls subject with one arguments
template <typename T1>
void Call(T1 param1);
/// calls subject with two arguments
template <typename T1, typename T2>
void Call(T1 param1, T2 param2);
/// removes all observer
void Clear();
};
T
is the function signature of the observer functions. Define a subject
like this:
Subject<void(const Widget& senderObject, const EventArgs& eventArgs)> m_buttonClicked;
In the code, set up an observer function or lambda:
m_buttonClicked += [] (senderObject, eventArgs) { /* do something here */ }
The Add()
method returns an int
ID that can be used to remove observers
again.
#include <ulib/Path.hpp>
This header provides the Path
class with static methods to manipulate folder
or file paths.
class Path
{
public:
/// returns filename and extension
static CString FilenameAndExt(const CString& path);
/// returns filename without extension
static CString FilenameOnly(const CString& path);
/// returns extension only, with leading dot
static CString ExtensionOnly(const CString& path);
/// returns folder name, without filename, but ending slash
static CString FolderName(const CString& path);
/// returns short path name (filename in 8.3 format); file must actually exist
static CString ShortPathName(const CString& path);
/// make path relative to the given root path and returns it
static CString MakeRelativeTo(const CString& path, const CString& rootPath);
/// returns if stored path is a relative path
static bool IsRelative(const CString& path);
/// returns if path represents a file and if it exists
static bool FileExists(const CString& path);
/// returns if path represents a folder and if it exists
static bool FolderExists(const CString& path);
/// canonicalizes path by removing '..', etc.
static bool Canonicalize(CString& path);
/// adds a backslash at the end of the path
static void AddEndingBackslash(CString& path);
/// combine both path parts and return new path
static CString Combine(const CString& part1, const CString& part2);
/// returns the common root path of both given paths; returns empty string when there's no common root
static CString GetCommonRootPath(const CString& path1, const CString& path2);
/// returns special folder; see CSIDL_* constants
static CString SpecialFolder(int csidl);
/// returns the windows folder
static CString WindowsFolder();
/// returns the temp folder
static CString TempFolder();
/// returns file name of given module name (e.g. kernel32.dll); nullptr means the currently running .exe module
static CString ModuleFilename(HMODULE moduleHandle = nullptr);
/// creates a directory, possibly also creating non-existent parent directories
static bool CreateDirectoryRecursive(LPCTSTR directoryName);
// public members
/// path separator string
static const TCHAR Separator[2];
/// path separator character
static const TCHAR SeparatorCh = _T('\\');
}
#include <ulib/Singleton.hpp>
Provides a thread-safe singleton pattern implementation.
class WidgetManager : public Singleton<WidgetManager>
{
// ...
};
The singleton instance can then be accessed with:
WidgetManager& manager = WidgetManager::Instance();
In a .cpp file, declare the implementation of the singleton like this:
IMPLEMENT_SINGLETON(WidgetManager)
This defines the static variables needed to provide the singleton instance.
#include <ulib/SystemException.hpp>
An exception that additionally stores a Win32 error code:
class SystemException : public Exception
{
public:
/// ctor
SystemException(const CString& message, DWORD win32ErrorCode, LPCSTR sourceFile, UINT sourceLine);
/// returns win32 error code
DWORD Win32Error() const
};
#include <ulib/UTF8.hpp>
This header provides UTF-8 conversion functions:
/// converts string to UTF-8 encoding
void StringToUTF8(const CString& text, std::vector<char>& utf8Buffer);
/// converts from UTF-8 encoded text to CString
CString UTF8ToString(const char* utf8Text);
The UlibCpp library is compiled using the UNICODE
define, so CString can
always hold the UTF-8 encoded string.
#include <ulib/CommandLineParser.hpp>
The CommandLineParser
simplifies parsing command line parameters and also
supports parsing arguments in double quotes, e.g. path names with spaces.
class CommandLineParser
{
public:
/// parses single long command line string
CommandLineParser(const CString& commandLine = GetCommandLine());
/// parses _tmain parameter
CommandLineParser(int argc, TCHAR* argv[]);
/// returns next parameter
bool GetNext(CString& nextParameter);
};
The first parameter is always the executable's file path. The class can easily be used in a while loop:
CommandLineParser parser;
CString parameter;
while (parser.GetNext(parameter))
{
// do something with parameter
}
#include <ulib/ProgramOptions.hpp>
The ProgramOptions
class extends the command line parsing and provides a
way to specify all options of a program and then lets the class parse the
command line by itself.
As a first step, register an output handler:
ProgramOptions options;
options.RegisterOutputHandler([] (const CString& text) { _tprintf(text); });
You can also use the provided OutputConsole
function:
options.RegisterOutputHandler(&ProgramOptions::OutputConsole);
You can also decide to collect the output in a string and show a message box, or use any other output mechanism.
If you want to show the user help text when /h
, /?
or --help
is
specified, use:
options.RegisterHelpOption();
Now register one or more parameters that you want to handle. This call
registers an option -i
with long variant --ignore-case
, a help text and
without further arguments:
options.RegisterOption(_T("i"), _T("ignore-case"),
_T("Ignores case on comparisons"),
[] () { m_ignoreCase = true; return true; });
This call registers an option with a single argument:
options.RegisterOption(_T("o"), _T("output"),
_T("Specifies the output folder"),
[] (const CString& arg) { m_outputFolder = arg; return true; });
This registers an option with multiple arguments:
options.RegisterOption(_T("d"), _T("define"),
_T("Defines a key with a value (2 arguments)"),
2,
[] (const std::vector<CString>& args) { /* do something with args */ });
The first two RegisterOptions()
calls can also be written as:
options.RegisterOption(_T("i"), _T("ignore-case"),
_T("Ignores case on comparisons"),
m_ignoreCase);
options.RegisterOption(_T("o"), _T("output"),
_T("Specifies the output folder"),
m_outputFolder);
The m_ignoreCase
and m_outputFolder
variables are taken by reference
and are assigned appropriately while parsing.
Finally, call one of the Parse()
functions:
/// parses command line options, C-style
void Parse(int argc, _TCHAR* argv[]);
/// parses command line options, Win32-style
void Parse(LPCTSTR commandLine);
When the user specified one of the help options, a help text is output, based
on the help texts specified in the RegisterOption()
calls. You can check
for the help option and quit the app without starting up by checking
IsSelectedHelpOption()
:
if (options.IsSelectedHelpOption())
return 0;
These headers define date/time related classes.
#include <ulib/DateTime.hpp>
The class DateTime
defines a point in time, with date and time parts. The
point in time is time zone agnostic and usually stored as local time. Be sure
to store dates as UTC in external systems (like databases) that may be running
in different time zones.
A DateTime
object can be created in several ways:
/// date/time status values
enum T_enStatus { valid = 0, invalid, min, max };
/// default ctor; initialized with invalid status
DateTime();
/// ctor; takes date/time components
DateTime(unsigned int year, unsigned int month, unsigned int day,
unsigned int hour, unsigned int minute, unsigned int second,
unsigned int millisecond = 0);
/// ctor; initialize with min or max status
DateTime(T_enStatus status);
/// returns current date/time
static DateTime Now();
/// returns current date with time part set to 00:00:00
static DateTime Today();
/// returns max time value
static DateTime MaxValue() { return DateTime(DateTime::max); }
/// returns min time value
static DateTime MinValue() { return DateTime(DateTime::min); }
The date can also be modified using these methods:
/// sets date/time components
void SetDateTime(unsigned int year, unsigned int month, unsigned int day,
unsigned int hour, unsigned int minute, unsigned int second,
unsigned int millisecond = 0);
/// parses ISO 8601 formatted date/time
void ParseISO8601(const CString& iso8601Timestamp);
The properties of the date/time point can also be accessed with these getters:
/// returns date/time status
T_enStatus Status() const;
/// returns year
unsigned int Year() const;
/// month of year; 1 = january
unsigned int Month() const;
/// day of month (1-31)
unsigned int Day() const;
/// hour in day (0-23)
unsigned int Hour() const;
/// minute in hour (0-59)
unsigned int Minute() const;
/// second in minute (0-59)
unsigned int Second() const;
/// millisecond in second (0-999)
unsigned int Millisecond() const;
/// day of week; 0: sunday; ... 6: saturday
unsigned int DayOfWeek() const;
/// day of year; 1-based
unsigned int DayOfYear() const;
/// returns time part of date
TimeSpan TimeOfDay();
There are also operators for addition and subtraction of TimeSpan
and
comparison operators with DateTime
, not shown here.
Formatting dates as text can be done with these methods:
/// ISO 8601 format
enum T_enISO8601Format
{
formatY, // year only
formatYM, // year and month
formatYMD, // year and month
formatYMD_HM_Z, // year, month, day, hour, minute and timezone offset
formatYMD_HMS_Z, // year, month, day, hour, minute, second and timezone offset
formatYMD_HMSF_Z, // full date/time with fraction and timezone offset
};
/// formats date/time using ISO 8601 format
CString FormatISO8601(T_enISO8601Format enFormat = formatYMD_HMS_Z, bool bBasic = false,
const TimeZone& tz = TimeZone::System()) const;
/// formats date/time with given format, see _tcsftime
CString Format(const CString& format, const TimeZone& tz = TimeZone::System()) const;
Note: Use UTC timezone if the date should be stored in external systems (like databases) when it could be used in multiple time zones.
#include <ulib/TimeSpan.hpp>
The class TimeSpan
defines a time span that can be positive or negative.
A TimeSpan
object can be created in several ways:
/// time span status values
enum T_enStatus { valid = 0, invalid, min, max };
/// default ctor
TimeSpan();
/// ctor; takes date/time span components
TimeSpan(int hours, int minutes, int seconds, int milliseconds = 0);
/// ctor; initialize with min or max status only
TimeSpan(T_enStatus status);
/// sets time span components
void SetDateTimeSpan(int hours, int minutes, int seconds, int milliseconds);
The properties of the time span can also be accessed with these getters:
/// returns date/time status
T_enStatus Status() const;
/// component hours in span (-23 to 23)
int Hours() const;
/// component minutes in span (-59 to 59)
int Minutes() const;
/// component seconds in span (-59 to 59)
int Seconds() const;
/// component milliseconds in span (-999 to 999)
int Milliseconds() const;
/// span in hours (about -8.77e7 to 8.77e6)
double TotalHours() const;
/// span in minutes (about -5.26e9 to 5.26e9)
double TotalMinutes() const;
/// span in seconds (about -3.16e11 to 3.16e11)
double TotalSeconds() const;
/// span in milliseconds
double TotalMilliseconds() const;
There are also operators for addition and subtraction of TimeSpan
and
comparison operators with TimeSpan
, not shown here.
Formatting time spans as text can be done with these methods:
/// time span format
enum T_enTimeSpanFormat
{
formatHMS, // "hh:mm:ss" format
formatISO8601, // ISO 8601, "PTxxHxxMxxS" format
};
/// formats time span using specified format
CString Format(T_enTimeSpanFormat format = formatHMS) const;
/// formats time span using given format, see _tcsftime
CString Format(LPCTSTR format) const;
#include <ulib/TimeZone.hpp>
The class TimeZone
defines a time zone that can be used when formatting
DateTime
objects as text.
Time zones can be accessed with these static methods:
/// returns UTC timezone
static TimeZone TimeZone::UTC();
/// returns current system's timezone
static TimeZone TimeZone::System();
/// enumerates all timezones in the system
static std::vector<TimeZone> TimeZone::EnumerateTimezones();
The TimeZone
class can be accessed for further infos:
class TimeZone
{
public:
/// returns timezone name during daylight savings time
CString DaylightName() const;
/// returns timezone name during standard time
CString StandardName() const;
/// returns display name for timezone when enumerating all system timezones
CString DisplayName() const;
/// gets UTC offset for given date/time
TimeSpan GetUtcOffset(const DateTime& dt) const;
/// returns if date/time is in daylight savings time
bool IsDaylightSavingTime(const DateTime& dt) const;
};
#include <ulib/Timer.hpp>
The Timer
class can be used to measure running time, e.g. for statistics or
debugging purposes. The class has a precision of about 15 ms.
class Timer
{
public:
/// ctor
Timer();
/// starts timer; timer must be stopped or reset
void Start();
/// stops timer; timer must be started
void Stop();
/// resets timer; timer must be stopped
void Reset();
/// restarts timer by resetting and starting again
void Restart();
/// returns elapsed time since Start() was called, in seconds
double Elapsed() const;
/// returns total elapsed time in seconds
double TotalElapsed() const;
/// returns if timer is running
bool IsStarted() const;
};
Elapsed()
only considers the last Start()
call, whereas TotalElapsed()
also returns the accumulated time from previous start/stop cycles.
#include <ulib/HighResolutionTimer.hpp>
The HighResolutionTimer
has the exact same interface as the Timer
class,
but has a higher resolution, as it uses the CPU's Performance Counters.
#include <ulib/TraceOutputStopwatch.hpp>
Both Timer
and HighResolutionTimer
classes can be used as types in the
TraceOutputStopwatch
class that automatically provides trace output. Use it
like this:
TraceOutputStopwatch<HighResolutionTimer> renderStopwatch{ _T("renderTime") };
The ctor and the dtor of the class both call ATLTRACE()
to output start and
stop of the timer, and the elapsed time in a readable format.
The log
include folder contains classes for logging. It's not as thorough
as e.g. log4cxx, but often enough for smaller applications.
Logging involves the following concepts:
- Log level: Determines how severe a log message is
- Log message: The message text itself
- Log category: A tree-like hierarchy of loggers that determine what messages get logged where
- Layout: Determines how the log text is to be formatted
- Appender: Determines where the log text is going to be written or sent
Log categories are organized like a tree, and each tree node and leaf are an
instance of a Logger
class. Here are some log categories, the tree "branches"
separated with dots:
client
client.renderer
client.import
client.import.audio
client.import.models
The logger client
sits at the root and has the loggers renderer
and
import
. The import
logger again has two loggers audio
and models
.
Depending on the use cases, I can now log to one of the listed loggers:
LOG_DEBUG(_T("rendering finished"), _T("client.renderer"));
Loggers are created on-the-fly if they are not found in the (root) logger's tree. You can get a logger directy, e.g. like this:
auto root = Log::Logger::GetRootLogger();
auto logger = root->GetLogger("client.renderer");
Each logger can have one or more Appender
that determines what to do with
log messages that are logged to this logger. Each appender has a Layout
object that determines how to format the log message. Setting this up on a
specific logger looks like this:
auto patternLayout = std::make_shared<Log::PatternLayout>(_T("%F(%L): log [%p] %m"));
auto debugAppender = std::make_shared<Log::OutputDebugStringAppender>();
debugAppender->Layout(patternLayout);
auto rendererLogger = Log::Logger::GetRootLogger()->GetLogger("client.renderer")
rendererLogger->AddAppender(debugAppender);
Note that a logger, and also the root logger, has no appender by default. You have to configure this before you can see any logging output.
The default behavior of each logger is to also send the log event to the
parent logger, in this case client
, which may also log the message. This can
be changed on a by-logger basis and is called "additivity". For example, the
client.renderer
messages shouldn't reach the client
logger, since they
are only for debug console output:
auto logger = Log::Logger::GetRootLogger()->GetLogger("client.renderer");
logger->Additivity(false);
Each logger can also be configured to only log messages of a specific severity
or higher. The client.import
(and all of those below) may only get
LOG_ERROR messages or above, to not degrade performance:
auto logger = Log::Logger::GetRootLogger()->GetLogger("client.import");
logger->Level(Log::Level::error);
#include <ulib/log/Log.hpp>
This header defines some common types used for logging, among them the log levels:
namespace Log
{
enum Level
{
debug = 0,
info,
warn,
error,
fatal,
none,
};
}
#include <ulib/log/Logger.hpp>
This header files defines the Logger
class that implements a logger that
can be part of a logger hierarchy.
namespace Log
{
class Logger
{
public:
// properties
/// returns logger level
Level Level() const;
/// returns additivity flag
bool Additivity() const;
/// returns parent logger
LoggerPtr Parent();
/// returns full logger name
CString Name();
/// sets logger level
void Level(Log::Level level);
/// sets additivity
void Additivity(bool additivity);
// methods
/// returns root logger
static LoggerPtr GetRootLogger();
/// returns logger with given name
static LoggerPtr GetLogger(const CString& name);
/// adds appender to this logger
void AddAppender(AppenderPtr appender);
/// removes all appender from this logger
void RemoveAllAppender();
/// removes given appender
void RemoveAppender(AppenderPtr appender);
};
}
The header file also defines macros used for logging:
LOG_DEBUG(msg, cat)
LOG_INFO(msg, cat)
LOG_WARN(msg, cat)
LOG_ERROR(msg, cat)
LOG_FATAL(msg, cat)
The msg
parameter is a text that should be logged, and cat
is a category
to be logged under. The category determines where in the logger hierarchy the
logged text is sent to.
#include <ulib/log/LoggingEvent.hpp>
The LoggingEvent
class encapsulates properties of the logging event.
Normally you won't get in touch with the class. See the header file for
details.
#include <ulib/log/Layout.hpp>
The Layout
class formats the logging event for output in an appender. You
can derive from this class in order to have a custom layout.
namespace Log
{
class Layout
{
public:
virtual ~Layout();
/// appends header to output; override when necessary
virtual void AppendHeader(CString& outputText);
/// appends footer to output; override when necessary
virtual void AppendFooter(CString& outputText);
/// formats logging event to string
virtual void Format(CString& outputText, const LoggingEventPtr loggingEvent) = 0;
};
}
Only the Format()
method is mandatory to override. AppendHeader()
and
AppendFooter()
methods are called for appender that want to put a header
and/or a footer line in a log file.
#include <ulib/log/SimpleLayout.hpp>
The SimpleLayout
class is derived from the Layout
class and simply
formats the output text using the log level in capital letters, a dash and the
log text, e.g.:
DEBUG - Render thread took 12.3 ms.
#include <ulib/log/PatternLayout.hpp>
The PatternLayout
class is derived from the Layout
class and lets you
specify a printf-like pattern to specify how the log text should be formatted.
A pattern string consists of text and pattern specifiers that start with the % char and end with a pattern character, similar to the printf notation. The following characters have meaning:
- c: outputs logger name
- d: date in ISO 8601 format
- F: source filename where log message occured
- l: not supported
- L: source file line where log message occured
- m: log message; not supported
- n: platform-specific newline character
- p: log level (priority)
- r: not supported
- t: thread id
- %: percent sign
The following format modifiers are allowed:
- The - adds left justification to the string
- Next comes the minimum field width (excess space is padded)
- Optional . (dot)
- Next comes the maximum field width; if string is larger, the last n characters are shown
The class only has a ctor that lets you specify the pattern:
namespace Log
{
class PatternLayout : public Layout
{
public:
PatternLayout(const CString& pattern);
};
}
#include <ulib/log/Appender.hpp>
The Appender
class is the base class for all appenders that can write the
formatted log text to a target. Each appender can have a different Layout
class set. Override this class to implement your own appender.
namespace Log
{
class Appender
{
public:
virtual ~Appender();
/// returns layout object being used
LayoutPtr Layout();
/// sets new layout object
void Layout(LayoutPtr layout);
/// appends logging event to output
virtual void DoAppend(const LoggingEventPtr loggingEvent) = 0;
};
}
#include <ulib/log/ConsoleAppender.hpp>
The ConsoleAppender
is derived from the Appender
class and lets you write
the log text to the console using printf.
namespace Log
{
class ConsoleAppender : public Appender
{
public:
virtual ~ConsoleAppender();
virtual void DoAppend(const LoggingEventPtr loggingEvent);
};
}
#include <ulib/log/OutputDebugStringAppender.hpp>
The OutputDebugStringAppender
is derived from the Appender
class and
uses the Win32 API function OutputDebugString to write the log text to the
debugger. The log text can also be intercepted with tools like the
SysInternal's Process Monitor.
namespace Log
{
class OutputDebugStringAppender : public Appender
{
public:
virtual ~OutputDebugStringAppender();
virtual void DoAppend(const LoggingEventPtr loggingEvent);
};
}
#include <ulib/log/TextStreamAppender.hpp>
The TextStreamAppender
is derived from the Appender
class and can write
the log text to a Stream::ITextStream
instance. See the "Streams" chapter
for more infos on the ITextStream
interface.
namespace Log
{
class TextStreamAppender : public Appender
{
public:
TextStreamAppender(std::shared_ptr<Stream::ITextStream> textStream);
virtual ~TextStreamAppender();
virtual void DoAppend(const LoggingEventPtr loggingEvent);
};
}
#include <ulib/log/AndroidLogcatAppender.hpp>
The AndroidLogcatAppender
is derived from the Appender
class and can
write the log text to the Android logcat facility. This class can only be used
when compiling and linking an Android C++ project type.
namespace Log
{
class AndroidLogcatAppender : public Appender
{
public:
virtual ~AndroidLogcatAppender();
virtual void DoAppend(const LoggingEventPtr loggingEvent);
};
}
The stream
include folder contains classes for handing binary files, text
files and other streams. The classes are inspired by C#'s System.IO.Stream
classes.
#include <ulib/stream/IStream.hpp>
This header defines the IStream
interface that describes a stream that can
be read from, written to and seeked on.
namespace Stream
{
class IStream
{
public:
/// origin for Seek() operations
enum ESeekOrigin
{
seekBegin = 0,
seekCurrent = 1,
seekEnd = 2,
};
virtual ~IStream();
// stream capabilities
/// returns true when stream can be read
virtual bool CanRead() const = 0;
/// returns true when stream can be written to
virtual bool CanWrite() const = 0;
/// returns true when seek operations are possible in the stream
virtual bool CanSeek() const = 0;
// read support
/// reads amount of data into given buffer; returns if stream is at its end
virtual bool Read(void* buffer, DWORD maxBufferLength, DWORD& numBytesRead) = 0;
/// reads one byte
virtual BYTE ReadByte();
/// returns true when the stream end is reached
virtual bool AtEndOfStream() const = 0;
// write support
/// writes out given data buffer
virtual void Write(const void* dataToWrite, DWORD lengthInBytes, DWORD& numBytesWritten) = 0;
/// writes out single byte
virtual void WriteByte(BYTE byteToWrite);
// seek support
/// seeks to given position, regarding given origin
virtual ULONGLONG Seek(LONGLONG seekOffset, ESeekOrigin origin) = 0;
/// returns current position in stream
virtual ULONGLONG Position() = 0;
/// returns length of stream
virtual ULONGLONG Length() = 0;
/// flushes data
virtual void Flush() = 0;
/// closes stream
virtual void Close() = 0;
};
}
#include <ulib/stream/StreamException.hpp>
This header defines a StreamException
class that is used throughout the
stream classes to report errors.
namespace Stream
{
class StreamException : public Exception
{
public:
StreamException(LPCSTR sourceFile, UINT sourceLine);
StreamException(const CString& message, LPCSTR sourceFile, UINT sourceLine)
};
}
See the Exception
class for more member functions.
#include <ulib/stream/FileStream.hpp>
The FileStream
class implements IStream
based on a file in storage.
namespace Stream
{
class FileStream : public IStream
{
public:
/// file open mode
enum EFileMode
{
modeCreateNew = 1, ///< creates a new file; fails when it already exists
modeCreate = 2, ///< create a new file; if it already exists, it is overwritten
modeOpen = 3, ///< open an existing file; fail when no file was found
modeOpenOrCreate = 4,///< open an existing file; create empty file when no file was found
modeTruncate = 5, ///< open an existing file; truncate file to 0 bytes; fail when no file was found
modeAppend = 6, ///< like modeOpen, and seeks to the end of the file; no seek before end is allowed
};
/// file access
enum EFileAccess
{
accessRead = 0x80000000, ///< read access to file only
accessWrite = 0x40000000, ///< write access to file only
accessReadWrite = 0xC0000000, ///< read and write access to file
};
/// file share mode
enum EFileShare
{
shareNone = 0, ///< declines reading or writing the file
shareRead = 1, ///< allows reading from the file
shareWrite = 2, ///< allows writing to the file
shareReadWrite = 3, ///< allows both reading and writing
shareDelete = 4, ///< file can be deleted by others
};
/// ctor; opens or creates a file
FileStream(LPCTSTR filename, EFileMode fileMode, EFileAccess fileAccess, EFileShare fileShare);
/// returns if the file was successfully opened
bool IsOpen() const;
// more overridden IStream methods...
};
}
#include <ulib/stream/MemoryReadStream.hpp>
The MemoryReadStream
class represents a memory-based stream that can only
be read from. Seeking is possible, but not beyond the buffer size. CanWrite()
returns false.
namespace Stream
{
class MemoryReadStream : public IStream
{
public:
MemoryReadStream(const BYTE* data, DWORD_PTR length);
// more overridden IStream methods...
};
}
#include <ulib/stream/MemoryStream.hpp>
The MemoryStream
class represents a memory-based stream that can be read
from and be written to. Writing beyond the current memory expands the memory space.
Seeking beyond the current memory space isn't possible.
namespace Stream
{
class MemoryStream : public IStream
{
public:
/// ctor; opens an empty memory stream
MemoryStream();
/// ctor; provides memory contents for memory stream
MemoryStream(const BYTE* dataToUse, DWORD_PTR lengthInBytes);
/// returns data
const std::vector<BYTE>& GetData() const;
// more overridden IStream methods...
};
}
#include <ulib/stream/NullStream.hpp>
The NullStream
class implements a stream that behaves like /dev/zero
:
Reading results in zeros, writing discards data, and seeking always seeks to
position 0.
namespace Stream
{
class NullStream : public IStream
{
public:
NullStream();
// more overridden IStream methods...
};
}
#include <ulib/stream/EndianAwareFilter.hpp>
The EndianAwareFilter
implements some helper methods for reading and writing
16-bit and 32-bit values in an endian aware fashion. It can be used with any
IStream
implementation. The method suffixes LE
means little-endian and
BE
means big-endian.
namespace Stream
{
class EndianAwareFilter
{
public:
EndianAwareFilter(IStream& stream);
WORD Read16LE();
WORD Read16BE();
DWORD Read32LE();
DWORD Read32BE();
void Write16LE(WORD w);
void Write16BE(WORD w);
void Write32LE(DWORD dw);
void Write32BE(DWORD dw);
};
}
#include <ulib/stream/ITextStream.hpp>
This header defines the ITextStream
interface that describes a text based
stream that can be read from and written to, both per-character and per-line.
The interface has similarities to IStream
but doesn't derive from
it.
namespace Stream
{
class ITextStream
{
public:
/// text encoding that is possible for text files
enum ETextEncoding
{
textEncodingNative, ///< native encoding; compiler options decide if ANSI or Unicode is used for output
textEncodingAnsi, ///< ANSI text encoding; depends on the current codepage (not recommended)
textEncodingUTF8, ///< UTF-8 encoding
textEncodingUCS2, ///< UCS-2 encoding
};
/// line ending mode used to detect lines or is used for writing
enum ELineEndingMode
{
lineEndingCRLF, ///< a CR and LF char (\\r\\n) is used to separate lines; Win32-style
lineEndingLF, ///< a LF char (\\n) is used to separate lines; Linux-style
lineEndingCR, ///< a CR char (\\r) is used to separate lines; Mac-style
lineEndingReadAny,///< when reading, any of the above line ending modes are detected when using ReadLine()
lineEndingNative, ///< native mode is used
};
/// ctor
ITextStream(ETextEncoding textEncoding = textEncodingNative,
ELineEndingMode lineEndingMode = lineEndingNative);
/// dtor
virtual ~ITextStream();
// stream capabilities
/// returns text encoding currently in use
ETextEncoding TextEncoding() const;
/// returns line ending mode currently in use
ELineEndingMode LineEndingMode() const;
/// returns true when stream can be read
virtual bool CanRead() const = 0;
/// returns true when stream can be written to
virtual bool CanWrite() const = 0;
/// returns true when the stream end is reached
virtual bool AtEndOfStream() const = 0;
// read support
/// reads a single character
virtual TCHAR ReadChar() = 0;
/// reads a whole line using line ending settings
virtual void ReadLine(CString& line) = 0;
// write support
/// writes text
virtual void Write(const CString& text) = 0;
/// writes endline character
virtual void WriteEndline() = 0;
/// writes a line
void WriteLine(const CString& line);
/// flushes out text stream
virtual void Flush() = 0;
};
}
#include <ulib/stream/TextStreamFilter.hpp>
The TextStreamFilter
class is an implementation of ITextStream
that uses
an underlying IStream
to read from or write to a stream of any type. The
ctor needs the properties for text encoding and line ending to determine the
text format.
namespace Stream
{
class TextStreamFilter : public ITextStream
{
public:
TextStreamFilter(IStream& stream,
ETextEncoding textEncoding = textEncodingNative,
ELineEndingMode lineEndingMode = lineEndingNative);
/// returns underlying stream (const version)
const IStream& Stream() const;
/// returns underlying stream
IStream& Stream();
// more overridden ITextStream methods...
};
}
#include <ulib/stream/TextFileStream.hpp>
The TextFileStream
implements reading from a text file. It's derived from
TextStreamFilter
to provide the char-based and line-based read and write
methods, and internally uses a FileStream
to access the file in storage.
namespace Stream
{
/// text file stream
class TextFileStream : public TextStreamFilter
{
public:
/// ctor; opens or creates text file stream
TextFileStream(LPCTSTR filename,
EFileMode fileMode,
EFileAccess fileAccess,
EFileShare fileShare,
ETextEncoding textEncoding = textEncodingNative,
ELineEndingMode lineEndingMode = lineEndingCRLF);
/// returns if the file was successfully opened
bool IsOpen() const;
/// returns true when the file end is reached
bool AtEndOfStream() const;
private:
/// file stream
FileStream m_fileStream;
};
}
The thread
include folder contains classes for multithreading purposes.
#include <ulib/thread/Event.hpp>
Defines two classes ManualResetEvent
and AutoResetEvent
that share the
same interface but differ in what happens when an event is Wait()
ed on. The
ctor and the methods may throw a SystemException
on error.
class ManualResetEvent
{
public:
ManualResetEvent(bool initialState);
/// sets event
void Set();
/// resets event
void Reset()
/// waits given time (or infinitely) for event to get set
bool Wait(DWORD timeoutInMilliseconds = INFINITE);
/// returns internal event handle
HANDLE Handle() const;
};
#include <ulib/thread/Mutex.hpp>
Includes all other mutex related header files.
#include <ulib/thread/LightweightMutex.hpp>
Defines a light-weight, non-recursive mutex that may not call into kernel, and
so performing much faster than other mutex types. This is implemented using a
critical section. The ctor and the methods may throw a SystemException
on
error. Note that this class is always used together with the MutexLock
class.
class LightweightMutex
{
public:
/// lock type
typedef MutexLock<LightweightMutex> LockType;
/// ctor
LightweightMutex();
/// dtor
~LightweightMutex();
};
#include <ulib/thread/MutexLock.hpp>
Locks are used to obtain access to the object protected by the mutex. The lock
classes are templated classes that have access to the implementation of the
various mutex classes. MutexLock
locks the given mutex while the object is
live:
template <typename T>
class MutexLock
{
public:
/// ctor; locks mutex
MutexLock(T& mutex);
/// dtor; unlocks object
~MutexLock();
};
The MutexTryLock
doesn't lock in the ctor, but provides a Try()
method:
template <typename T>
class MutexTryLock
{
public:
/// ctor; takes a lockable object, but doesn't lock it yet
MutexTryLock(T& mutex);
/// dtor; unlocks object
~MutexTryLock();
/// tries locking mutex until timeout (in milliseconds)
bool Try(DWORD timeoutInMilliseconds);
};
The MutexUnLocker
class can temporarily unlock a locked mutex:
template <typename T>
class MutexUnLocker
{
public:
/// ctor; takes a lockable object and unlocks it
MutexUnLocker(T& mutex);
/// dtor; locks object
~MutexUnLocker();
};
#include <ulib/thread/ReaderWriterMutex.hpp>
A mutex that supports locking for reading and for writing. Multiple readers can lock the mutex, with only one writer being able to lock it for writing.
class ReaderWriterMutex
{
public:
ReaderWriterMutex();
~ReaderWriterMutex();
};
There are two custom lock classes that can be used with ReaderWriterMutex
.
The reader lock can be obtained multiple times:
class ReaderLock
{
public:
/// ctor; locks the mutex as reader
ReaderLock(ReaderWriterMutex& mutex);
/// dtor; releases the mutex
~ReaderLock();
};
Only one writer lock can be obtained at any time:
class WriterLock
{
public:
/// ctor; locks the mutex as writer
WriterLock(ReaderWriterMutex& mutex);
/// dtor; releases the mutex
~WriterLock();
};
#include <ulib/thread/RecursiveMutex.hpp>
A recursively lockable mutex. This is implemented using a system mutex. The
ctor may throw a SystemException
on error. Note that this class is always
used together with the MutexLock
or MutexTryLock
class.
class RecursiveMutex
{
public:
/// lock type
typedef MutexLock<RecursiveMutex> LockType;
/// try-lock type
typedef MutexTryLock<RecursiveMutex> TryLockType;
RecursiveMutex();
};
#include <ulib/thread/Thread.hpp>
The Thread
class provides two methods for threads:
class Thread
{
public:
/// sets thread name for current or specified thread
static void SetName(LPCTSTR threadName, DWORD threadId = DWORD(-1))
/// returns current thread ID
static DWORD CurrentId();
};
The thread name appears in the debugger Threads window and may also appear in minidump crash dumps.
The unittest
include folder contains classes supporting writing unit tests.
#include <ulib/unittest/AutoCleanupFile.hpp>
Provides a RAII class that deletes the file specified as soon as the object goes out of scope:
namespace UnitTest
{
class AutoCleanupFile
{
public:
AutoCleanupFile(LPCTSTR filename)
~AutoCleanupFile();
};
}
#include <ulib/unittest/AutoCleanupFolder.hpp>
Provides a RAII class that creates a temporary folder that is deleted (as well as its contents) as soon as the object goes out of scope:
namespace UnitTest
{
class AutoCleanupFolder
{
public:
AutoCleanupFile()
~AutoCleanupFile();
const CString& FolderName() const;
};
}
The win32
include folder contains helper classes for the Win32 API.
#include <ulib/win32/Clipboard.hpp>
Provides access to the Win32 clipboard.
namespace Win32
{
class Clipboard
{
public:
Clipboard(HWND hwnd = nullptr);
~Clipboard();
/// empties the clipboard
void Clear() const;
/// returns number of formats currently on the clipboard
int CountFormats() const;
/// checks if a given format is available
static bool IsFormatAvail(UINT format);
/// returns text format (CF_TEXT or CF_UNICODETEXT)
CString GetText();
/// returns binary data for given clipboard format
void GetData(UINT format, std::vector<BYTE>& data);
/// sets clipboard text as format CF_TEXT or CF_UNICODETEXT (depending on build options)
void SetText(const CString& text);
/// sets clipboard data; generic function
void SetData(UINT format, const BYTE* dataPtr, UINT size);
/// registers a given format name
UINT RegisterFormat(LPCTSTR formatName) const;
/// returns format name for given format id
CString GetFormatName(UINT format) const;
/// enumerates all clipboard formats currently available on the clipboard
void EnumFormats(std::vector<UINT>& formatList) const;
};
}
The class and its functions should be pretty self explanatory.
#include <ulib/win32/DocHostUI.hpp>
This header file provides a default implementation of the automation interface
IDocHostUIHandlerDispatch
that is used when customizing an IE WebBrowser
control. Derive your class from IDocHostUIHandlerDispatchImpl
and
implement the methods that interest you. See detailed commands in the header
file itself.
#include <ulib/win32/ErrorMessage.hpp>
Helps with handling Win32 error messages. Format an error code like this:
DWORD win32ErrorCode = GetLastError(); // also for HRESULTs
CString message = Win32::ErrorMessage(win32ErrorCode).ToString();
#include <ulib/win32/IniFile.hpp>
Encapsulates access to .ini files:
class IniFile
{
public:
IniFile(const CString& iniFilename);
/// returns integer value from section and key
int GetInt(LPCTSTR sectionName, LPCTSTR keyName, int defaultValue);
/// returns string value from section and key
CString GetString(LPCTSTR sectionName, LPCTSTR keyName, LPCTSTR defaultValue);
/// writes a string value to a section and key
void WriteString(LPCTSTR sectionName, LPCTSTR keyName, LPCTSTR value);
};
#include <ulib/win32/Process.hpp>
The Process
class provides an easy way to start a process, without worrying
abount missing closing any handles.
namespace Win32 { class Process { public: /// sets working directory for process void WorkingDirectory(const CString& workingDirectory);
/// creates process with given command line
bool Create(const CString& commandLine);
/// returns process handle
HANDLE ProcessHandle() const;
};
}
#include <ulib/win32/ResourceData.hpp>
Helps accessing data that is stored in the resources, e.g. as RT_RCDATA
type.
namespace Win32
{
class ResourceData
{
public:
ResourceData(LPCTSTR resourceName, LPCTSTR resourceType = _T("\"RT_RCDATA\""),
HINSTANCE instanceHandle = nullptr);
/// returns true if the resource is available
bool IsAvailable() const;
/// returns resource data as byte array
bool AsRawData(std::vector<BYTE>& rawData);
/// returns resource data as string
CString AsString(bool storedAsUnicode = false);
/// saves resource data as file
bool AsFile(LPCTSTR filename);
};
}
The ctor lets you pass the resource name, e.g. using the MAKEINTRESOURCE()
macro, and the resource type. You can also specify a different instance
handle, e.g. when using resource DLLs.
#include <ulib/win32/VersionInfoResource.hpp>
The VersionInfoResource
class lets you access the version info resource
stored in an .exe or .dll file. There are fixed infos and language-dependent
resources.
VersionInfoResource resource{ filename };
if (!resource.IsAvail())
return;
FixedFileInfo* info = resource.GetFixedFileInfo();
The FixedFileInfo
class looks like this:
class FixedFileInfo : public VS_FIXEDFILEINFO
{
public:
CString GetFileVersion() const;
CString GetProductVersion() const;
CString GetFileOS() const;
CString GetFileType() const;
};
The four methods serve to format a displayable text from the infos stored in
the Win32's VS_FIXEDFILEINFO
struct.
Version info resources may also contain language-dependent strings. The languages can be queried like this:
typedef struct tagLANGANDCODEPAGE
{
WORD wLanguage; // language code
WORD wCodePage; // code page
} LANGANDCODEPAGE, *PLANGANDCODEPAGE;
void GetLangAndCodepages(std::vector<LANGANDCODEPAGE>& langAndCodepageList);
CString GetStringValue(const LANGANDCODEPAGE& langAndCodepage, LPCTSTR valueName);
First get all LANGANDCODEPAGE
entries using GetLangAndCodepages()
, then
call GetStringValue()
, specifying the value name. These can be e.g.
"Comment", "ProductName", "SpecialBuild" or any other text values appearing in
the version info resource.
#include <ulib/win32/Win7Taskbar.hpp>
Provides access to the Taskbar and its functionality introduced in Windows 7. The main goal is to control the task bar icon's progress bar.
This opens the taskbar for the given HWND:
Win32::Taskbar taskbar{ m_hWnd };
if (!taskbar.IsAvailable())
return; // below Windows 7
You can open the progress bar for the taskbar icon for this process:
Win32::TaskbarProgressBar progressBar = taskbar.OpenProgressBar();
progressBar.SetState(TBPF_NORMAL);
progressBar.SetPos(40, 100); // 40 of 100 percent done
Other states that can be set are:
TBPF_NOPROGRESS
: Stops showing progressTBPF_INDETERMINATE
: Shows an indeterminate progress, cycling repeatedlyTBPF_NORMAL
: Shows normal progress using the values set viaSetPos()
TBPF_ERROR
: Shows error progress (red background color)TBPF_PAUSED
: Shows paused progress (yellow background color)
When TaskbarProgressBar
leaves the scope and the dtor is called, the
progress is also set to TBPF_NOPROGRESS
.
#include <ulib/win32/SystemImageList.hpp>
Provides two static methods in the SystemImageList
class:
class SystemImageList
{
static CImageList Get(bool smallIcons);
static int IndexFromFilename(LPCTSTR filename);
};
The Get()
method returns the system's image list. Use this for list views or
tree views that show file icons. Be sure to mark the image list as shared,
e.g. using the LVS_SHAREIMAGELISTS style on list views.
The IndexFromFilename()
returns an image list index for the given filename.
The file name doesn't have to exist.