diff --git a/Base/CMakeLists.txt b/Base/CMakeLists.txt index f7ee7316a5aa170c34356a18eb6b8e8ae5fe43ef..6a3d803c0fa34dc4c609733ac8f1659d5c6018aa 100644 --- a/Base/CMakeLists.txt +++ b/Base/CMakeLists.txt @@ -13,3 +13,4 @@ INCLUDE_DIRECTORIES( . ) +ADD_SUBDIRECTORY(logog) \ No newline at end of file diff --git a/Base/README.md b/Base/README.md index e48b101060ce4e7eee242d01bc033c7bc6075419..9fdcc0c5ef84387d6e517e0fad03c98e1628e9d8 100644 --- a/Base/README.md +++ b/Base/README.md @@ -2,3 +2,14 @@ ## Intro Base ## +### Logging with logog ### + +For details how to use logog see the [OGS devguide](http://ufz.github.com/devguide/logging/) and the [logog documentation](http://johnwbyrd.github.com/logog/). + +[logog](http://johnwbyrd.github.com/logog/) is integrated as a [git-subtree](https://github.com/apenwarr/git-subtree) and can be updated with (executed in the sources root): + + git-subtree pull -P Base/logog --squash https://github.com/johnwbyrd/logog.git + +It was initially integrated with: + + git-subtree add -P Base/logog --squash https://github.com/johnwbyrd/logog.git master diff --git a/Base/logog/CMakeLists.txt b/Base/logog/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..35bfb6c5764d3ac47e30f9dd368d3d698637fddd --- /dev/null +++ b/Base/logog/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required (VERSION 2.8.4) +enable_testing() +project (logog) +include(CTest) +# logog needs thread support on linux +find_package( Threads ) +set( RUNTIME_OUTPUT_DIRECTORY bin/ ) +set( ARCHIVE_OUTPUT_DIRECTORY bin/ ) +set( LIBRARY_OUTPUT_DIRECTORY bin/ ) +set( CMAKE_BINARY_DIR build/ ) +set( CMAKE_LEGACY_CYGWIN_WIN32 0 ) +if(MSVC) + # Force to always compile with W4 + if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") + string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") + endif() +elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + # Update if necessary + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-variadic-macros -Wall -Wno-long-long") +endif() +include_directories( include ) +add_library( logog + src/api.cpp + src/checkpoint.cpp + src/formatter.cpp + src/lobject.cpp + src/lstring.cpp + src/message.cpp + src/mutex.cpp + src/node.cpp + src/platform.cpp + src/socket.cpp + src/statics.cpp + src/target.cpp + src/timer.cpp + src/topic.cpp + src/unittest.cpp +) +set_target_properties(logog PROPERTIES DEBUG_POSTFIX "d") +add_executable( test-logog test/test.cpp ) +target_link_libraries( test-logog logog ${CMAKE_THREAD_LIBS_INIT}) +add_test( NAME test-harness COMMAND test-logog ) +install(TARGETS logog ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) +install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_PREFIX}/include/logog" + FILES_MATCHING PATTERN "*.hpp") + +# Docs generation with Doxygen +find_package( Doxygen ) +if( DOXYGEN_FOUND ) + add_custom_target (logog-doc ${DOXYGEN_EXECUTABLE} ${CMAKE_SOURCE_DIR}/doxyfile + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMENT "Generating source code documentation with Doxygen." VERBATIM) +endif() # DOXYGEN_FOUND \ No newline at end of file diff --git a/Base/logog/doc/make-doxygen.bat b/Base/logog/doc/make-doxygen.bat new file mode 100644 index 0000000000000000000000000000000000000000..ec6f2db2a281b30389f824ef95bfd6ef90f026a5 --- /dev/null +++ b/Base/logog/doc/make-doxygen.bat @@ -0,0 +1,2 @@ +cd .. +bin\win32\dev\doxygen.exe \ No newline at end of file diff --git a/Base/logog/doc/overview.dox b/Base/logog/doc/overview.dox new file mode 100644 index 0000000000000000000000000000000000000000..0baf43db1b99c2dab35e61a0342f1c0d38dfa17e --- /dev/null +++ b/Base/logog/doc/overview.dox @@ -0,0 +1,991 @@ +/* + +Online documentation is available at http://www.logog.org. + +This is a documentation file for logog, written in doxygen format. It's not +intended to be read by a human; rather, it's intended to be read by the +doxygen formatting tool to generate help in a number of user-readable formats. + +If you wish to generate a local copy of the html documentation, then +install the doxygen utility from http://www.doxygen.org , and run the +utility from the root of the logog installation folder. A folder +named doc/html will be created with complete html documentation for the +current release of logog. + +*/ + +namespace logog { + +/*! +\mainpage logog - logger optimized for games + +\htmlonly +<a href="http://www.github.com/johnwbyrd/logog"> +<img src="download.jpg" width=120 height=90 alt="Download logog" > +</a> +\endhtmlonly + +\section introduction Introduction + +logog is a portable C++ library to +facilitate logging of real-time events in performance-oriented +applications, such as games. It is especially appropriate for projects +that have constrained memory and constrained CPU requirements. + +The latest documentation for logog is online at <a href="http://www.logog.org"> +http://www.logog.org</a>. You can download the latest tarball or zipfile +<a href="http://www.github.com/johnwbyrd/logog">here</a>. + +For the impatient, you can add logging functionality into your program +within five minutes by reading the \ref quickstart section. + +\subsection gettingstarted Getting started + +- \ref quickstart +- \ref requirements +- \ref supportedplatforms +- \ref features + +\subsection basic Basic functionality + +- \ref loggingevents +- \ref levels + +\subsection advancedfeatures Advanced functionality + +- \ref categoriesgroups +- \ref multipletargets +- \ref deferredoutput +- \ref addingnewtargets +- \ref customformatting +- \ref memorymanager +- \ref unicodesupport +- \ref performance +- \ref leakdetection + +\subsection relatedtopics Related topics + +- \ref porting +- \ref unittesting +- \ref othersystems +- \ref license + +\subsection gettinghelp Getting help + +- \ref community + +\htmlonly +<table border=0 style="background-color: #fff; padding: 5px;" cellspacing=0> + <tr><td> + <img src="http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif" + height=30 width=140 alt="Google Groups"> + </td></tr> + <tr><td style="padding-left: 5px"> + <b>Subscribe to Logog</b> + </td></tr> + <form action="http://groups.google.com/group/logog/boxsubscribe"> + <input type=hidden name="hl" value="en"> + <tr><td style="padding-left: 5px;"> + Email: <input type=text name=email> + <input type=submit name="sub" value="Subscribe"> + </td></tr> +</form> +<tr><td align=right> + <a href="http://groups.google.com/group/logog?hl=en">Visit this group</a> +</td></tr> +</table> +\endhtmlonly + + +\page features Features + +Why yet another logging library? Because professional games have +very specific logging requirements that aren't met by any previous +logging system. + +logog supports the following features: + +- High performance. It is possible to disable all logging functionality +entirely within logog. In this case logog incurs zero performance +penalty on executing code. All logging functions compile to no-ops in +the case where the logging level is set to disabled. When logging is +enabled, the do-not-log control path requires exactly one boolean +comparison. All performance-critical operations have been constructed to +have constant time or near constant time performance. logog was designed +to assume that no memory or CPU time is available for logging on a final +release build of software. When compiling in final release mode, logog +is designed to compile completely away -- zero memory allocations, zero +CPU cycles spent. + +- Logging to arbitrary destinations. Log messages can be sent to stdout, +stderr, a log file or into memory, or any combination of these. Logging +classes are highly extensible, so logging to new destinations is easy to +add. + +- Multiple simultaneous logging criteria. Messages can be simultaneously +logged to different log targets with different requirements: by file, by +category, by log level, or by group. Substring matching permits +regexp-like behavior on logging criteria. + +- Limited external dependencies. logog only requires a reasonably modern +standards-compliant C++ compiler (tr1 or later). logog has limited +dependencies on STL. Those dependencies have been orchestrated to mitigate +the negative performance impacts of the STL. Additionally, those dependencies +have been abstracted into macros so that STL can easily be replaced with a somewhat +compatible template library of your own choosing. + +- Highly granular control over which message types are logged. Control +over these messages may be determined at compile-time for maximal +performance, or at run-time for interactively enabling or disabling log +message types during execution. Messages may be logged by source file, +group, category, or message contents. + +- Support for advanced logging requirements. logog's pub-sub +architecture means that logging can occur simultaneously to multiple log +targets, each with its own logging criteria. Stable base classes permit +new logging targets to be easily added. + +- Extremely configurable memory usage and policy. All memory allocations +happen after initialization time via a user-supplied memory manager (or +the default std::allocator). In other words, you can configure ALL +memory allocations to occur from custom malloc() and free() style +functions of your own authorship. + +- All allocated memory is freed after the shutdown -- logog does not +leak memory. A special compilation flag (LOGOG_LEAK_DETECTION) audits +every allocation and free within logog to discover and fix memory leaks therein. +Additionally, if you don't trust logog's internal leak detector, support exists +for auditing leaks with Microsoft's CRT library with LOGOG_LEAK_DETECTION_MICROSOFT. + +- Support for POSIX-type and Windows-type operating systems. +Support for proprietary game consoles is implicit or latent; the system +has been demonstrated to work on Xbox 360 and PS3 (with certain proprietary header +files). Support for other OSes is straightforward; all OS dependencies are encapsulated in +macros. + +- Support for multithreading. Multiple thread sources are permitted to +send logging messages at the same time. + +- Support for re-entrancy. If triggering a log message indirectly causes +another log message to be triggered, either through the memory allocator +or some other user-based policy, logog is not permitted to hang in a +multithreaded environment. + +- Unicode (wide-character) support for all strings; define LOGOG_UNICODE +and all strings go from char-based to wchar-based. + +- Verbose documentation. By far the best documented logging system +available. logog uses doxygen comments throughout. + +- Extremely permissive license. logog is released under the MIT License, +so it may be used freely in commercial as well as open-source projects, +with proper attribution to logog's authors (Gigantic Software). + +\page quickstart Quick start + +The logog system is a set of C++ header files and C++ source files. +To use them, compile all the C++ source files into a single library. +The compiler will need to reference the files in the include directory +when compiling the logog library. + +\par Build the logog library + +To make this easier, logog has been set up to compile using CMake, available +at http://www.cmake.org/ . +CMake is a free cross-platform build system, and executables are available for +most major platforms and development environments. Download and install CMake +in order to generate a build for your platform. + +For example, to build the project and libraries on Visual Studio 2008, find and launch +the Visual Studio 2008 Command Prompt at <b>Microsoft Visual Studio 2008 / Visual Studio Tools / +Visual Studio 2008 Command Prompt.</b> + +Then, to create the appropriate project files, create an empty directory, make that +directory the current directory, and run the CMake command with the logog directory as +a parameter: + +\code +mkdir logog-build +cd logog-build +cmake [path-to-logog-top-directory] +\endcode + +At this point, your directory will contain appropriate project and solution files for your +platform. Once the solution is building, the CMake program is no longer needed. +Compile and link the library for your own platform using the makefile or project files generated +for your platform. + +\par Your Hello, Logog! program + +Here is a working logog example that initializes logog, outputs some sample messages and then +terminates. + +\snippet test.cpp HelloLogog + +The most common mistake when getting started with logog is creating a Cerr or Cout +object and not destroying the object before LOGOG_SHUTDOWN(). This is a usage +error and will be detected as heap corruption by most debuggers. + +\par Integrate logog into your own project + +To use the logging library in your own project, put the following line +at the top of your source file(s): + +\code +#include "logog.hpp" +\endcode + +Additionally, make sure to add logog's /include path into the directories +your build process searches. + +At the beginning of your program, initialize logog exactly once, with this +macro: + +\code +LOGOG_INITIALIZE(); +\endcode + +\par Choose one or more output types + +To tell logog what type of output should be logged, you must first create a logog::Cerr, +a logog::Cout, or a logog::LogFile object. Once this object is instantiated, +all messages will be routed to that target. For example: + +\code +logog::Cerr errOutput; +\endcode + +Or: + +\code +logog::LogFile errFile("log.txt"); +\endcode + +It's only necessary to create one target per process, immediately after initializing +logog; you don't need to instance a different Cout, Cerr or LogFile for each class or each file. + +\par Start logging events + +To log a message in your code, use one of the following macros, in order +of severity. Arguments are assumed to be sprintf-style varargs: + +\snippet macro.hpp Shorthand + +Note the funny spellings on DBUG and ERR to avoid conflicting with existing macros +on Windows platforms. + +If it turns out that the names of these macros conflict with your existing code, +then before including the logog.hpp file, define the following constant: + +\code +#define LOGOG_USE_PREFIX 1 +\endcode + +If you enable that prefix, you may log with the following macros instead: + +\code +LOGOG_DEBUG( __VA_ARGS__ ) +LOGOG_INFO( __VA_ARGS__ ) +LOGOG_WARN3( __VA_ARGS__ ) +LOGOG_WARN2( __VA_ARGS__ ) +LOGOG_WARN1( __VA_ARGS__ ) +LOGOG_WARN( __VA_ARGS__ ) +LOGOG_ERROR( __VA_ARGS__ ) +LOGOG_ALERT( __VA_ARGS__ ) +LOGOG_CRITICAL( __VA_ARGS__ ) +LOGOG_EMERGENCY( __VA_ARGS__ ) +\endcode + +An example usage follows: + +\code +int foo = 9001; +int maxfoo = 9000; + +if ( foo > maxfoo ) +{ + WARN("Foo is over %d! Current value is %d.", maxfoo, foo ); + WARN("Since this is a warning, by default all builds will log this message!"); +} + +const char *pMessage = "message" +INFO( "This is an informational %s.", pMessage ); +\endcode + +At this point, your Target object (in this case, the errOutput object) should +be destroyed. Falling out of scope is the easiest way to achieve this. + +Then, terminate your program and logging with the following message: + +\code +LOGOG_SHUTDOWN(); +\endcode + +Lastly, compile and link with the logog library you created earlier. + +\page loggingevents Logging events in your code + +In order to log events in your code, it's important to first understand the logging +levels that are available to you. Out of the box, logog comes with these logging +levels, which are compatible with the syslog standard: + +\snippet const.hpp Level Constants + +So, the lower the level number is, the more important the logging message is. + +To log an event in your code, there exists macros for each of the logging +levels. So, for example, to log an event of level WARN3, you would use the +following macro in your code: + +\code +LOGOG_WARN3("Disk reads are taking %d milliseconds to complete!", nMilliseconds ); +\endcode + +It can get laborious to type the LOGOG_ prefix on all log messages, so, if the +LOGOG_USE_PREFIX macro is not defined, you may use these shorter alternate +logging forms: + +\snippet macro.hpp Shorthand + +If you want to generate a log message of some user-specified level, check out +the LOGOG_LEVEL_MESSAGE macro. + +Keep in mind that you must instantiate at least one Target object in your application +for log messages to have any effect. + +\page levels Verbosity levels of logging + +During early stages of product development, you may want to spread DBUG() +and INFO() type messages liberally across your code base in order to detect +bugs earlier in the process. However, as your code develops you will +want to omit these instructions entirely, as too many log messages will +slow down your program. + +To omit all logging messages of lower than a specific level at compilation time, +\#define the LOGOG_LEVEL constant to be some value from the following list: + +\snippet const.hpp Level Constants + +All logging macros of a lower level will be omitted. + +You may enable all logging messages in the following manner before loading +logog.hpp: + +\code +#define LOGOG_LEVEL LOGOG_LEVEL_ALL +\endcode + +And you may disable all logging messages with this before logog.hpp: + +\code +#define LOGOG_LEVEL LOGOG_LEVEL_NONE +\endcode + +The standard warnings apply to incrementing or changing variables within a macro. +For example: + +\code +INFO("The core has exploded %d times", nExploded++); +\endcode + +The nExploded variable will only be incremented if LOGOG_LEVEL is set to LOGOG_LEVEL_INFO +or lower. + +\page filters Filters and their uses + +A Filter is a special type of Topic that accepts messages from a publisher +and routes them forward to another Topic, based on criteria decided by the Filter. +When logog is initialized, it creates one default filter that automatically forwards +all messages to all Target objects. This default behavior can be modified in +to generate more complicated logging behaviors. + +You can get a reference to the default filter with GetDefaultFilter(). You +can also instantiate your own filters before you start logging messages +and messages will automatically find and route through them as well. + +Messages attempt to publish themselves to all filters when the message is +instantiated. Filters automatically attempt to publish themselves +to all targets when the filter is instantiated. + +\page categoriesgroups Organizing and filtering log messages by types + +Areas of a program such as a game can be broken down into a set of functional +areas. Some areas might include AI, audio, graphics, input and the file system. + +The logog system allows you to store two dimensions of information with each message +that further type the message. These dimensions are referred to as the +<b>category</b> and the <b>group</b> of the message. The ultimate meaning of +these fields is up to you. + +To define the current group for all messages following a certain point in your +code, \#define the LOGOG_GROUP constant to the name of the group, surrounded +by double quotes. Likewise \#define the LOGOG_CATEGORY constant to the name +of the category surrounded by double quotes. + +Your compiler may need the \#undef macro to undefine the previous setting of +a constant before \#define'ing it to something else. + +If you wish to set further messages to no specific category or group, \#define +either LOGOG_CATEGORY or LOGOG_GROUP to NULL as necessary. + +\snippet test.cpp GroupCategory1 + +A Filter can be told to route only messages matching a specified category or +group. To do this, call the Filter::Category() or Filter::Group() methods on +a specific filter. That filter will then only pass messages matching +the criteria you've set. + +\snippet test.cpp GroupCategory2 + +\page multipletargets Logging to permutations of multiple targets + +It's possible to create multiple filters with multiple criteria on each filter +and then route each filter to a separate target. This permits advanced logging +of different types of events to different log targets. + +A filter will automatically attempt to publish itself to all targets. You can +change this behavior by calling UnpublishToMultiple( AllTargets() ) on that filter, +followed by PublishTo() to publish that filter to an output of your choice. + +\snippet test.cpp GroupCategory4 + +\page deferredoutput Deferring logging output + +Writing debug output can take a lot of time on most platforms. +Especially in programs that produce copious debug output, logging can slow +a program's operation considerably. + +To get around this behavior, use the LogBuffer class. To Create a LogBuffer, +you provide a pointer to the ultimate Target you want to write to, such as +a Cerr or a LogFile, as well as the size of the LogBuffer. Logging then occurs +solely to the LogBuffer, and no output is written to the Target until the +LogBuffer is destroyed, or the LogBuffer::Dump() method is called. + +\page customformatting Custom formatting of log messages + +The Formatter object is responsible for rendering a particular Topic into a human-readable +format. A default Formatter is created by default for you based on the flavor of the compile +target. See FormatterGCC and FormatterMSVC objects for examples. + +If you want log messages in your own custom format, subclass the Formatter::Format +method into your own custom class, and write your own formatting function for a topic. +See FormatterMSVC::Format and FormatterGCC::Format for examples. + +If all you want to do is enable or disable a certain type of output on your formatter -- for example, to remove file and line number information from +all your output lines -- then there's a very simple way of doing this. +The Formatter class implements a Formatter::GetTopicFlags() function that +in the default case queries the topic in order to find out which fields +to render. You can if you wish override this default behavior for a custom +formatter in order to always or never render specific fields. + +The following example overrides the standard FormatterMSVC formatter in +order to never show file and line number information on the output. + +\snippet test.cpp FormatterCustom1 + +If you wish, you can force a formatter to output the current date and time as +part of their format. You can do this by calling the Formatter::SetShowTimeOfDay function +with a value of true, as follows: + +\snippet test.cpp DateAndTimeLogging + +\page addingnewtargets Adding new logging output targets + +To add a new type of logging output, such as a network socket or a printer, +create a subclass of the Target class, and implement your own Output() +function that renders the information to your output device. + +\snippet target.cpp Cout + +Then, instance your new class after your call to LOGOG_INITIALIZE() but +before messages are invoked. + +\page memorymanager Implementing a custom memory manager + +All memory allocated by logog, by default, goes through the system malloc() +and free() routines. However, many games prefer to implement their +own memory management systems in order to track memory usage in detail. + +In this case, logog can be made to allocate memory via a callback into +your own custom code. In order to implement this callback, +allocate an INIT_PARAMS structure on the stack, and then replace +the m_pfMalloc and m_pfFree pointers to pointers to your own custom +allocation and free routines. (It's fine to allocate an INIT_PARAMS structure +on the stack.) +Then, when calling the LOGOG_INITIALIZE() macro, provide the address +of the INIT_PARAMS structure as a parameter. The INIT_PARAMS structure +may then be freed. + +\snippet api.hpp INIT_PARAMS + +\page leakdetection Memory leak detection + +A memory leak detection mechanism has been built into logog that tracks all +internal memory allocations and matches them with corresponding frees. +The system also detects incorrect reallocations and double frees. This +system only needs to be enabled if you suspect that logog is leaking memory. + +To enable this checking, \#define the LOGOG_LEAK_DETECTION constant at compilation +time before \#include'ing logog.hpp, and then recompile both the logog library and your +code. Additionally, if you want a detailed report about memory allocations +and frees at the time they occur, enable the LOGOG_REPORT_ALLOCATIONS flag during +compilation for an action-packed report during run time. + +If you don't trust logog's built-in leak detector and are using a Microsoft +toolset, Microsoft may have enabled support for your platform to detect +leaks using the CRT library. If you want to try to use the Microsoft leak +detector to find leaks in logog, then \#define the LOGOG_LEAK_DETECTION_MICROSOFT constant at +compile time before \#include'ing logog, and recompile both the logog library +and your application. See http://msdn.microsoft.com/en-us/library/x98tx3cf.aspx +for more details on how Microsoft implements CRT leak detection. + +It's not recommended to leave either of these options on for general use. +Memory leak detection will slow down logog; additionally, memory leak +detection avoids logog's custom memory manager, in order +to avoid an infinite recursion. + +Don't try to enable both LOGOG_LEAK_DETECTION and LOGOG_LEAK_DETECTION_MICROSOFT +at the same time. + +\page unicodesupport Unicode support + +The logog library may be compiled either in ANSI mode or in Unicode mode. By default, +logog is compiled as an ANSI library. In this case, multibyte characters are +not permitted in log messages. To enable Unicode support, define the LOGOG_UNICODE +flag in logog.hpp before compiling the logog library. + +Defining this flag sets wchar_t as the base type for all logog character operations. +On Windows like platforms, a wchar_t is two bytes, and on Posix-like platforms this +may be two bytes or four bytes. + +Because Unicode support is based around wchar_t, the exact format of the Unicode +output depends on the endianness of the target platform as well as the size of +wchar_t. If logog is creating a new output log file, logog tries to add a Unicode +BOM to the start of the file, if wchar_t is either two or four bytes in length. +In this case, logog can write a UTF-16 or UTF-32 BOM, in either little or big +endian format. See LogFile::WriteUnicodeBOM() for more information on how this +BOM is written. More information on what a BOM does is available at +http://unicode.org/faq/utf_bom.html . + +Defining LOGOG_UNICODE has several effects on logog. First, this forces all +logging functions to expect to receive string parameters as arrays of the +local wchar_t type instead of char types. In other words, the LOGOG_CHAR +base type is redefined to wchar_t instead of char. Second, this forces logog to route all +Cout messages to wcout, and all Cerr messages to wcerr. + +A convenience macro, _LG(), is available (if you haven't defined LOGOG_USE_PREFIX) +for easily switching your constant strings from ANSI to Unicode, based on the compilation +value of LOGOG_UNICODE. To use this macro, and hence to have all your code compile +in both ANSI and Unicode modes, log in the following fashion: + +\code +INFO(_LG("This informational message is displayed in both ANSI and Unicode builds.")); +WARN(LOGOG_CONST_STRING("And this one works regardless of the LOGOG_USE_PREFIX setting.")); +\endcode + +Mixing and matching both Unicode and ANSI messages in one application is not currently +supported, as most compilers do not implement this functionality. + +<b>Windows specific issues</b> + +Windows platforms have special considerations when logging to the console with +wide characters via wcout or wcerr. Because logog shares the console with its +host application, it does not initialize the console in any way. On Windows +flavored platforms, the console must be initialized in a Unicode-friendly way. +See http://blogs.msdn.com/b/michkap/archive/2008/03/18/8306597.aspx for details +about how this might work. + +One way of initializing a Windows console for Unicode support, which seems to +work, is as follows: + +\snippet test.cpp WindowsUnicodeSetup + +See also Microsoft's documentation at http://msdn.microsoft.com/en-us/library/tw4k6df8.aspx . + +\page pubsub Publisher-subscriber functionality + +Topic objects are a base class that provides publisher-subscriber +features to subclasses. Most classes in logog descend from the +Topic class. + +Topics are capable of sending other topics (or themselves) to other +subscribers, as well as receiving another topic from a publisher. +See Topic::Send() and Topic::Receive() for more details. + +You can cause a Topic to send itself to its subscribers by calling +Topic::Transmit(). + +Topics can publish, unpublish, subscribe and unsubscribe themselves +from other topics. See Topic::Publish(), Topic::Unpublish(), +Topic::Subscribe(), Topic::Unsubscribe() for more details. + +Topics can also do all these tasks to lists of other topics. +See Topic::PublishToMultiple(), Topic::UnpublishToMultiple(), +Topic::SubscribeToMultiple(), Topic::UnsubscribeToMultiple(). + +\snippet test.cpp Subscription + +\page architecture Internal architecture + +The logog system uses a publisher-subscriber model in order to handle +message flow from source to destination. While the higher-level class +architecture takes care of typical message routing, the standard message +flow can be changed to support advanced logging models. + +A key base class is the Topic class. Topics contain strings and numeric +information representing a file name, line number, a free-form group +name, a free-form category name, and a free-form message string. Topics +know how to transmit and receive themselves to and from other topics, +but they don't know how to discover those other topics. They do know how +to negotiate with another topic in order to discover whether they should +subscribe to that other topic -- see Topic::CanSubscribeCheckTopic() for +details. Topics implement the core functionality of subscribing and +publishing. Topics can subscribe to multiple other topics, and they can +publish to multiple other topics. + +Some Topic behavior, especially the subscriber-publisher behavior and +cross-thread locking, is subsumed into the Node class. Nodes should +generally not be instanced by themselves, as they are not sufficiently +functional to be useful. + +Topics are subclassed into TopicSource and TopicSink classes. +TopicSources can only publish, and TopicSinks can only subscribe. + +A Target is a TopicSink that is capable of rendering its subscriptions +to a logical output device. Targets include the Cout, Cerr, and the +OutputDebug classes. To make logog send output to some arbitrary new +destination, create a new Target subclass and override the +Target::Output() method, and instance a new element of your class at the +top of your program. + +A Filter is a Topic that functions basically as a subscriber and a +publisher. It's used to limit the scope of incoming messages to a +particular type, group, or category. For example, a Filter may be used +to permit only messages with a level of LOGOG_LEVEL_WARN or higher to be +logged. Other messages are dropped. By default a filter will attempt to +subscribe itself to all existing Targets; however, this behavior may be +changed by overriding the Initialize() method within your Target +subclass, or by manually calling Target::Unpublish() on each Target that +you want the Filter to not publish to. + +This design permits more advanced logging models. For example, it's +possible to have two Filters, one which filters for error messages and +logs those to the console, versus informational and warning messages, +which are logged to a file. Note that the routing of any of the +higher-level classes such as Filter or Output by simply instantiating a +Topic and manually calling PublishTo() and SubscribeTo() to the desired +inputs and outputs. + +A Message is a sub-sub-class of TopicSource that knows how to publish +itself automatically to any outstanding Filter objects. A program will +typically instance a set of static Message objects and use them to +indicate execution of a certain point in the program. + +String types receive their own custom class. Since logog spends a lot of +time shuffling strings from class to class, this permits string copies +to be fast constant-time operations (a copy of pointers). + +All statically allocated elements are stored inside the Statics class. +This permits all items to be tracked and freed, thus assuring no memory +leaks. + +In order to support cross-platform, multithread-safe support, logog +implements a Thread class, a Timer class, and a Mutex class. These of +course can be customized to support future platforms. + +\page performance Performance topics + +Multiple memory allocations and frees are the bane of performance-oriented +code. STL is particularly naughty in this regard. + +To that end, logog attempts to do as few allocations as possible at run-time. +Message objects are allocated exactly once, when a message macro is run for +the first time. Publisher-subscriber negotiation occurs at this time +as well, which is when the pub-sub lists in the Filter objects get updated. +This process happens only once per message, regardless of the number of times +the message is invoked. Therefore, messages that are never executed never +allocate any memory, and the memory-allocation penalty for a message is incurred +exactly once, the first time the message is transmitted. + +A platform-specific vsprintf() type function is used to convert the varargs +in a message into a final destination string. I looked at this problem +for quite a while, and it seems that this method provides the best performance +guarantees without relying on a large external library, such as Boost, or +increasing the code size significantly. A template-based approach for +running vsprintf type functions would theoretically be faster than calling +vsprintf(); however, this would require the inclusion of a significant +amount of template-based code to handle all the possible situations that +vsprintf() must deal with. This would likely double the existing code size; +so I erred on the size of keeping logog smaller and more self-contained. + +If, after all that, you want to replace vsprintf() for your platform, it is +called in only one place in the logog source code (in String.cpp). + +Because logog spends so much time passing strings around, logog provides a +custom string class that internally represents strings as fixed buffers. +This helps reduce the repeated allocations and frees that std::string is notorious +for. + +Because most logging outputs can be slow, logog provides a LogBuffer class to +help with \ref deferredoutput . + +\page locking Multithread locking and mutexes + +The Mutex object is responsible for multithread safety in logog. All +platforms support a Mutex object. Only one thread may acquire +a Mutex object at one time. Other threads fall into a wait state until +the locking thread releases the Mutex. + +The ScopedLock object is a convenient way to represent a Mutex lock +as part of an object's auto scope. To use a ScopedLock, create +a Mutex lock on a known thread, then create a ScopedLock with the +previously defined Lock as a parameter. Code is guaranteed to be +single threaded within the scope of the ScopedLock. + +\snippet test.cpp SimpleLocking + +\page porting Porting logog + +Porting logog should be a straightforward affair, if you follow these +guidelines. + +First of all, you'll need to choose a flavor of operating system for +your platform. Nearly all OSes will fall into one of two flavors: +POSIX-like and Windows-like. platform.hpp tries to detect the local OS +and compile for it; you may need to change the detection logic for your +new platform. You'll need to set either LOGOG_FLAVOR_POSIX or LOGOG_FLAVOR_WINDOWS +before loading platform.hpp. + +The files containing platform dependent code are as follows. + +- platform.hpp. In addition to detecting the build flavor, platform.hpp +also sets some macros for STL usage. All STL calls are routed through +the macros in platform.hpp. As of this writing, the STL templates are +available in tr1; you may need to tweak this if you're depending on a C++x11 +compiler. + +\snippet platform.hpp STLTypes + +- mutex.hpp. You'll need to write macros for your own LOGOG_MUTEX_* +calls: + +\snippet mutex.hpp Mutex + +- thread.hpp. Write macros for LOGOG_THREAD, LOGOG_THREAD_CREATE and +LOGOG_THREAD_JOIN. Since logog does not actually initiate multiple threads, +this step can technically be skipped; however, unless threads are implemented +for a target platform, the unit tests will fail. + +\snippet thread.hpp Thread + +- timer.hpp. Add code for the Timer initializer and the Get() function. + +\snippet timer.cpp TimerGet + +Lastly, verify that unittest.cpp compiles and executes with a zero +return code. + +\page requirements Requirements + +logog assumes the existence of a C99 compliant C++ compiler. It uses +variadic macros in the C99 style, so any compiler that does not understand +__VA_ARGS__ style message passing will not work. The vast majority of +semi-modern compilers do. + +logog also has limited dependencies on STL. These dependencies are represented +by \#defines in the file platform.hpp. If your STL lacks support for any +of these containers, or if you're using another STL-like container system, +you may replace these \#defines and they will be used as logog's standard +containers. + +\snippet platform.hpp STLTypes + +\page supportedplatforms Supported platforms + +logog has been demonstrated to work with the following platforms: + +- Microsoft Visual Studio 2008 +- Microsoft Visual Studio 2010 (x64 and x86) +- Ubuntu 10.04 LTS +- MacOS 10.7 +- Xbox 360 +- Sony PlayStation 3 + +It is expected that logog should work on any reasonably +POSIX-compliant or Windows-compliant operating system with minimal changes. +Please see \ref porting for more information on support for alternative platforms. + +Since the console implementations are proprietary to the +platform holders, implementation details for these platforms have not been +publicly provided. However they may be provided to authorized and licensed +platform developers if the platform holder permits; contact us if you're a +licensed developer. + +\page license License agreement + +logog is Copyright (c) 2011, Gigantic Software. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +\page community Community and support + +Two mailing lists exist for general support and development discussion related to +logog. The first is for general logog questions and answers. This mailing list is +visible at +<a href="http://groups.google.com/group/logog">http://groups.google.com/group/logog</a>. +All users of logog are invited to join this mailing list for general discussion and help. + +\htmlonly +<table border=0 style="background-color: #fff; padding: 5px;" cellspacing=0> + <tr><td> + <img src="http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif" + height=30 width=140 alt="Google Groups"> + </td></tr> + <tr><td style="padding-left: 5px"> + <b>Subscribe to Logog</b> + </td></tr> + <form action="http://groups.google.com/group/logog/boxsubscribe"> + <input type=hidden name="hl" value="en"> + <tr><td style="padding-left: 5px;"> + Email: <input type=text name=email> + <input type=submit name="sub" value="Subscribe"> + </td></tr> +</form> +<tr><td align=right> + <a href="http://groups.google.com/group/logog?hl=en">Visit this group</a> +</td></tr> +</table> +\endhtmlonly + +The second mailing list is for those who wish to actively participate in the +development of logog. The mailing list is visible at +<a href="http://groups.google.com/group/logog-devel">http://groups.google.com/group/logog-devel</a>. + +\htmlonly +<table border=0 style="background-color: #fff; padding: 5px;" cellspacing=0> + <tr><td> + <img src="http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif" + height=30 width=140 alt="Google Groups"> + </td></tr> + <tr><td style="padding-left: 5px"> + <b>Subscribe to Logog Development</b> + </td></tr> + <form action="http://groups.google.com/group/logog-devel/boxsubscribe"> + <tr><td style="padding-left: 5px;"> + Email: <input type=text name=email> + <input type=submit name="sub" value="Subscribe"> + </td></tr> +</form> +<tr><td align=right> + <a href="http://groups.google.com/group/logog-devel">Visit this group</a> +</td></tr> +</table> +\endhtmlonly + +The best way to get help with a specific problem is to submit an issue +on Github. You can review a list of current issues, or submit your own, +<a href="https://github.com/johnwbyrd/logog/issues">here</a>. + +I'd REALLY like to get other developers involved in maintaining and further +developing the code. All sane developers encouraged to fork the <a href="https://github.com/johnwbyrd/logog">git repository</a> and +do bug fixing or new feature implementation. Contact me at jbyrd at giganticsoftware dot com if you need administrative privileges on the master logog branch; I'm +actively looking to incorporate positive changes. + +\page othersystems Other logging frameworks and systems + +Here are other logging and testing frameworks that have served as +inspiration (negative and/or positive) for logog. + +\section pantheios Panetheios + +http://www.pantheios.org + +Pantheios claims to compile to nothing in the final release case. +However, the Pantheios library depends on STLSoft, xTests, b64, and +shwild. + +\section marginean Petru Margenian's library in Dr. Dobb's Journal + +http://drdobbs.com/cpp/201804215 + +Another interesting effort at a portable logging implementation. +This library depends on an atomic_ops library from HP, and +it won't mention this fact until you try to compile it. Logging can be +enabled or disabled based on a single dimension ("level") and all +logging output ends up at stderr (there is no support for alternative +outputs). + +\section glog glog, the Google logging library + +http://code.google.com/p/google-glog/ + +Provides logging to stderr, a file or syslog. Their ostream-style +logging methodology creates two sets of macros: one for macros that +compile away in release mode, one for macros that don't. Interesting (if +non portable) support for stack walking. + +\section rlog rlog + +http://code.google.com/p/rlog/ + +Uses a publisher-subscriber model for all objects, and demonstrated that +this basic architecture was highly appropriate for logging. Does not +play nicely with custom allocators. + +\section loki Loki + +http://loki-lib.sourceforge.net/ + +The loki library demonstrates how to abstract platform-specific features +like mutexes and threads in remarkably few lines of code. + +*/ + +} + +/** \def LOGOG_UNICODE + ** Define this macro to enable Unicode support in logog. The logog library works either in Unicode mode or not -- + ** attempting to mix the two log types will have unexpected results. + **/ +#define LOGOG_UNICODE 1 + +/** \def LOGOG_INTERNAL_DEBUGGING + ** Define this macro to debug logog itself. This setting enables sanity checks on many logog functions. This setting + ** is not very useful for end users of logog. */ +#define LOGOG_INTERNAL_DEBUGGING 1 + +/** \def LOGOG_LEAK_DETECTION + ** Define this macro to check all memory allocations and frees. This setting will check against double allocations + ** and double deletes as well. Do not enable this on production code -- it'll slow down the performance considerably. + **/ + #define LOGOG_LEAK_DETECTION 1 + +/** \def LOGOG_REPORT_ALLOCATIONS + ** Define this macro to report on ALL allocations and frees that happen through logog. + ** Enable this if you get paid by lines of program output. */ +#define LOGOG_REPORT_ALLOCATIONS 1 + +/** \def LOGOG_COUT + ** This is equivalent to std::wcout if LOGOG_UNICODE is defined, and std::cout otherwise. + **/ + +/** \def LOGOG_CERR + ** This is equivalent to std::wcerr if LOGOG_UNICODE is defined, and std::cerr otherwise. + **/ diff --git a/Base/logog/doxyfile b/Base/logog/doxyfile new file mode 100644 index 0000000000000000000000000000000000000000..371353e1c5609e7839cd0878d80c93607666f216 --- /dev/null +++ b/Base/logog/doxyfile @@ -0,0 +1,1716 @@ +# Doxyfile 1.7.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = logog + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "logger optimized for games" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doc + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES =YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file($line) : $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = include doc test + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.dox *.c *.cpp *.hpp *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = test/ include/ src/ + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = NO + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is adviced to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = YES + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = com.giganticsoftware.logog + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Gigantic Software + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = com.giganticsoftware.logog + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = com.giganticsoftware.logog + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the +# mathjax.org site, so you can quickly see the result without installing +# MathJax, but it is strongly recommended to install a local copy of MathJax +# before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = YES + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = DOXYGEN LOGOG_UNICODE=1 LOGOG_INTERNAL_DEBUGGING=1 LOGOG_LEAK_DETECTION=1 LOGOG_REPORT_ALLOCATIONS=1 + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called Helvetica to the output +# directory and reference it in all dot files that doxygen generates. +# When you want a differently looking font you can specify the font name +# using DOT_FONTNAME. You need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/Base/logog/include/api.hpp b/Base/logog/include/api.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6d98f4b343c987752c339b9fe4aad3022d93cc9d --- /dev/null +++ b/Base/logog/include/api.hpp @@ -0,0 +1,47 @@ +/** + * \file api.hpp Initialization and shutdown functions for logog. + */ + +#ifndef __LOGOG_API_HPP__ +#define __LOGOG_API_HPP__ + +namespace logog +{ +//! [INIT_PARAMS] +/** + * To initialize the memory manager with non-default values, allocate a temporary INIT_PARAMS structure, fill it with + * zeros, and then change the individual entries in the INIT_PARAMS struct before passing as a parameter to Initialize(). + * \sa Initialize + */ +struct INIT_PARAMS +{ + /** A pointer to a function that allocates memory. By default logog allocates using malloc(). Change this pointer + ** before passing to Initialize() to use your own custom memory allocator. + * \sa logog::Initialize() + */ + void *( *m_pfMalloc )( size_t ); + + /** A pointer to a function that frees memory. By default logog frees using free(). Change this pointer + * before passing to Initialize() to use your own custom memory allocator. + * \sa logog::Initialize() + */ + void ( *m_pfFree )( void * ); +}; +//! [INIT_PARAMS] + +/** Initializes the logog system. No logog calls or allocations may be made before calling this function; expect + * crashes if you haven't called this at the top of your program. + * \param params The address of an INIT_PARAMS structure you have already allocated on the heap, or NULL to + * use default values. + * \sa INIT_PARAMS + */ +extern int Initialize( INIT_PARAMS *params = NULL ); + +/** Shuts down the logog system and frees all memory allocated by logog. Memory still allocated by the logog system after Shutdown() indicates + ** a bug. + **/ +extern int Shutdown( ); + +} + +#endif // __LOGOG_API_HPP diff --git a/Base/logog/include/checkpoint.hpp b/Base/logog/include/checkpoint.hpp new file mode 100644 index 0000000000000000000000000000000000000000..05c8ba1f7a43a06ad242bf0edd8fb93970bb3310 --- /dev/null +++ b/Base/logog/include/checkpoint.hpp @@ -0,0 +1,31 @@ +/** + * \file checkpoint.hpp Representations of a program counter reaching a specific point in code. + */ + +#ifndef __LOGOG_CHECKPOINT_HPP__ +#define __LOGOG_CHECKPOINT_HPP__ + +namespace logog +{ +/** A checkpoint is a topic that fires when a specific section of code is executed. The first time a bit of + ** code is executed, a Checkpoint is instanced, and when the code is executed again, the Checkpoint is + ** reused. + **/ +class Checkpoint : public TopicSource +{ +public: + Checkpoint( const LOGOG_LEVEL_TYPE level = LOGOG_LEVEL_ALL, + const LOGOG_CHAR *sFileName = NULL, + const int nLineNumber = 0, + const LOGOG_CHAR *sGroup = NULL, + const LOGOG_CHAR *sCategory = NULL, + const LOGOG_CHAR *sMessage = NULL, + const double dTimestamp = 0.0f ); + + /** Sends the node in question. Optionally updates the timestamp in this checkpoint before sending the node. */ + virtual int Send( const Topic &node ); + +}; +} + +#endif // __LOGOG_CHECKPOINT_HPP_ diff --git a/Base/logog/include/const.hpp b/Base/logog/include/const.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2608447595d3e8034e054a0513522a28492acc4b --- /dev/null +++ b/Base/logog/include/const.hpp @@ -0,0 +1,90 @@ +/** + * \file const.hpp Constants. + */ + +#ifndef __LOGOG_CONST_HPP__ +#define __LOGOG_CONST_HPP__ + +#ifndef LOGOG_FORMATTER_MAX_LENGTH +/** The maximum length of a single line that a formatter may output, in LOGOG_CHAR units. */ +#define LOGOG_FORMATTER_MAX_LENGTH ( 1024 * 16 ) +#endif + +#ifndef LOGOG_DEFAULT_LOG_BUFFER_SIZE +/** The default size of a RingBuffer object for buffering outputs. */ +#define LOGOG_DEFAULT_LOG_BUFFER_SIZE ( 4 * 1024 * 1024 ) +#endif + + +/** \addtogroup levelsettings Level Settings + ** These are level settings for logog. These settings are valid for the LOGOG_LEVEL compilation flag. To enable + ** all logog messages, use the compilation flag -DLOGOG_LEVEL=LOGOG_LEVEL_ALL. To disable all logog messages + ** (and effectively remove logog code from your executable) use the compilation flag -DLOGOG_LEVEL=LOGOG_LEVEL_NONE. + ** \sa LOGOG_LEVEL + ** @{ + **/ +//! [Level Constants] +#define LOGOG_LEVEL_NONE 0 +#define LOGOG_LEVEL_EMERGENCY 8 +#define LOGOG_LEVEL_ALERT 16 +#define LOGOG_LEVEL_CRITICAL 24 +#define LOGOG_LEVEL_ERROR 32 +#define LOGOG_LEVEL_WARN 40 +#define LOGOG_LEVEL_WARN1 48 +#define LOGOG_LEVEL_WARN2 56 +#define LOGOG_LEVEL_WARN3 64 +#define LOGOG_LEVEL_INFO 72 +#define LOGOG_LEVEL_DEBUG 80 +#define LOGOG_LEVEL_ALL 88 +//! [Level Constants] + +#define LOGOG_LEVEL_TYPE int + +#ifndef LOGOG_LEVEL +#define LOGOG_LEVEL LOGOG_LEVEL_DEBUG +#endif + +/** @} */ + +/** \addtogroup topicbitstype Topic Bits Type + * Bit flags representing whether a topic cares about a specific field or not. 1 = care, 0 = don't care. + * @{ + */ +//! [Topic Bits] +typedef enum +{ + TOPIC_LEVEL_FLAG = 0x01, + TOPIC_LINE_NUMBER_FLAG = 0x02, + TOPIC_FILE_NAME_FLAG = 0x04, + TOPIC_GROUP_FLAG = 0x08, + TOPIC_CATEGORY_FLAG = 0x10, + TOPIC_MESSAGE_FLAG = 0x20, + TOPIC_TIMESTAMP_FLAG = 0x40, + /** Bits 0 through TOPIC_COUNT turned on */ + TOPIC_ALL = 0x7f +} TopicBitsType; +//! [Topic Bits] + +/** @} */ + +typedef int TOPIC_FLAGS; + +//! [Topic Offsets] +/** Offsets within the m_vIntProps and m_vStringProps arrays for this topic. */ +typedef enum +{ + TOPIC_LEVEL = 0, + TOPIC_LINE_NUMBER = 1, + /** This must be the number of integer fields. */ + TOPIC_INT_COUNT = 2, + + TOPIC_FILE_NAME = 0, + TOPIC_GROUP = 1, + TOPIC_CATEGORY = 2, + TOPIC_MESSAGE = 3, + /** This must be the number of string fields for this topic. */ + TOPIC_STRING_COUNT = 4 +} TopicOffsetType; +//! [Topic Offsets] + +#endif // __LOGOG_CONST_HPP__ diff --git a/Base/logog/include/formatter.hpp b/Base/logog/include/formatter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b3b8378290a854c2720d57ad2e712b1cc118cebc --- /dev/null +++ b/Base/logog/include/formatter.hpp @@ -0,0 +1,89 @@ +/** + * \file formatter.hpp Formats a topic into human-readable, compiler lookalike format. + */ + +#ifndef __LOGOG_FORMATTER_HPP__ +#define __LOGOG_FORMATTER_HPP__ + +namespace logog +{ + +#define LOGOG_TIME_STRING_MAX 80 + +/** A helper object for generating a current timestamp as a string. */ +class TimeStamp : public Object +{ +public: + /** Returns a pointer to a string representing the current time. Note! This pointer is only + ** valid while this object is valid -- if you destroy this object, this pointer is no longer + ** valid. + */ + const char *Get(); + +protected: + char cTimeString[ LOGOG_TIME_STRING_MAX ]; +}; + +/** Converts a topic into a human-readable string for printing or otherwise rendering to a target. */ +class Formatter : public Object +{ +public: + Formatter(); + + /** Causes this formatter to format a topic into its own m_sMessageBuffer field, and thence to + ** return a reference to that string. This function must be written to be efficient; it will be called + ** for every logging operation. It is strongly recommended not to allocate or free memory in this function. + **/ + virtual LOGOG_STRING &Format( const Topic &topic, const Target &target ) = 0; + + /** Causes the time of day to be rendered, if it needs to be rendered. This function is only supported on + ** ANSI builds, not Unicode, as the underlying functions are ANSI only. + */ + virtual void RenderTimeOfDay(); + + /** In the base case, this function calls GetTopicFlags() on the provided + ** topic in order to figure out which fields this formatter should render. + ** However, subclasses of the Formatter class can override this function in order + ** to change the default fields that the topic wants to be rendered. For example, + ** you can turn off the TOPIC_LINE_NUMBER_FLAG and the TOPIC_FILE_NAME_FLAG + ** in order to disable these fields from being rendered in your own Formatter + ** subclass. + ** \param topic The topic whose flags are to be determined + ** \return The set of flags representing the topics that really need to be rendered. + **/ + virtual TOPIC_FLAGS GetTopicFlags( const Topic &topic ); + + /** Should this formatter render the current time of day? */ + bool GetShowTimeOfDay() const; + + /** Sets whether this formatter renders the current time of day. */ + void SetShowTimeOfDay(bool val); + +protected: + const LOGOG_CHAR *ErrorDescription( const LOGOG_LEVEL_TYPE level ); + + LOGOG_STRING m_sMessageBuffer; + LOGOG_STRING m_sIntBuffer; + + bool m_bShowTimeOfDay; +}; + +class FormatterGCC : public Formatter +{ +public: + virtual LOGOG_STRING &Format( const Topic &topic, const Target &target ); + +}; + +class FormatterMSVC : public Formatter +{ +public: + virtual LOGOG_STRING &Format( const Topic &topic, const Target &target ); +}; + +extern Formatter &GetDefaultFormatter(); +extern void DestroyDefaultFormatter(); + +} + +#endif // __LOGOG_FORMATTER_HPP_ diff --git a/Base/logog/include/logog.hpp b/Base/logog/include/logog.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e4c343c811f66843e7ab5b72e1a7e8712c204ee3 --- /dev/null +++ b/Base/logog/include/logog.hpp @@ -0,0 +1,36 @@ +/** + * \file logog.hpp Main include file for logog logging functionality. Include this file to enable logging for your application. + */ + +#ifndef __LOGOG_HPP__ +#define __LOGOG_HPP__ + +/** \def LOGOG_UNICODE + ** Define this macro to enable Unicode support in logog. The logog library works either in Unicode mode or not -- + ** attempting to mix the two log types will have unexpected results. + **/ +// #define LOGOG_UNICODE 1 + +#include "const.hpp" +#include "platform.hpp" +#include "statics.hpp" +#include "object.hpp" +#include "timer.hpp" +#include "mutex.hpp" +#include "string.hpp" +#include "node.hpp" +#include "topic.hpp" +#include "formatter.hpp" +#include "target.hpp" +// #include "socket.hpp" +#include "checkpoint.hpp" +#include "api.hpp" +#include "message.hpp" +#include "macro.hpp" + +#ifdef LOGOG_UNIT_TESTING +#include "thread.hpp" +#include "unittest.hpp" +#endif + +#endif // __LOGOG_HPP_ diff --git a/Base/logog/include/macro.hpp b/Base/logog/include/macro.hpp new file mode 100644 index 0000000000000000000000000000000000000000..97b661f8a3879f7fd1792f6cf0c8174b6efca5b8 --- /dev/null +++ b/Base/logog/include/macro.hpp @@ -0,0 +1,240 @@ +/** + * \file macro.hpp Macros for instantiation of a message. + */ + +#ifndef __LOGOG_MACRO_HPP__ +#define __LOGOG_MACRO_HPP__ + +namespace logog +{ +#ifdef LOGOG_USE_PREFIX +#define LOGOG_PREFIX LOGOG_ +#endif // LOGOG_USE_PREFIX + +#ifndef LOGOG_GROUP +/** This is the current group for created messages. Set this to NULL if you + * want messages to not be part of any specific group. + */ +#define LOGOG_GROUP NULL +#endif + +#ifndef LOGOG_CATEGORY +/** This is the current category for created messages. Set this to NULL if you + * want messages to not be part of any specific group. + */ +#define LOGOG_CATEGORY NULL +#endif + +/** When you have a macro replacement, the preprocessor will only expand the macros recursively + * if neither the stringizing operator # nor the token-pasting operator ## are applied to it. + * So, you have to use some extra layers of indirection, you can use the token-pasting operator + * with a recursively expanded argument. + */ +#define TOKENPASTE2(x, y) x ## y +/** \sa TOKENPASTE2(x, y) */ +#define TOKENPASTE(x, y) TOKENPASTE2(x, y) + +/** This macro is used when a message is instantiated without any varargs + * provided by the user. It locks a global mutex, creates the message, + * locks it, transmits it, and releases all locks. + */ +#define LOGOG_LEVEL_GROUP_CATEGORY_MESSAGE_NO_VA( level, group, cat, msg ) \ +{ \ + Mutex *___pMCM = &GetMessageCreationMutex(); \ + ___pMCM->MutexLock(); \ + static logog::Message *TOKENPASTE(_logog_,__LINE__) = new logog::Message( level, \ + LOGOG_CONST_STRING( __FILE__ ), \ + __LINE__ , \ + LOGOG_CONST_STRING( group ), \ + LOGOG_CONST_STRING( cat ), \ + msg; \ + ___pMCM->MutexUnlock(); \ + TOKENPASTE(_logog_,__LINE__)->m_Transmitting.MutexLock(); \ + TOKENPASTE(_logog_,__LINE__)->Transmit(); \ + TOKENPASTE(_logog_,__LINE__)->m_Transmitting.MutexUnlock(); \ +} + +/** This macro is used when a message is instantiated with varargs provided + * by the user. It locks a global mutex, creates the message, locks it, + * formats the message string inside the message, transmits it, + * and releases all locks. + * When logog is shut down, it may be started back up again later. Therefore, + * logog needs a way to flag all static Message pointers that they need + * to be recreated. We manually simulate a static Message pointer by + * implementing it via a static bool. The bool is turned on the first time + * this code is run. + * NOTE! A subtle race condition exists in the following code, that will ONLY occur + * if logog is shut down at the same moment that a log message is processed from + * another thread than the one calling the shutdown. The Message object could + * theoretically be destroyed from another thread just before it's locked + * in a thread that calls the Format() and Transmit() calls on it. I'm not + * sure if this is really a bug -- technically, this race condition will + * occur only if you are calling log messages right on top of the SHUTDOWN + * call from the main thread. + */ +#define LOGOG_LEVEL_GROUP_CATEGORY_MESSAGE( level, group, cat, formatstring, ... ) \ +{ \ + ::logog::Mutex *___pMCM = &::logog::GetMessageCreationMutex(); \ + ___pMCM->MutexLock(); \ + static bool TOKENPASTE(_logog_static_bool_,__LINE__) = false; \ + static logog::Message * TOKENPASTE(_logog_,__LINE__); \ + if ( TOKENPASTE(_logog_static_bool_,__LINE__) == false ) \ + { \ + TOKENPASTE(_logog_,__LINE__) = \ + new logog::Message( level, \ + LOGOG_CONST_STRING( __FILE__ ), \ + __LINE__ , \ + LOGOG_CONST_STRING( group ), \ + LOGOG_CONST_STRING( cat ), \ + LOGOG_CONST_STRING( "" ), \ + 0.0f, \ + & (TOKENPASTE(_logog_static_bool_,__LINE__)) ); \ + } \ + ___pMCM->MutexUnlock(); \ + /* A race condition could theoretically occur here if you are shutting down at the same instant as sending log messages. */ \ + TOKENPASTE(_logog_,__LINE__)->m_Transmitting.MutexLock(); \ + TOKENPASTE(_logog_,__LINE__)->Format( formatstring, ##__VA_ARGS__ ); \ + TOKENPASTE(_logog_,__LINE__)->Transmit(); \ + TOKENPASTE(_logog_,__LINE__)->m_Transmitting.MutexUnlock(); \ +} + +/** Calls LOGOG_LEVEL_GROUP_CATEGORY_MESSAGE with the current LOGOG_GROUP and + * LOGOG_CATEGORY setting. + */ +#define LOGOG_LEVEL_MESSAGE( level, formatstring, ... ) \ + LOGOG_LEVEL_GROUP_CATEGORY_MESSAGE( level, LOGOG_GROUP, LOGOG_CATEGORY, formatstring, ##__VA_ARGS__ ) + +/** Calls LOGOG_LEVEL_MESSAGE with the current LOGOG_LEVEL setting. */ +#define LOGOG_MESSAGE( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL, formatstring, ##__VA_ARGS__ ) + + +#if LOGOG_LEVEL >= LOGOG_LEVEL_DEBUG +/** Logs a message at the DEBUG reporting level. */ +#define LOGOG_DEBUG( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_DEBUG, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_DEBUG( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_INFO +/** Logs a message at the INFO reporting level. */ +#define LOGOG_INFO( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_INFO, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_INFO( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_WARN3 +/** Logs a message at the WARN3 reporting level. */ +#define LOGOG_WARN3( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_WARN3, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_WARN3( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_WARN2 +/** Logs a message at the WARN2 reporting level. */ +#define LOGOG_WARN2( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_WARN2, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_WARN2( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_WARN1 +/** Logs a message at the WARN1 reporting level. */ +#define LOGOG_WARN1( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_WARN1, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_WARN1( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_WARN +/** Logs a message at the WARN reporting level. */ +#define LOGOG_WARN( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_WARN, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_WARN( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_ERROR +/** Logs a message at the ERROR reporting level. */ +#define LOGOG_ERROR( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_ERROR, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_ERROR( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_CRITICAL +/** Logs a message at the CRITICAL reporting level. */ +#define LOGOG_CRITICAL( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_CRITICAL, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_CRITICAL( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_ALERT +/** Logs a message at the ALERT reporting level. */ +#define LOGOG_ALERT( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_ALERT, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_ALERT( formatstring, ... ) {}; +#endif + +#if LOGOG_LEVEL >= LOGOG_LEVEL_EMERGENCY +/** Logs a message at the EMERGENCY reporting level. */ +#define LOGOG_EMERGENCY( formatstring, ... ) \ + LOGOG_LEVEL_MESSAGE( LOGOG_LEVEL_EMERGENCY, formatstring, ##__VA_ARGS__ ) +#else +#define LOGOG_EMERGENCY( formatstring, ... ) {}; +#endif + +#define LOGOG_SET_LEVEL( level ) \ + ::logog::SetDefaultLevel( level ); + +/** Define this compilation flag if your compilation environment conflicts with + * any of the shorthand logging macros in macro.hpp. + */ +#ifndef LOGOG_USE_PREFIX +/* If you get compilation errors in this section, then define the flag LOGOG_USE_PREFIX during compilation, and these + * shorthand logging macros won't exist -- you'll need to use the LOGOG_* equivalents above. + */ +/* We can't use DEBUG in Win32 unfortunately, so we use DBUG for shorthand here. */ +//! [Shorthand] +/** \sa LOGOG_DEBUG */ +#define DBUG(...) LOGOG_DEBUG( __VA_ARGS__ ) +/** \sa LOGOG_INFO */ +#define INFO(...) LOGOG_INFO( __VA_ARGS__ ) +/** \sa LOGOG_WARN3 */ +#define WARN3(...) LOGOG_WARN3( __VA_ARGS__ ) +/** \sa LOGOG_WARN2 */ +#define WARN2(...) LOGOG_WARN2( __VA_ARGS__ ) +/** \sa LOGOG_WARN1 */ +#define WARN1(...) LOGOG_WARN1( __VA_ARGS__ ) +/** \sa LOGOG_WARN */ +#define WARN(...) LOGOG_WARN( __VA_ARGS__ ) +/** \sa LOGOG_ERROR */ +#define ERR(...) LOGOG_ERROR( __VA_ARGS__ ) +/** \sa LOGOG_ALERT */ +#define ALERT(...) LOGOG_ALERT( __VA_ARGS__ ) +/** \sa LOGOG_CRITICAL */ +#define CRITICAL(...) LOGOG_CRITICAL( __VA_ARGS__ ) +/** \sa LOGOG_EMERGENCY */ +#define EMERGENCY(...) LOGOG_EMERGENCY( __VA_ARGS__ ) +//! [Shorthand] +#endif + +/** Call this function to initialize logog and prepare for logging. + * \sa logog::Initialize() + */ +#define LOGOG_INITIALIZE(...) logog::Initialize( __VA_ARGS__ ); + +/** Call this function to shut down logog and release all memory allocated. + * \sa logog::Shutdown() + */ + +#define LOGOG_SHUTDOWN() logog::Shutdown(); + +} // namespace logog + +#endif // __LOGOG_MACRO_HPP_ diff --git a/Base/logog/include/message.hpp b/Base/logog/include/message.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c7d7dc659c9f2ca706a99c34180712f727a01911 --- /dev/null +++ b/Base/logog/include/message.hpp @@ -0,0 +1,45 @@ +/** + * \file message.hpp Messages; items transmitted to a log. + */ + +#ifndef __LOGOG_MESSAGE_HPP__ +#define __LOGOG_MESSAGE_HPP__ + +namespace logog +{ + +/** A message is a piece of text that's actually transmitted to outputs. Messages can be asked to + ** Transmit() themselves once they are created. + **/ +class Message : public Checkpoint +{ +public: + Message( const LOGOG_LEVEL_TYPE level = LOGOG_LEVEL_ALL, + const LOGOG_CHAR *sFileName = NULL, + const int nLineNumber = 0, + const LOGOG_CHAR *sGroup = NULL, + const LOGOG_CHAR *sCategory = NULL, + const LOGOG_CHAR *sMessage = NULL, + const double dTimestamp = 0.0f, + bool *bIsCreated = NULL ); + + virtual ~Message(); + + /** Causes this checkpoint to republish itself to all existing filters after + * unpublishing itself. This can be necessary if the message within this + * message has changed in such a way that the downstream Filter objects + * might react differently to it. + */ + virtual bool Republish(); + + Mutex m_Transmitting; + bool *m_pbIsCreated; +}; + +extern Mutex &GetMessageCreationMutex(); +extern void DestroyMessageCreationMutex(); + +} + + +#endif // __LOGOG_MESSAGE_HPP_ diff --git a/Base/logog/include/mutex.hpp b/Base/logog/include/mutex.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c9916f0eaa568e0c486d84036526289c34b64782 --- /dev/null +++ b/Base/logog/include/mutex.hpp @@ -0,0 +1,98 @@ +/** + * \file mutex.hpp Defines a mutual exclusion object for the current platform. + */ + +#ifndef __LOGOG_MUTEX_HPP__ +#define __LOGOG_MUTEX_HPP__ + +#ifdef LOGOG_FLAVOR_POSIX +#include <pthread.h> +#endif + +namespace logog +{ + +//! [Mutex] +#ifndef LOGOG_MUTEX + +#ifdef LOGOG_FLAVOR_WINDOWS +#define LOGOG_MUTEX(x) CRITICAL_SECTION (x); +#define LOGOG_MUTEX_INIT(x) InitializeCriticalSection (x) +#define LOGOG_MUTEX_DELETE(x) DeleteCriticalSection (x) +#define LOGOG_MUTEX_LOCK(x) EnterCriticalSection (x) +#define LOGOG_MUTEX_UNLOCK(x) LeaveCriticalSection (x) +#define LOGOG_MUTEX_CTOR(x) +#endif // LOGOG_FLAVOR_WINDOWS + +#ifdef LOGOG_FLAVOR_POSIX +#define LOGOG_MUTEX(x) pthread_mutex_t (x); +#define LOGOG_MUTEX_INIT(x) pthread_mutex_init(x, 0) +#define LOGOG_MUTEX_DELETE(x) pthread_mutex_destroy (x) +#define LOGOG_MUTEX_LOCK(x) pthread_mutex_lock (x) +#define LOGOG_MUTEX_UNLOCK(x) pthread_mutex_unlock (x) +#define LOGOG_MUTEX_CTOR(x) +#endif // LOGOG_FLAVOR_POSIX +#endif // LOGOG_MUTEX + +#ifndef LOGOG_MUTEX +#error You need to define mutex macros for your platform; please see mutex.hpp +#endif + +//! [Mutex] + +/** An object that can only be locked by one thread at a time. Implement the LOGOG_MUTEX_* functions for your platform + * to support the Mutex object. + * A mutex is intended to be used with the ScopedLock object to implement critical sections within logog. + * \sa ScopedLock + */ +class Mutex : public Object +{ +public: + Mutex(); + ~Mutex(); + /** Acquires a lock on the mutex. Only one thread is permitted to lock the mutex at one time. */ + void MutexLock(); + /** Releases the lock on the mutex. */ + void MutexUnlock(); + +protected: + Mutex(const Mutex &); + Mutex & operator = (const Mutex &); + + LOGOG_MUTEX( m_Mutex ) +}; + +/** Asserts a lock while this object exists and is in scope. A ScopedLock should be + * declared in "auto" format, typically on the stack. + */ +class ScopedLock : public Object +{ +public : + /** Instances and locks a ScopedLock. + * \param mutex The mutex to attempt to lock. Program execution halts at this point until the lock can be obtained. + */ + ScopedLock( Mutex &mutex ); + ~ScopedLock(); +protected: + /** A pointer to the lockable mutex. */ + Mutex *m_pMutex; + +private: + /* no default constructor */ + ScopedLock(); + /* no copy constructor */ + ScopedLock( const ScopedLock &other ); +}; + +#ifdef LOGOG_LEAK_DETECTION +extern Mutex s_AllocationsMutex; +extern void LockAllocationsMutex(); +extern void UnlockAllocationsMutex(); +#endif // LOGOG_LEAK_DETECTION + +extern Mutex &GetStringSearchMutex(); +extern void DestroyStringSearchMutex(); + +} + +#endif // __LOGOG_MUTEX_HPP_ diff --git a/Base/logog/include/node.hpp b/Base/logog/include/node.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8174635b4821c521d681d8b77c73a0bcc655bf06 --- /dev/null +++ b/Base/logog/include/node.hpp @@ -0,0 +1,142 @@ +/** + * \file node.hpp Base class for higher-level logog objects. + */ + +#ifndef __LOGOG_NODE_HPP__ +#define __LOGOG_NODE_HPP__ + +namespace logog +{ +/** The base class for most high-level logog objects. Represents the publisher-subscriber + ** model within logog. */ +class Node; + +/** An aggregation of nodes. Internally, we choose a set representation because we want to be able to traverse + ** the aggregation quickly while still looking up entries quickly. + **/ +typedef LOGOG_SET< Node *, std::less< Node * >, Allocator< Node * > > NodesType; + +/** A type that double inherits from NodesType and Mutex. A lockable NodesType. Handles the copy + ** case correctly. + **/ +class LockableNodesType : public NodesType, public Mutex +{ +public: + /** A LockableNodesType shouldn't copy the internal Mutex when it is copied, but it + ** should copy all the internal nodes. + **/ + LockableNodesType & operator = (const LockableNodesType &other); +}; + +extern LockableNodesType &GetStaticNodes( void ** pvLocation ); + +/** Returns a reference to the global nodes group. Allocates a new global node group if one does not already + ** exist. + */ +extern LockableNodesType &AllNodes(); + +/** Returns a reference to the group of nodes that are capable of subscribing. Allocates a new global subscriber + ** node group if one does not already exist. + */ +extern LockableNodesType &AllSubscriberNodes(); + +/** Returns a reference to the group of nodes that are capable of both subscribing as well as publishing. Allocates a new global subscriber + ** node group if one does not already exist. + */ +extern LockableNodesType &AllFilters(); + +/** Returns a reference to the group of nodes that represent terminals in the graph, i.e. nodes that can't publish. */ +extern LockableNodesType &AllTargets(); + +class Node : public Object +{ +public: + + /** All nodes self-register as part of the all-nodes database. */ + Node(); + + ~Node(); + + /** Call this function immediately after creating a node (or any of the children of the node class.) This function currently + ** registers the node as part of the list of subscriber nodes, if this node may in fact subscribe. + ** If this node is capable of subscribing at all, then this function registers this node as a possible subscriber. + ** Doing this helps to keep down the number of nodes we search, when we are determining which nodes a new node + ** might subscribe to. We have to do this registration as a second step, after the node is completely + ** initialized, as subscriberness is determined late in initialization. + **/ + virtual void Initialize(); + + /** Can a node send notifications? By default they can; later subclasses may not be able to. */ + virtual bool CanPublish() const; + /** Can a node receive notifications? By default they can; later subclasses may not be able to. */ + virtual bool CanSubscribe() const; + /** Is this node interested in receiving notifications from another topic? */ + virtual bool CanSubscribeTo( const Node & ); + + /** In order to avoid bringing in a bunch of RTTI stuff, we permit nodes to be asked whether they're topics or not */ + virtual bool IsTopic() const; + + /** Causes this node to begin publishing events to the given subscriber. + ** \param subscriber The node to receive published events + ** \return true if the request was successful, false if the subscriber was already subscribed + **/ + virtual bool PublishTo( Node &subscriber ); + + /** Causes this node to attempt to publish to some other nodes. */ + virtual bool PublishToMultiple( LockableNodesType &nodes ); + + /** Causes this node to stop publishing events to this subscriber. + ** \param subscriber The node to stop receiving events + ** \return true if successful, false if the subscriber was not being published to in the first place + **/ + virtual bool UnpublishTo( Node &subscriber ); + + /** Causes this node to attempt to unpublish to some other nodes. */ + virtual bool UnpublishToMultiple( LockableNodesType &nodes ); + + /** Causes this node to start receiving events from the given publisher. + ** \param publisher The node to start receiving events from + ** \return true if successful, false if the publisher was already subscribed + **/ + virtual bool SubscribeTo( Node &publisher ); + + /** Causes this node to attempt to subscribe to some other nodes. */ + virtual bool SubscribeToMultiple( LockableNodesType &nodes ); + + + /** Causes this node to unsubscribe from the given publisher's events. + ** \param publisher The publisher to unsubscribe from + ** \return true if successful, false if the node was already unsubscribed + **/ + virtual bool UnsubscribeTo( Node &publisher ); + + /** Causes this node to attempt to unsubscribe to some other nodes. */ + virtual bool UnsubscribeToMultiple( LockableNodesType &nodes ); + + void Clear(); + + + /** A pointer to any custom data you need to store for a node. */ + void *m_pUserData1; + + /** A pointer to any custom data you need to store for a node. */ + void *m_pUserData2; + +protected: + /** A bunch of nodes that are interested in what this node has to report. */ + LockableNodesType m_Subscribers; + + /** A bunch of nodes that this node interested in hearing from. */ + LockableNodesType m_Publishers; +}; + +extern void DestroyNodesList( void **pvList ); + +/** Destroys all nodes currently recorded. This happens at shutdown time. NOTE! If you have allocated + ** your own logog items and free them yourself AFTER this call, exciting crashes will occur. + **/ +extern void DestroyAllNodes(); + +} + +#endif // __LOGOG_NODE_HPP_ diff --git a/Base/logog/include/object.hpp b/Base/logog/include/object.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2ecbf5806dae9e7c96f6406c677a426caeedb0c0 --- /dev/null +++ b/Base/logog/include/object.hpp @@ -0,0 +1,221 @@ +/** + * \file object.hpp Base class for all allocated logog objects. + */ + +#ifndef __LOGOG_OBJECT_HPP__ +#define __LOGOG_OBJECT_HPP__ + +namespace logog +{ + +#ifdef LOGOG_LEAK_DETECTION +/** Iteration over an unordered map is slow, but we only do it at shutdown time. Access to a lookup or insert + ** is very fast. Additionally, we do not allocate leak detection records from our own heap; it saves us from + ** doing a recursive allocation. + **/ +typedef void *PointerType; + +/** A type describing the currently outstanding memory allocations. Note that this type does not inherit from + ** Object, because to do so would create an infinite recursion when memory is allocated. + **/ +typedef LOGOG_UNORDERED_MAP< PointerType, size_t > AllocationsType; + +/** All currently outstanding memory allocations, including their size. */ +extern AllocationsType s_Allocations; + +/** A global function to lock the global allocations mutex. We must do this as a static + ** void because Mutexes depend on Objects. This should probably belong in a class, but then + ** we get recursive class definitions. + ** */ +extern void LockAllocationsMutex(); + +/** A global function to unlock the global allocations mutex. We must do this as a static + ** void because Mutexes depend on Objects. */ +extern void UnlockAllocationsMutex(); +#endif // LOGOG_LEAK_DETECTION + +#ifdef new +#define LOGOG_PREVIOUS_DEFINITION_OF_NEW new +#undef new +#endif +#ifdef delete +#define LOGOG_PREVIOUS_DEFINITION_OF_DELETE delete +#undef delete +#endif //delete + +/** Base class for all objects allocated with logog. */ +class Object +{ +public: + Object(); + /* Some builds complain about ~Object() being virtual... sorry lint :( */ + virtual ~Object(); + /** Initializes an object of size new. */ + void *operator new( size_t nSize ); + /** Initializes an array of size new. */ + void *operator new[](size_t nSize); + + /* There's a wonderful behavior in Windows MFC in debug builds that causes it + * to attempt to redefine NEW with a macro if we're compiling in debug mode + * and we're using Gdiplus as well. In that case, Windows attempts to redefine + * new with a macro for everything that has a new. This wonderful behavior + * is worked around here. See http://support.microsoft.com/kb/317799/EN-US/ + * and http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/0df13145-670e-4070-b0a1-61794b20dff7 + * for more exciting information. + */ +#ifdef _DEBUG +#ifdef LOGOG_FLAVOR_WINDOWS + void* operator new(size_t nSize, LPCSTR lpszFileName, int nLine); + void* operator new[](size_t nSize, LPCSTR lpszFileName, int nLine); + void operator delete(void* ptr, LPCSTR lpszFileName, int nLine); + void operator delete[](void* ptr, LPCSTR lpszFileName, int nLine); +#endif // LOGOG_FLAVOR_WINDOWS +#endif // _DEBUG + + /** Deletes an object pointed to by ptr. */ + void operator delete( void *ptr ); + /** Deletes an object array pointed to by ptr. */ + void operator delete[]( void *ptr ); + + /** Allocates nSize bytes of memory. You must call logog::Initialize() before calling this function. + * \sa Initialize() + */ + static void *Allocate( size_t nSize ); + + /** Deallocate a pointer previously acquired by Allocate(). */ + static void Deallocate( void *ptr ); + }; + +#ifdef LOGOG_PREVIOUS_DEFINITION_OF_NEW +#define new LOGOG_PREVIOUS_DEFINITION_OF_NEW +#endif + +#ifdef LOGOG_PREVIOUS_DEFINITION_OF_DELETE +#define delete GA_PREVIOUS_DEFINITION_OF_DELETE +#endif + +/** An STL-compatible allocator which redirects all memory requests to the logog allocator. Used for all STL-like classes within logog. */ +template <class T> +class Allocator +{ +public: + /** Memory allocation size type. */ + typedef size_t size_type; + /** Memory allocation comparison type. */ + typedef ptrdiff_t difference_type; + /** A pointer to T type. */ + typedef T *pointer; + /** A const pointer to T type. */ + typedef const T *const_pointer; + /** A reference to T type. */ + typedef T &reference; + /** A const reference to T type. */ + typedef const T &const_reference; + /** A value type (T itself). */ + typedef T value_type; + + Allocator() {} + /** Not implemented here -- required by the STL standard though. */ + Allocator( const Allocator & ) {} + + /** Allocate and return n value_types of memory through this allocator. Requires that logog::Initialize() has been called. */ + pointer allocate( size_type n, const void * = 0 ) + { + T *t = ( T * ) Object::Allocate( n * sizeof( value_type ) ); + return t; + } + + /** Frees memory previously allocated by allocate(). */ + void deallocate( void *p, size_type ) + { + if ( p ) + { + Object::Deallocate( p ); + } + } + + /** Returns the address of a reference to T. */ + pointer address( reference x ) const + { + return &x; + } + /** Returns the address of a const reference to T. */ + const_pointer address( const_reference x ) const + { + return &x; + } + /** STL required override for = operator. */ + Allocator<T>& operator=( const Allocator & ) + { + return *this; + } + /** Constructs a new T at location p with value val. */ + void construct( pointer p, const T &val ) + { + new(( T * ) p ) T( val ); + } + /** Destroys a T at location p. */ + void destroy( pointer p ) + { +#ifdef LOGOG_FLAVOR_WINDOWS + // MSVC tends to complain unless we reference this pointer here. + p; +#endif // LOGOG_FLAVOR_WINDOWS + p->~T(); + } + + /** The largest size of an object that can be allocated with this allocator. */ + size_type max_size() const + { + return size_t( -1 ); + } + + /** Rebinding to permit allocations of unknown types. Part of std::allocator definition. + * \param other The other "unknown" type to be permitted access to this allocator */ + template <class U> + struct rebind + { + /** The "other" class that will use this allocator for its allocation. */ + typedef Allocator<U> other; + }; + + /** Required by STL -- unused here. */ + template <class U> + Allocator( const Allocator<U>& ) {} + + /** Permit this allocator to be used for assignment in other classes */ + template <class U> + Allocator &operator=( const Allocator<U>& ) + { + return *this; + } +}; + +/* All specializations of this allocator are interchangeable. */ +template <class T1, class T2> +bool operator== ( const Allocator<T1>&, + const Allocator<T2>& ) +{ + return true; +} +template <class T1, class T2> +bool operator!= ( const Allocator <T1>&, + const Allocator<T2>& ) +{ + return false; +} + +// + +/** Returns the current number of outstanding memory allocations in logog. Returns -1 iff LOGOG_LEAK_DETECTION + ** has not been defined at compile time. + **/ +extern int MemoryAllocations(); + +/** Sends a report to cout describing the current memory allocations that exist. Returns the outstanding number of + ** memory allocations, or -1 iff LOGOG_LEAK_DETECTION is defined. + **/ +extern int ReportMemoryAllocations(); + +} +#endif // __LOGOG_OBJECT_HPP diff --git a/Base/logog/include/platform.hpp b/Base/logog/include/platform.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4f39ebe091b4fe6aee496b4af3c2e2c1f54eba87 --- /dev/null +++ b/Base/logog/include/platform.hpp @@ -0,0 +1,180 @@ +/** + * \file platform.hpp Platform specific detection and defines, including STL overrides + */ + +#ifndef __LOGOG_PLATFORM_HPP__ +#define __LOGOG_PLATFORM_HPP__ + +#ifdef DOXYGEN +/** Set this compilation flag to 1 to indicate that this is a Windows-like platform, e.g. Win32, Win64, Xbox 360, etc. */ +#define LOGOG_FLAVOR_WINDOWS 1 +/** Set this compilation flag to 1 to indicate that this is a Posix-like platform, e.g. Linux, PS3, etc. */ +#define LOGOG_FLAVOR_POSIX 1 +#endif + +#ifndef LOGOG_FLAVOR_WINDOWS +#ifndef LOGOG_FLAVOR_POSIX + +/* The user hasn't told us which flavor we're running on, so we must make a guess as to which platform is valid. */ + +/* If this is MSVC, then it's Windows like */ +#ifdef _MSC_VER +#define LOGOG_FLAVOR_WINDOWS 1 +#endif // _MSC_VER + +/* gcc probably means Posix */ +#ifdef __GNUC__ +#define LOGOG_FLAVOR_POSIX 1 +#endif + +#ifdef __CYGWIN__ +#ifdef LOGOG_UNICODE +#error LOGOG_UNICODE not supported on Cygwin platform because Cygwin does not support vsnwprintf +#endif +/* Cygwin lacks vsnprintf support in headers but it's in the libraries. First we + * need to define the constants size_t and va_list, then define vsnprintf */ +#include <cstdio> +#include <cstdlib> +#include <cstdarg> +extern int vsnprintf(char *str, size_t size, const char *format, va_list ap); +#define LOGOG_USE_TR1 1 +#endif // __CYGWIN__ + +#ifdef __linux__ +#define LOGOG_USE_TR1 1 +#endif + +#ifdef __APPLE__ +#define LOGOG_USE_TR1 1 +#endif + +/* If we've recognized it already, it's a relatively modern compiler */ +#if defined( LOGOG_FLAVOR_WINDOWS ) || defined( LOGOG_FLAVOR_POSIX ) +#define LOGOG_HAS_UNORDERED_MAP 1 +#endif // defined(...) + +/* PS3 */ +#ifdef SN_TARGET_PS3 +#include "proprietary/ps3.hpp" +#endif + +#endif // LOGOG_FLAVOR_POSIX +#endif // LOGOG_FLAVOR_WINDOWS + +#ifdef LOGOG_FLAVOR_WINDOWS +/* Detect Xbox 360 */ +#if _XBOX_VER >= 200 +#include "proprietary/xbox360.hpp" +#else +/* Windows has been detected. */ + +/** Microsoft's CRT library has its own leak detection mechanism. If you don't trust logog's + ** LOGOG_LEAK_DETECTION, you can enable LOGOG_LEAK_DETECTION_WINDOWS to enable + ** Microsoft's version. This really doesn't belong in platform.hpp -- feel + ** free to refactor this into a Windows-specific header. + **/ +#ifdef LOGOG_LEAK_DETECTION_WINDOWS +#define _CRTDBG_MAP_ALLOC +#include <stdlib.h> +#include <crtdbg.h> + +#ifdef _DEBUG +#ifndef DBG_NEW +#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ ) +#define new DBG_NEW +#endif +#endif // _DEBUG +#endif // LOGOG_LEAK_DETECTION_WINDOWS + +#include "windows.h" +#endif // _XBOX_VER +#endif // LOGOG_FLAVOR_WINDOWS + +#ifndef LOGOG_FLAVOR_WINDOWS +#ifndef LOGOG_FLAVOR_POSIX +#error Platform flavor not detected. Please see platform.hpp to configure this platform. +#endif +#endif + +// For the FILE type. +#include <cstdio> +// For POSIX file access. +#include <cstdlib> +// For Ubuntu 11 and ptrdiff_t +#include <cstddef> + +#include <ctime> + +#if defined(LOGOG_LEAK_DETECTION) || defined(LOGOG_INTERNAL_DEBUGGING) +#include <cstdio> +#include <iostream> +#endif // LOGOG_LEAK_DETECTION || LOGOG_INTERNAL_DEBUGGING + +#ifdef LOGOG_FLAVOR_POSIX +#include <sys/time.h> +// for uint64_t +#include <inttypes.h> +#include <wchar.h> +// for strstr support +#include <cstring> +#endif + +// For Unicode support +#include <typeinfo> + +/* ----------------------------------------------------------- */ +/* Here's the stuff your compiler may have a problem with... */ + +//! [STLTypes] + +/** The definition for a hash type in logog. You can replace it here with your own set compatible class if needed. */ +#define LOGOG_HASH std::tr1::hash +/** The definition for an unordered map type in logog. You can replace it here with your own unordered_map compatible class if needed. */ + +#ifdef LOGOG_HAS_UNORDERED_MAP +#ifdef LOGOG_USE_TR1 +#include <tr1/unordered_map> +#else +#include <unordered_map> +#endif // LOGOG_USE_TR1 +#ifdef _GLIBCXX_UNORDERED_MAP +#define LOGOG_UNORDERED_MAP std::unordered_map +#else // _GLIBCXX_UNORDERED_MAP +#define LOGOG_UNORDERED_MAP std::tr1::unordered_map +#endif // _GLIBCXX_UNORDERED_MAP +#else // LOGOG_HAS_UNORDERED_MAP +#include <hash_map> +#define LOGOG_UNORDERED_MAP std::hash_map +#endif // LOGOG_HAS_UNORDERED_MAP + +/** An internal consistency error has been detected in logog. */ +#define LOGOG_INTERNAL_FAILURE abort(); + +/* ----------------------------------------------- */ +/* Here's the stuff that's pretty standard in STL by now. */ + +/** The definition for a pair type in logog. You can replace it here with your own pair compatible class if needed. */ +#define LOGOG_PAIR std::pair +#include <list> +/** The definition for a list type in logog. You can replace it here with your own list compatible class if needed. */ +#define LOGOG_LIST std::list +#include <vector> +/** The definition for a vector type in logog. You can replace it here with your own vector compatible class if needed. */ +#define LOGOG_VECTOR std::vector +#include <set> +/** The definition for a set type in logog. You can replace it here with your own set compatible class if needed. */ +#define LOGOG_SET std::set +/** The definition for STL "equal to" in logog. You can replace it here with your own set compatible class if needed. */ +#define LOGOG_EQUAL_TO std::equal_to + +//! [STLTypes] + +/** The default port number that logog uses to communicate via TCP/UDP */ +#define LOGOG_DEFAULT_PORT 9987 + +/** We use va_list for formatting messages. */ +#include <cstdarg> + + + +#endif // __LOGOG_PLATFORM_HPP diff --git a/Base/logog/include/socket.hpp b/Base/logog/include/socket.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8417eeb89f9d12328012e28419bc6eb405a9be0e --- /dev/null +++ b/Base/logog/include/socket.hpp @@ -0,0 +1,150 @@ +/** + * \file socket.hpp Cross-platform socket abstractions + */ + +/** + * \todo Socket support is unfinished and is not working yet. Please review socket.hpp if you'd like to implement + * a cross-platform socket abstraction. + */ +#ifndef __LOGOG_SOCKET_HPP_ +#define __LOGOG_SOCKET_HPP_ + +#ifdef LOGOG_FLAVOR_WINDOWS +#pragma comment(lib, "wsock32.lib") +#endif + +#include <memory.h> + +namespace logog +{ +/** A sending or receiving socket to be used as a target. */ +class Socket : public Target +{ +public: + static int Initialize() + { + static bool bInitialized = false; + + if ( !bInitialized ) + { +#ifdef LOGOG_FLAVOR_WINDOWS + WORD wVersionRequested; + WSADATA wsaData; + int err; + wVersionRequested = MAKEWORD( 2, 2 ); + + err = WSAStartup( wVersionRequested, &wsaData ); + + if ( err != 0 ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + LOGOG_COUT << _LG("WSAStartup failed with error: ") << err << endl; +#endif + return 1; + } + +#endif + } + + bInitialized = true; + + return 0; + } + + static void Shutdown() + { +#ifdef LOGOG_FLAVOR_WINDOWS + WSACleanup(); +#endif + } + + static const int MAXHOSTNAME = 255; + + Socket( + int type = SOCK_STREAM, + int port = LOGOG_DEFAULT_PORT + ) + { + m_Socket = -1; + m_nType = type; + m_nPort = port; + } + + virtual void Close() + { +#ifdef LOGOG_FLAVOR_WINDOWS + closesocket( m_Socket ); +#endif +#ifdef LOGOG_FLAVOR_POSIX + close( m_Socket ); +#endif + } + +#ifdef NYI + virtual int Create( int type, + int port ) + { + char myname[MAXHOSTNAME+1]; + int err; + struct sockaddr_in sa; + struct hostent *hp; + + memset( &sa, 0, sizeof( struct sockaddr_in ) ); /* clear our address */ + gethostname( myname, MAXHOSTNAME ); /* who are we? */ + hp= gethostbyname( myname ); /* get our address info */ + + if ( hp == NULL ) /* we don't exist !? */ + return -1; + + sa.sin_family= hp->h_addrtype; /* this is our host address */ + sa.sin_port= (u_short)htons( (u_short)port ); /* this is our port number */ + + if (( m_Socket = socket( AF_INET, type, 0 ) ) < 0 ) /* create socket */ + return -1; + + if ( bind( m_Socket, (struct sockaddr*) &sa, sizeof( struct sockaddr_in ) ) < 0 ) + { + Close(); + return -1; + } + + if (( err = SetNonBlocking() ) != 0 ) + return err; + } + +#endif // NYI + + virtual int SetNonBlocking() + { + int err; + +#ifdef LOGOG_FLAVOR_POSIX + int flags; + flags = socket_fcntl( m_Socket, F_GETFL, 0 ); + flags |= O_NONBLOCK; + err = socket_fcntl( m_Socket, F_SETFL, flags ); +#endif + +#ifdef LOGOG_FLAVOR_WINDOWS + unsigned long parg; + parg = 1; + err = ioctlsocket( m_Socket, FIONBIO, &parg ); +#endif + return err; + } + + virtual int Output( const LOGOG_STRING &output ) = 0; + +protected: + int m_Socket; + int m_nType; + int m_nPort; +}; + +class SocketServer : Socket +{ + +}; +} + +#endif // __LOGOG_CHECKPOINT_HPP_ diff --git a/Base/logog/include/statics.hpp b/Base/logog/include/statics.hpp new file mode 100644 index 0000000000000000000000000000000000000000..938bd3d23a6f96921275a5aafe3501ad4b0780a6 --- /dev/null +++ b/Base/logog/include/statics.hpp @@ -0,0 +1,74 @@ +/*! + * \file statics.hpp Static variables simultaneously assigned for all instances of logog. + */ + +#ifndef __LOGOG_STATICS_HPP__ +#define __LOGOG_STATICS_HPP__ + + +namespace logog +{ + +class Timer; +class Formatter; +class Target; +class Mutex; + +extern void DestroyAllNodes(); +extern void DestroyGlobalTimer(); +extern void DestroyDefaultFormatter(); +extern void DestroyStringSearchMutex(); +extern void DestroyMessageCreationMutex(); + +/* Technically this information should be in node.hpp but statics is responsible for + * this global list. + */ +class Statics +{ +public: + /** A pointer to the malloc() compatible function used by logog. See logog::Initialize() + ** for more details. */ + void *(*s_pfMalloc)( size_t ); + /** A pointer to the free() compatible function used by logog. See logog::Initialize() + ** for more details. */ + void (*s_pfFree)( void * ); + /** Pointers to all the currently existing nodes in the network. */ + void *s_pAllNodes; + /** Pointers to only those nodes that are capable of subscribing. */ + void *s_pAllSubscriberNodes; + /** Pointers to only those nodes that are capable of subscribing and publishing. */ + void *s_pAllFilterNodes; + /** Pointers to the group of all valid targets. */ + void *s_pAllTargets; + /** Pointer to the default filter, if any. */ + void *s_pDefaultFilter; + /** The default global shared timer. All events are generally in reference to this timer, though yoy may create your own timers. */ + Timer *s_pTimer; + /** A lock on the KMP search for all strings. Prevents mutex explosions. */ + void *s_pStringSearchMutex; + /** A lock for creating messages. Prevents dual message creation from multiple threads. */ + Mutex *s_pMessageCreationMutex; + /** The default Formatter for all targets. Targets may use their individual formatters as well if preferred. */ + Formatter *s_pDefaultFormatter; + /** The number of sockets created. */ + int s_nSockets; + /** A pointer to this object; used for final destruction. */ + Statics *s_pSelf; + + Statics(); + ~Statics(); + + /** Resets all statics to startup values. Releases memory allocated by statics. */ + void Reset(); + +}; + +extern Statics &Static(); + +/** Destroys the Static() structure. Calls to Static() after calling DestroyStatic() will probably crash your + ** program. + */ +extern void DestroyStatic(); + +} +#endif // __LOGOG_STATICS_HPP diff --git a/Base/logog/include/string.hpp b/Base/logog/include/string.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e069b3a26fc939f1163416b57e45710dea797a9e --- /dev/null +++ b/Base/logog/include/string.hpp @@ -0,0 +1,115 @@ +/** + * \file string.hpp Defines the logog string class. + */ +#ifndef __LOGOG_STRING_HPP__ +#define __LOGOG_STRING_HPP__ + +/** \def LOGOG_UNICODE + ** Define this typedef in order to support wide characters in all logog strings. + ** \sa _LG + ** */ + +#ifdef LOGOG_UNICODE +/** logog has detected Unicode; therefore a LOGOG_CHAR is a wide character. */ +typedef wchar_t LOGOG_CHAR; +/** This is a naughty little hack. If we're using Unicode, then we append an + * L to your const string. However, we have several situations in which that string will actually be a NULL or 0 + * value in code, and the string will be rendered as L0 or LNULL. In that case, we catch it with yet another + * macro. Hacky, but seems to do the trick. Beware of conflicts with existing code though...! + */ +#define L0 (const LOGOG_CHAR *)'\0' +#define LNULL (const LOGOG_CHAR *)'\0' +#define L__null (const LOGOG_CHAR *)'\0' + +/* Two indirect references are necessary in order to expand a string and append the L to it. */ +#define LOGOG_CONST_STRING_INDIRECT(x) L ## x +/* This macro will cause a const string to be stored as a Unicode string on a Unicode build. */ +#define LOGOG_CONST_STRING(x) LOGOG_CONST_STRING_INDIRECT(x) +#define LOGOG_COUT std::wcout +#define LOGOG_CERR std::wcerr + +#else // LOGOG_UNICODE + +/** logog has not detected Unicode; therefore a LOGOG_CHAR is simply a char. */ +typedef char LOGOG_CHAR; +/** This macro will cause a const string to be stored as an ANSI string on an ANSI build, and as a Unicode string + * on a Unicode build. + */ +#define LOGOG_CONST_STRING(x) (x) +#define LOGOG_COUT std::cout +#define LOGOG_CERR std::cerr +#endif // LOGOG_UNICODE + +/** If this constant is defined, then you can use the shorthand macro _LG in your code to represent a constant + * string. + */ +#ifndef LOGOG_USE_PREFIX +/** The _LG() macro is defined only if LOGOG_USE_PREFIX is not defined. _LG() can be used to describe + ** a const string that is compiled to either as Unicode + * or ANSI, based on the setting of the LOGOG_UNICODE flag. + * _LG() is not needed if you don't need Unicode support. If you want your messages to work with both Unicode + * as well as ANSI builds of logog, preface them like this: _LG("This const string works on Unicode as well as ANSI.") + */ +#define _LG( x ) LOGOG_CONST_STRING( x ) +#endif + +namespace logog +{ + class String : public Object + { + public: + + static const size_t npos = (size_t) -1; + + String(); + virtual ~String(); + virtual void Free(); + static size_t Length( const LOGOG_CHAR *chars ); + + String( const String &other ); + String( const LOGOG_CHAR *pstr ); + String & operator =( const String & other); + String & operator =( const LOGOG_CHAR *pstr ); + size_t size() const; + virtual void clear(); + virtual size_t reserve( size_t nSize ); + virtual size_t reserve_for_int(); + virtual operator const LOGOG_CHAR *() const; + virtual size_t assign( const String &other ); + virtual size_t append( const String &other ); + virtual size_t append( const LOGOG_CHAR *other ); + virtual void reverse( LOGOG_CHAR* pStart, LOGOG_CHAR* pEnd); + virtual size_t assign( const int value ); + virtual size_t append( const LOGOG_CHAR c ); + virtual bool is_valid() const; + virtual size_t assign( const LOGOG_CHAR *other, const LOGOG_CHAR *pEnd = NULL ); + + virtual size_t find( String &other ) const; + virtual void format( const LOGOG_CHAR *cFormatString, ... ); + virtual void format_va( const LOGOG_CHAR *cFormatString, va_list args ); + + virtual const LOGOG_CHAR* c_str() const; + + protected: + virtual void Initialize(); + + /* Code modified from http://www-igm.univ-mlv.fr/~lecroq/string/node8.html#SECTION0080 */ + void preKmp(size_t m); + + size_t KMP( const LOGOG_CHAR *y, size_t n ); + +#define LOGOG_MAX( a, b ) ( ( a > b ) ? a : b ) + + size_t BM(LOGOG_CHAR *y, size_t n); + + LOGOG_CHAR *m_pBuffer; + LOGOG_CHAR *m_pOffset; + LOGOG_CHAR *m_pEndOfBuffer; + size_t *m_pKMP; + bool m_bIsConst; + }; +} + +#define LOGOG_STRING ::logog::String + +#endif // __LOGOG_STRING_HPP_ diff --git a/Base/logog/include/target.hpp b/Base/logog/include/target.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a5844135013a3962aa20577df5e766ef400cdcfd --- /dev/null +++ b/Base/logog/include/target.hpp @@ -0,0 +1,176 @@ +/** + * \file target.hpp Abstractions representing logging outputs. + */ + +#ifndef __LOGOG_TARGET_HPP_ +#define __LOGOG_TARGET_HPP_ + +namespace logog +{ +/** A target is abstraction representing an output stream from logog. cerr, cout, and syslog, and other logging formats are supported. + ** Targets do not validate their received data. Their only job is to render it on a call to Receive() to their supported target + ** type. Targets should generally make sure to handle calls on multiple threads -- they should make sure to avoid overlapping + ** outputs from multiple threads correctly. This base class handles this serialization in the Receive() function. + ** Children of this class are expected to implement the Output() function to do the actual output. + **/ +class Target : public TopicSink +{ + friend class LogBuffer; +public : + Target(); + virtual ~Target(); + + /** Sets the current formatter for this target. */ + void SetFormatter( Formatter &formatter ); + + /** Returns a reference to the current formatter for this target. */ + Formatter &GetFormatter() const; + + /** All targets must implement the Output function. This function outputs the provided string to the + * output that the target represents. + * \return Zero if no error has occurred; an error code (generally propagated + * from the operating system) if an error has occurred. + */ + virtual int Output( const LOGOG_STRING &data ) = 0; + + /** Receives a topic on behalf of this target. A mutex prevents race conditions from occurring when + ** multiple threads attempt to write to this target at the same time. + ** \return Zero if no error has occurred; an error code (generally propagated from the operating + ** system) if an error has occurred. + */ + virtual int Receive( const Topic &topic ); + + /** Does this target want its formatter to null terminate its strings? */ + bool GetNullTerminatesStrings() const { return m_bNullTerminatesStrings; } + + /** Tells this target whether to request its formatter to null terminate its strings. */ + void SetNullTerminatesStrings(bool val) { m_bNullTerminatesStrings = val; } + + +protected: + /** A pointer to the formatter used for this output. */ + Formatter *m_pFormatter; + /** A mutex on the Receive() function. */ + Mutex m_MutexReceive; + /** Does this target cause its formatter to null-terminate its output strings? File and buffer outputs + ** don't require null terminated strings, but line outputs do. + **/ + bool m_bNullTerminatesStrings; +}; + +/** A target representing the cerr stream. */ +class Cerr : public Target +{ + virtual int Output( const LOGOG_STRING &data ); +}; + +/** A target representing the cout stream. */ +class Cout : public Target +{ + virtual int Output( const LOGOG_STRING &data ); +}; + +/** A target representing the debugger stream on Win32 targets. This only logs information + * on Windows like platforms. + */ +class OutputDebug : public Target +{ + virtual int Output( const LOGOG_STRING &data ); +}; + +/** A LogFile renders received messages to a file. Provide the name of the file to be rendered to as + * a parameter to the construction of the LogFile() object. Destroying a LogFile object will cause the + * output file to be closed. LogFile objects always append to the output file; they do not delete the previous + * log file. + */ +class LogFile : public Target +{ +public: + /** Creates a LogFile object. + * \param sFileName The name of the file to be created. + * Since file names do not support Unicode on most systems, there is no option to create + * a filename with a LOGOG_CHAR. + */ + LogFile(const char *sFileName); + + /** Closes the log file. */ + virtual ~LogFile(); + + /** Opens the log file on first write. */ + virtual int Open(); + + /** This function makes a guess as to the correct BOM for this file, and attempts + ** to write it into the file. It does this by considering the size of LOGOG_CHAR + ** as well as considering the current endianness of this system. This guess + ** may be incorrect. + **/ + virtual void WriteUnicodeBOM(); + + /** Writes the message to the log file. */ + virtual int Output( const LOGOG_STRING &data ); + + /** Should a Unicode BOM be written to the beginning of this log file, if the log file + * was previously empty? By default a BOM is written to a log file if LOGOG_UNICODE + * is enabled. */ + bool m_bWriteUnicodeBOM; + +protected: + char *m_pFileName; + bool m_bFirstTime; + bool m_bOpenFailed; + FILE *m_pFile; + + /** Does the actual fwrite to the file. Call Output() instead to handle error conditions better. */ + virtual int InternalOutput( size_t nSize, const LOGOG_CHAR *pData ); + +private: + LogFile(); +}; + +/** A buffering target. Stores up to a fixed buffer size of output and then renders that output to another + * target. Can be used for buffering log output in memory and then storing it to a log file upon program completion. + * To use, create another target (such as a LogFile) and then create a LogBuffer, providing the other target + * as a parameter to the creation function. + */ +class LogBuffer : public Target +{ +public: + LogBuffer( Target *pTarget = NULL, + size_t s = LOGOG_DEFAULT_LOG_BUFFER_SIZE ); + virtual ~LogBuffer(); + + /** Changes the current rendering target. NOTE: This function does no locking on either the target or + * this object. Program accordingly. + */ + virtual void SetTarget( Target &t ); + + /** Inserts a range of LOGOG_CHAR objects into this buffer. The characters should consist of a null-terminated + * string of length size. Providing anything else as input creates undefined behavior. + */ + virtual int Insert( const LOGOG_CHAR *pChars, size_t size ); + + /** Dumps the current contents of the buffer to the output target. */ + virtual int Dump(); + + virtual int Output( const LOGOG_STRING &data ); + +protected: + virtual void Allocate( size_t size ); + + virtual void Deallocate(); + + /** The non-changing pointer to the basic buffer. */ + LOGOG_CHAR *m_pStart; + /** The current write offset into the buffer. */ + LOGOG_CHAR *m_pCurrent; + /** The position in the buffer after which no data may be written. */ + LOGOG_CHAR *m_pEnd; + /** The size of the buffer in LOGOG_CHAR primitives. */ + size_t m_nSize; + /** A pointer to the target to which the buffer will be rendered upon calling Dump(). */ + Target *m_pOutputTarget; +}; + +} + +#endif // __LOGOG_TARGET_HPP_ diff --git a/Base/logog/include/thread.hpp b/Base/logog/include/thread.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c37e3045d464534c4d1ab17f83597ee1ad43ccc8 --- /dev/null +++ b/Base/logog/include/thread.hpp @@ -0,0 +1,121 @@ +/** + * \file thread.hpp Defines a thread object for the current platform. + */ + +#ifndef __LOGOG_THREAD_HPP__ +#define __LOGOG_THREAD_HPP__ + +//! [Thread] +#ifndef LOGOG_THREAD +#if defined(LOGOG_FLAVOR_WINDOWS) + +#include <process.h> + +typedef unsigned int (WINAPI * __logog_pThreadFn )(void *); + +#define LOGOG_THREAD HANDLE + +#define LOGOG_THREAD_CREATE(handle, attr, pStartFn, arg) \ + (int)((*handle=(HANDLE) _beginthreadex (NULL, /* security */ \ + 0, /* stack size */ \ + (__logog_pThreadFn)pStartFn, /* start address */ \ + arg, /* pv argument */ \ + 0, /* init flag */ \ + NULL /*thread addr */ ))==NULL) + +#define LOGOG_THREAD_JOIN( thread ) \ + ( (WaitForSingleObject(( thread ),INFINITE)!=WAIT_OBJECT_0) \ + || !CloseHandle(thread) \ + ) +#define LOGOG_THREAD_SELF (LOGOG_THREAD)GetCurrentThreadId() + +#endif // defined(LOGOG_FLAVOR_WINDOWS) + +#if defined(LOGOG_FLAVOR_POSIX) + +#define LOGOG_THREAD \ + pthread_t + +#define LOGOG_THREAD_CREATE(handle, attr, pStartFn, arg) \ + pthread_create(handle, attr, pStartFn, arg) + +#define LOGOG_THREAD_JOIN(thread) \ + pthread_join(thread, NULL) + +#define LOGOG_THREAD_SELF \ + pthread_self() + +#endif + +#endif // LOGOG_THREAD + +#ifndef LOGOG_THREAD +#error You need to define mutex macros for your platform; please see mutex.hpp +#endif + +//! [Thread] + +namespace logog +{ + +/** A thread abstraction. Requires definition of macros to describe how to create, start, and wait for threads to terminate. */ +class Thread +{ +public: + + /** A type describing the entry point of a function. */ + typedef void* (*ThreadStartLocationType)(void *); + + /** Creating a new thread requires the starting location as well as a single void pointer to the argument to a function. */ + Thread(ThreadStartLocationType fnThreadStart, void* pvParams) + { + m_pFnThreadStart = fnThreadStart; + m_pvThreadParams = pvParams; + } + + /** Cause the created thread to commence execution asynchronously. */ + int Start() + { + return LOGOG_THREAD_CREATE(&m_Thread, NULL, m_pFnThreadStart, m_pvThreadParams); + } + + /** Causes the current thread to wait for completion of the provided thread. + ** \param thread The thread object to wait for + */ + static int WaitFor(const Thread& thread) + { + return LOGOG_THREAD_JOIN(thread.m_Thread); + } + +#ifdef LOGOG_THREAD_JOIN_SUPPORT + static void Join(const std::vector<Thread*>& threads) + { + for(size_t i=0; i<threads.size(); i++) + WaitFor(*threads.at(i)); + } + + static void Delete(std::vector<Thread*>& threads) + { + for(size_t i=0; i<threads.size(); i++) + delete threads.at(i); + threads.clear(); + } +#endif // LOGOG_THREAD_JOIN_SUPPORT + + /** Returns a LOGOG_THREAD representing the calling process. */ + static LOGOG_THREAD GetCurrent() + { + return (LOGOG_THREAD) LOGOG_THREAD_SELF ; + } + +private: + /** A platform-specific identifier for the calling process. */ + LOGOG_THREAD m_Thread; + /** The entry point for this thread. */ + ThreadStartLocationType m_pFnThreadStart; + /** An arbitrary argument to the thread entry point. */ + void* m_pvThreadParams; +}; +} + +#endif // __LOGOG_THREAD_HPP_ \ No newline at end of file diff --git a/Base/logog/include/timer.hpp b/Base/logog/include/timer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..579103b2bb78f19eb0b794bb5ff16b9d20cd1643 --- /dev/null +++ b/Base/logog/include/timer.hpp @@ -0,0 +1,42 @@ +/** + * \file timer.hpp Time management. + */ + +#ifndef __LOGOG_TIMER_HPP__ +#define __LOGOG_TIMER_HPP__ + +namespace logog +{ +/** A value for a high resolution timer on this platform. Time representations are in seconds. */ +typedef double LOGOG_TIME; + +/** A high-resolution timer. Reports in seconds. */ +class Timer : public Object +{ +public: + Timer(); + + /** Returns the offset from the time since the creation of the timer, or the time set by the most + ** recent Set() call. Time is assumed to be a value in LOGOG_TIME seconds. + ** \sa LOGOG_TIME + **/ + LOGOG_TIME Get(); + + /** Sets the current time for this timer. */ + void Set( LOGOG_TIME time ); + +protected: +#ifdef LOGOG_FLAVOR_WINDOWS + /** Windows only. Stores the number of high resolution timer ticks per second. */ + double m_fTicksPerMicrosecond; +#endif + /** Zero, if no calls to Set() have been made; else the value of the previous call to Set(). */ + LOGOG_TIME m_fStartTime; +}; + +extern Timer &GetGlobalTimer(); +extern void DestroyGlobalTimer(); + +} + +#endif // __LOGOG_TIMER_HPP_ diff --git a/Base/logog/include/topic.hpp b/Base/logog/include/topic.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ae3d6f3e5af801692354e7c8ebcd8c18a6e5916b --- /dev/null +++ b/Base/logog/include/topic.hpp @@ -0,0 +1,203 @@ +/** + * \file topic.hpp Topics -- subjects of interests for publishers and + * subscribers to communicate about + */ + +#ifndef __LOGOG_TOPIC_HPP__ +#define __LOGOG_TOPIC_HPP__ + +namespace logog +{ + +/** A subject that nodes can choose to discuss with one another. + ** Subscribers generally have very general topics, while publishers generally have very specific topics. + **/ +class Topic : public Node +{ + friend class TopicLevel; + friend class TopicGroup; + +public: + /** Creates a topic. Note the defaults for creating a topic -- these defaults are equivalent to "no setting" + ** for those fields. + **/ + Topic( const LOGOG_LEVEL_TYPE level = LOGOG_LEVEL_ALL, + const LOGOG_CHAR *sFileName = NULL, + const int nLineNumber = 0, + const LOGOG_CHAR *sGroup = NULL, + const LOGOG_CHAR *sCategory = NULL, + const LOGOG_CHAR *sMessage = NULL, + const double dTimestamp = 0.0f ); + + /** Topics are always topics. We use this to avoid any RTTI dependence. */ + virtual bool IsTopic() const; + + /** Causes this topic to publish another topic to all its subscribers. + ** \return 0 if successful, non-zero if this topic failed to send the publication to all subscribers */ + virtual int Send( const Topic &node ); + + /** Causes this topic to publish itself to all its subscribers. */ + virtual int Transmit(); + + /** Permits this node to receive a publication from another node, and act upon it. + ** \param node The node constituting the publication + ** \return 0 if successful, non-zero if this node failed to process the publication + **/ + virtual int Receive( const Topic &node ); + + /** Is this topic interested in receiving notifications from another topic? This function implements + ** a generic (slow) test that should work for all topic types. This function only checks fields + ** that have previously been set on this topic -- fields that have not been set will not limit this + ** topic's ability to subscribe. If any of the previously set fields does not "match" the other topic, + ** this function will return false. The matching function behaves slightly differently from field to + ** field. + ** - In the topic level case, this function rejects a publisher with a lower topic level than our + ** own. + ** - In the group, category, file name and message case, this function rejects a publisher if our + ** own group/category/file name or message cannot be found as a substring in the possible publisher. + ** This functionality permits very simple pattern matching functionality (i.e. show me all the message + ** lines that have the word "upload" in them, regardless of their log level.) + ** - In the line number case, this function rejects a publisher unless the line number matches exactly. + ** - In the timestamp case, this function rejects a publisher if their timestamp is before our own. + ** \param otherNode The topic which we are considering subscribing to + **/ + virtual bool CanSubscribeTo( const Node &otherNode ); + + virtual bool CanSubscribeCheckTopic( const Topic &other ); + + /** Causes this topic to begin publishing events to the given subscriber. + ** \param subscriber The node to receive published events + ** \return true if the request was successful, false if the subscriber was already subscribed + **/ + virtual bool PublishTo( Node &subscriber ); + + + + /** Formats the message in this topic given a sprintf-style set of arguments. + ** This function can be used to set the current message in this topic to a string with a variable number of parameters. + **/ + virtual void Format( const LOGOG_CHAR *cFormatMessage, ... ); + + const LOGOG_STRING &FileName() const; + void FileName( const LOGOG_STRING &s ); + + const LOGOG_STRING &Message() const; + void Message( const LOGOG_STRING &s ); + + const LOGOG_STRING &Category() const; + void Category( const LOGOG_STRING &s ); + + const LOGOG_STRING &Group() const; + void Group( const LOGOG_STRING &s ); + + int LineNumber() const; + void LineNumber( const int num ); + + LOGOG_LEVEL_TYPE Level() const; + void Level( LOGOG_LEVEL_TYPE level ); + + LOGOG_TIME Timestamp() const; + void Timestamp( const LOGOG_TIME t ); + + TOPIC_FLAGS GetTopicFlags() const; + +protected: + /** An array (not an STL vector) of string properties for this topic. */ + LOGOG_STRING m_vStringProps[ TOPIC_STRING_COUNT ]; + /** An array (not an STL vector) of integer properties for this topic. */ + int m_vIntProps[ TOPIC_INT_COUNT ]; + /** The time associated with this topic. Usually this field is updated when a topic is triggered. Times need not be associated + ** with a particular topic, in which case this value is zero. + ** */ + LOGOG_TIME m_tTime; + /** A bitfield representing the "important" fields in this topic. Not all fields are considered to contain important information + ** all the time. A logical OR of the TOPIC_*_FLAG fields. + ** \sa TopicBitsType + **/ + TOPIC_FLAGS m_TopicFlags; +}; + +/** A topic that permits both publishing as well as subscribing. This class is functionally same as a Topic; we've added it + ** as a class for clarity when referring to different topic types. Filters should be instantiated only after outputs are + ** instantiated, as they automatically search for and publish to targets. If you instantiate a filter before you + ** instantiate a target, you will need to call PublishTo( theTarget ) yourself before using the target. + **/ +class Filter : public Topic +{ +public: + Filter( const LOGOG_LEVEL_TYPE level = LOGOG_LEVEL_ALL, + const LOGOG_CHAR *sFileName = NULL, + const int nLineNumber = 0, + const LOGOG_CHAR *sGroup = NULL, + const LOGOG_CHAR *sCategory = NULL, + const LOGOG_CHAR *sMessage = NULL, + const double dTimestamp = 0.0f ); +}; + +/** Returns a reference to the unique default filter instantiated with logog. */ +extern Filter &GetDefaultFilter(); + +/** Sets the current reporting level for the default filter. All messages + * connected to the filter after this point should obey this default + * level setting. + */ +void SetDefaultLevel( LOGOG_LEVEL_TYPE level ); + +/** A topic with the group name being the only field of significance. */ +class TopicGroup : public Topic +{ +public: + TopicGroup( const LOGOG_CHAR *sGroup = NULL ); + + virtual bool CanSubscribeCheckTopic( const Topic &other ); +}; + +/** A topic with the level being the only field of significance. */ +class TopicLevel : public Topic +{ +public: + TopicLevel( const LOGOG_LEVEL_TYPE level ); + virtual bool CanSubscribeCheckTopic( const Topic &other ); +}; + +/** A topic that is also a source. */ +class TopicSource : public Topic +{ +public: + TopicSource( const LOGOG_LEVEL_TYPE level = LOGOG_LEVEL_ALL, + const LOGOG_CHAR *sFileName = NULL, + const int nLineNumber = 0, + const LOGOG_CHAR *sGroup = NULL, + const LOGOG_CHAR *sCategory = NULL, + const LOGOG_CHAR *sMessage = NULL, + const double dTimestamp = 0.0f ); + + /** Returns false. Sources do not subscribe. */ + virtual bool SubscribeTo( Node & ); + + /** Returns false. Sources do not unsubscribe. */ + virtual bool UnsubscribeTo( Node & ); + virtual bool CanSubscribe() const; +}; + +/** A topic that is also a sink. */ +class TopicSink : public Topic +{ +public: + virtual bool IsTopic() const; + + /** Sinks do not add themselves to the list of interested subscribers. That's up to intermediate topics to decide. */ + virtual void Initialize(); + + /** Returns false. Sinks do not publish. */ + virtual bool PublishTo( Node & ); + + /** Returns false. Sinks do not unpublish. */ + virtual bool UnpublishTo( Node & ); + + /** Returns false. Sinks do not publish. */ + virtual bool CanPublish() const; +}; +} + +#endif // __LOGOG_TOPIC_HPP_ diff --git a/Base/logog/include/unittest.hpp b/Base/logog/include/unittest.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3441636bd56d41db50bcf2184402fe1639129496 --- /dev/null +++ b/Base/logog/include/unittest.hpp @@ -0,0 +1,151 @@ +/** + * \file unittest.hpp Unit testing interface; may be used for other programs as well. + */ +#ifndef __LOGOG_UNITTEST_HPP +#define __LOGOG_UNITTEST_HPP + +#include <iostream> +#include <string> + +namespace logog +{ + +/** + * \page unittesting Unit test framework + * + * A unit test framework is included with logog. This framework was intended specifically to exercise logog functionality, but it may also + * be used as a general purpose test framework. + * + * A typical test program will look like this: + * \code + * int main( int argc, char *argv[] ) + * { + * int nResult; + * nResult = RunAllTests(); + * ShutdownTests(); + * return nResult; + * } + * \endcode + * + * To define a unit test, create a function of the following form: + * + * \code + * UNITTEST( AdditionTest ) + * { + * logog::Initialize(); + * + * int nResult = 0; + * + * if ( 2 + 2 == 4 ) + * { + * LOGOG_COUT << _LG("Sane.") << endl; + * } + * else + * { + * LOGOG_COUT << _LG("Insane!") << endl; + * nResult = 1; + * } + * + * logog::Shutdown(); + * + * return nResult; + * + * }; + * \endcode + * + * The UNITTEST() macro defines a unique UnitTest object that encompasses your function. + * Your function should take no parameters and return an integer value. It should return + * zero if the test was successful, and non-zero if the test was unsuccesful. If any + * of your tests fail, the stub main() program will propagate that error to the operating + * system, which in turn can be used to halt an automated build system. + * + * The unit testing framework is known to leak memory. However, the underlying logog macros are not known to leak memory (let us know + * if you find any leaks). + */ + +/** A standard string type, used for labelling a test. We don't use LOGOG_STRING here because that class is mutable + ** and it allocates memory. + **/ +typedef const char * TestNameType; +class UnitTest; + +/** A registry for all tests. All tests are instanced using the UNITTEST() macro and stored in the LogogTestRegistry. + ** \ref UNITTEST + **/ +typedef LOGOG_LIST< UnitTest * > TestRegistryType; + +/** All unit tests are registered in here at program initialization time. */ +extern TestRegistryType &LogogTestRegistry(); + +/** A TestSignup is responsible for recording each instanced UnitTest in the test registry. */ +class TestSignup +{ +public: + /** Creates a new TestSignup. Only called from constructor for UnitTest. */ + TestSignup( UnitTest *pTest ); + +protected: + /** A pointer back to the UnitTest that created this TestSignup */ + UnitTest *m_pTest; +private: + TestSignup(); +}; + +/** The base class for unit testing. Children of UnitTest are instanced by the UNITTEST() macro. */ +class UnitTest +{ +public: + /** Instances a test. An instanced test is automatically executed when the RunAllTests() function is called. + * \param sTestName A string representing the name of this test. + */ + UnitTest( const TestNameType &sTestName ); + + virtual ~UnitTest(); + /** Returns the name of this UnitTest provided at construction time. */ + virtual TestNameType &GetName(); + /** Child classes of UnitTest() must provide a RunTest() function. A RunTest() function must initialize logog, conduct its + ** tests, and return 0 if the test was successful, a non-0 value otherwise. If any RunTest() function returns any value other than + ** zero, then the main RunAllTests() function will return non zero as well. + */ + virtual int RunTest() = 0; + + /** This function is called during ShutdownTests(). Its purpose is to free + ** the internal structures allocated by the UnitTest without freeing + ** the UnitTest itself. Microsoft's leak detector will flag the internals + ** of a UnitTest as a leak unless these internals are explicitly destroyed + ** prior to exit(). + **/ + virtual void FreeInternals(); + +protected: + /** The name of this particular test. */ + TestNameType m_sTestName; + /** A pointer to the TestSignup constructed by this UnitTest. */ + TestSignup *m_pTestSignup; +private: + UnitTest(); +}; + +/** Executes all currently registered tests and prints a report of success or failure. */ +extern int RunAllTests(); + +/** Should remove all memory allocated during unit testing. */ +extern void ShutdownTests(); + +/** This should be the function prefix for a unit test. It defines a new class for the test inherited from UnitTest. It instances + ** a member of the class at run-time (before main() starts). Lastly it provides the function definition for the actual test class. + */ +#define UNITTEST( x ) class x : public UnitTest { \ +public: \ + x( TestNameType name ) : \ + UnitTest( name ) \ + {} \ + virtual ~x() {}; \ + virtual int RunTest(); \ + }; \ + x __LogogTestInstance_ ## x ( #x ); \ + int x::RunTest() + +} + +#endif // __LOGOG_UNITTEST_HPP diff --git a/Base/logog/readme.txt b/Base/logog/readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..1c0339e148845dc6afeccff2a6870fcd5ff8560d --- /dev/null +++ b/Base/logog/readme.txt @@ -0,0 +1,11 @@ +See http://www.logog.org or http://johnwbyrd.github.com/logog/ for more information and complete documentation. + +logog is a portable C++ library to facilitate logging of real-time events in performance-oriented applications, such as games. It is especially appropriate for projects that have constrained memory and constrained CPU requirements. + +General support and discussion is available at http://groups.google.com/group/logog . Development support and discussion is available at http://groups.google.com/group/logog-devel . + +This project can be built using CMake, available from http://www.cmake.org . + +Local documentation can be generated with doxygen, available from http://www.doxygen.org . + +The license agreement for logog is viewable at http://johnwbyrd.github.com/logog/license.html . diff --git a/Base/logog/src/api.cpp b/Base/logog/src/api.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2b67c14051b29e29e3661aa5753b8bd1b51a3523 --- /dev/null +++ b/Base/logog/src/api.cpp @@ -0,0 +1,74 @@ + /* + * \file api.cpp + */ + +#include "logog.hpp" + +#include <cstdlib> + +namespace logog { + +static Mutex s_mInitialization; +static int s_nInitializations = 0; + +int Initialize( INIT_PARAMS *params ) +{ + s_mInitialization.MutexLock(); + + if ( s_nInitializations++ == 0 ) + { + if ( params == NULL ) + { + Static().s_pfMalloc = malloc; + Static().s_pfFree = free; + } + else + { + if ( params->m_pfMalloc != NULL ) + { + Static().s_pfMalloc = params->m_pfMalloc; + Static().s_pfFree = params->m_pfFree; + } + else + { + Static().s_pfMalloc = malloc; + Static().s_pfFree = free; + } + } + + // Let's allocate a default filter here. + GetDefaultFilter(); + + // Socket::Initialize(); + } + + s_mInitialization.MutexUnlock(); + + return 0; +} + +int Shutdown( ) +{ + s_mInitialization.MutexLock(); + + if ( --s_nInitializations == 0 ) + { + // Socket::Shutdown(); + +#ifdef LOGOG_DESTROY_STATIC_AREA + delete &( Static() ); +#else + Static().Reset(); +#endif + +#ifdef LOGOG_LEAK_DETECTION + ReportMemoryAllocations(); +#endif + } + + s_mInitialization.MutexUnlock(); + + return 0; +} +} + diff --git a/Base/logog/src/checkpoint.cpp b/Base/logog/src/checkpoint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45ecf39f5b0299ecc70a2c9e682ae2f1ca8dff15 --- /dev/null +++ b/Base/logog/src/checkpoint.cpp @@ -0,0 +1,30 @@ + +/* + * \file checkpoint.cpp + */ + +#include "logog.hpp" + +namespace logog { + + Checkpoint::Checkpoint( const LOGOG_LEVEL_TYPE level, + const LOGOG_CHAR *sFileName , + const int nLineNumber, + const LOGOG_CHAR *sGroup, + const LOGOG_CHAR *sCategory, + const LOGOG_CHAR *sMessage, + const double dTimestamp ) : + TopicSource( level, sFileName, nLineNumber, sGroup, sCategory, sMessage, dTimestamp ) + { + } + + int Checkpoint::Send( const Topic &node ) + { + /* Optionally update our own timestamp before we send on our information */ + if (( m_TopicFlags & TOPIC_TIMESTAMP_FLAG ) != 0 ) + m_tTime = GetGlobalTimer().Get(); + + return TopicSource::Send( node ); + } +} + diff --git a/Base/logog/src/formatter.cpp b/Base/logog/src/formatter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..22515f3c5a6cb9362b176aabeae462dc93202fa0 --- /dev/null +++ b/Base/logog/src/formatter.cpp @@ -0,0 +1,238 @@ + +/* + * \file formatter.cpp + */ + +#include "logog.hpp" + +namespace logog { + + Formatter::Formatter() : + m_bShowTimeOfDay( false ) + { + m_sMessageBuffer.reserve( LOGOG_FORMATTER_MAX_LENGTH ); + m_sIntBuffer.reserve_for_int(); + } + + void Formatter::RenderTimeOfDay() + { + if ( m_bShowTimeOfDay ) + { +#ifndef LOGOG_UNICODE + TimeStamp stamp; + m_sMessageBuffer.append( stamp.Get() ); + m_sMessageBuffer.append(": "); +#endif + } + } + + const LOGOG_CHAR * Formatter::ErrorDescription( const LOGOG_LEVEL_TYPE level ) + { + if ( level <= LOGOG_LEVEL_NONE ) + return LOGOG_CONST_STRING("none"); + + if ( level <= LOGOG_LEVEL_EMERGENCY ) + return LOGOG_CONST_STRING("emergency"); + + if ( level <= LOGOG_LEVEL_ALERT ) + return LOGOG_CONST_STRING("alert"); + + if ( level <= LOGOG_LEVEL_CRITICAL ) + return LOGOG_CONST_STRING("critical"); + + if ( level <= LOGOG_LEVEL_ERROR ) + return LOGOG_CONST_STRING("error"); + + if ( level <= LOGOG_LEVEL_WARN ) + return LOGOG_CONST_STRING("warning"); + + if ( level <= LOGOG_LEVEL_INFO ) + return LOGOG_CONST_STRING("info"); + + if ( level <= LOGOG_LEVEL_DEBUG ) + return LOGOG_CONST_STRING("debug"); + + return LOGOG_CONST_STRING("unknown"); + } + + bool Formatter::GetShowTimeOfDay() const + { + return m_bShowTimeOfDay; + } + + void Formatter::SetShowTimeOfDay( bool val ) + { + m_bShowTimeOfDay = val; + } + + TOPIC_FLAGS Formatter::GetTopicFlags( const Topic &topic ) + { + return topic.GetTopicFlags(); + } + + LOGOG_STRING &FormatterGCC::Format( const Topic &topic, const Target &target ) + { + TOPIC_FLAGS flags; + flags = GetTopicFlags( topic ); + + m_sMessageBuffer.clear(); + + if ( flags & TOPIC_FILE_NAME_FLAG ) + { + m_sMessageBuffer.append( topic.FileName() ); + m_sMessageBuffer.append( ':' ); + } + + if ( flags & TOPIC_LINE_NUMBER_FLAG ) + { + m_sIntBuffer.assign( topic.LineNumber() ); + m_sMessageBuffer.append( m_sIntBuffer ); + + m_sMessageBuffer.append( LOGOG_CONST_STRING(": ")); + } + + RenderTimeOfDay(); + + if ( flags & TOPIC_LEVEL_FLAG ) + { + m_sMessageBuffer.append( ErrorDescription( topic.Level())); + m_sMessageBuffer.append( LOGOG_CONST_STRING(": ")); + } + + if ( flags & TOPIC_GROUP_FLAG ) + { + m_sMessageBuffer.append( LOGOG_CONST_STRING("{") ); + m_sMessageBuffer.append( topic.Group() ); + m_sMessageBuffer.append( LOGOG_CONST_STRING("} ") ); + } + + if ( flags & TOPIC_CATEGORY_FLAG ) + { + m_sMessageBuffer.append( LOGOG_CONST_STRING("[")); + m_sMessageBuffer.append( topic.Category() ); + m_sMessageBuffer.append( LOGOG_CONST_STRING("] ")); + } + + if ( flags & TOPIC_MESSAGE_FLAG ) + { + m_sMessageBuffer.append( topic.Message() ); + m_sMessageBuffer.append( (LOGOG_CHAR)'\n' ); + } + + if ( target.GetNullTerminatesStrings() ) + m_sMessageBuffer.append( (LOGOG_CHAR)NULL ); + + return m_sMessageBuffer; + } + + + LOGOG_STRING &FormatterMSVC::Format( const Topic &topic, const Target &target ) + { + m_sMessageBuffer.clear(); + + TOPIC_FLAGS flags; + flags = GetTopicFlags( topic ); + + if ( flags & TOPIC_FILE_NAME_FLAG ) + { + m_sMessageBuffer.append( topic.FileName() ); + m_sMessageBuffer.append( '(' ); + } + + if ( flags & TOPIC_LINE_NUMBER_FLAG ) + { + m_sIntBuffer.assign( topic.LineNumber() ); + m_sMessageBuffer.append( m_sIntBuffer ); + + m_sMessageBuffer.append( LOGOG_CONST_STRING(") : ") ); + } + + RenderTimeOfDay(); + + if ( flags & TOPIC_LEVEL_FLAG ) + { + m_sMessageBuffer.append( ErrorDescription( topic.Level() ) ); + m_sMessageBuffer.append( LOGOG_CONST_STRING(": ")); + } + + if ( flags & TOPIC_GROUP_FLAG ) + { + m_sMessageBuffer.append( LOGOG_CONST_STRING("{")); + m_sMessageBuffer.append( topic.Group() ); + m_sMessageBuffer.append( LOGOG_CONST_STRING("} ")); + } + + if ( flags & TOPIC_CATEGORY_FLAG ) + { + m_sMessageBuffer.append( LOGOG_CONST_STRING("[")); + m_sMessageBuffer.append( topic.Category() ); + m_sMessageBuffer.append( LOGOG_CONST_STRING("] ")); + } + + if ( flags & TOPIC_MESSAGE_FLAG ) + { + m_sMessageBuffer.append( topic.Message() ); +#ifdef LOGOG_FLAVOR_WINDOWS + m_sMessageBuffer.append( LOGOG_CONST_STRING("\r\n") ); +#else // LOGOG_FLAVOR_WINDOWS + m_sMessageBuffer.append( LOGOG_CONST_STRING("\n") ); +#endif // LOGOG_FLAVOR_WINDOWS + } + + if ( target.GetNullTerminatesStrings() ) + m_sMessageBuffer.append( LOGOG_CHAR( NULL ) ); + + return m_sMessageBuffer; + } + + Formatter &GetDefaultFormatter() + { + Statics *pStatic = &Static(); + + if ( pStatic->s_pDefaultFormatter == NULL ) + { +#ifdef LOGOG_FLAVOR_WINDOWS + pStatic->s_pDefaultFormatter = new FormatterMSVC(); +#else + pStatic->s_pDefaultFormatter = new FormatterGCC(); +#endif + } + + return *( pStatic->s_pDefaultFormatter ); + } + + void DestroyDefaultFormatter() + { + Statics *pStatic = &Static(); + Formatter *pDefaultFormatter = pStatic->s_pDefaultFormatter; + + if ( pDefaultFormatter != NULL ) + delete pDefaultFormatter; + + pStatic->s_pDefaultFormatter = NULL; +} + +const char * TimeStamp::Get() +{ + time_t tRawTime; + struct tm * tmInfo; + + time ( &tRawTime ); + +#ifdef LOGOG_FLAVOR_WINDOWS +#pragma warning( push ) +#pragma warning( disable : 4996 ) +#endif // LOGOG_FLAVOR_WINDOWS + /* Microsoft is afraid of this function; I'm not sure this warning is sensible */ + tmInfo = localtime ( &tRawTime ); +#ifdef LOGOG_FLAVOR_WINDOWS +#pragma warning( pop ) +#endif // LOGOG_FLAVOR_WINDOWS + + strftime (cTimeString, LOGOG_TIME_STRING_MAX, "%c", tmInfo); + + return cTimeString; +} + +} + diff --git a/Base/logog/src/lobject.cpp b/Base/logog/src/lobject.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8fbce7755567d1b4b1e29df5f2ee6d7bf2cb6ab4 --- /dev/null +++ b/Base/logog/src/lobject.cpp @@ -0,0 +1,177 @@ +/* + * \file object.cpp + */ + +#include "logog.hpp" +#ifdef LOGOG_LEAK_DETECTION +#include <iostream> +#endif // LOGOG_LEAK_DETECTION + +namespace logog { + +#ifdef LOGOG_LEAK_DETECTION +AllocationsType s_Allocations; +#endif + + Object::Object() {} + + Object::~Object() + { + + } + + void *Object::operator new( size_t nSize ) + { + return Allocate( nSize ); + } + + void *Object::operator new[]( size_t nSize ) + { + return Allocate( nSize ); + } + +#ifdef _DEBUG +#ifdef LOGOG_FLAVOR_WINDOWS + + void *Object::operator new(size_t nSize, LPCSTR lpszFileName, int nLine) + { + /* avoid unref'd parameter warnings */ + lpszFileName; + nLine; + return Allocate( nSize ); + } + + void *Object::operator new[](size_t nSize, LPCSTR lpszFileName, int nLine) + { + /* avoid unref'd parameter warnings */ + lpszFileName; + nLine; + return Allocate( nSize ); + } + + void Object::operator delete(void *ptr, LPCSTR lpszFileName, int nLine) + { + lpszFileName; + nLine; + Deallocate( ptr ); + } + + void Object::operator delete[](void *ptr, LPCSTR lpszFileName, int nLine) + { + lpszFileName; + nLine; + Deallocate( ptr ); + } + +#endif // LOGOG_FLAVOR_WINDOWS +#endif // _DEBUG + + /** Deletes an object pointed to by ptr. */ + void Object::operator delete( void *ptr ) + { + Deallocate( ptr ); + } + /** Deletes an object array pointed to by ptr. */ + void Object::operator delete[]( void *ptr ) + { + Deallocate( ptr ); + } + + /** Allocates nSize bytes of memory. You must call logog::Initialize() before calling this function. + * \sa Initialize() + */ + void *Object::Allocate( size_t nSize ) + { + void *ptr = Static().s_pfMalloc( nSize ); +#ifdef LOGOG_REPORT_ALLOCATIONS + LOGOG_COUT << _LG("Allocated ") << nSize << _LG(" bytes of memory at ") << ptr << endl; +#endif // LOGOG_REPORT_ALLOCATIONS +#ifdef LOGOG_LEAK_DETECTION + AllocationsType::iterator it; + + LockAllocationsMutex(); + it = s_Allocations.find( ptr ); + + if ( it != s_Allocations.end() ) + { + LOGOG_COUT << _LG("Reallocation detected in memory manager! We seem to have allocated the same address twice ") + << _LG("without freeing it! Address = ") << ptr << std::endl; + UnlockAllocationsMutex(); + LOGOG_INTERNAL_FAILURE; + } + + s_Allocations.insert( LOGOG_PAIR< const PointerType, size_t >( ptr, nSize ) ); + UnlockAllocationsMutex(); +#endif // LOGOG_LEAK_DETECTION + return ptr; + } + + /** Deallocate a pointer previously acquired by Allocate(). */ + void Object::Deallocate( void *ptr ) + { +#ifdef LOGOG_LEAK_DETECTION + LockAllocationsMutex(); + AllocationsType::iterator it; + + it = s_Allocations.find( ptr ); + + if ( it == s_Allocations.end() ) + { + LOGOG_COUT << _LG("Freeing memory not previously allocated! Address = ") << ptr << std::endl; + UnlockAllocationsMutex(); + LOGOG_INTERNAL_FAILURE; + } + +#ifdef LOGOG_REPORT_ALLOCATIONS + LOGOG_COUT << _LG("Freeing ") << it->second << _LG(" bytes of memory at ") << it->first << endl; +#endif // LOGOG_REPORT_ALLOCATIONS + s_Allocations.erase( ptr ); + UnlockAllocationsMutex(); +#endif // LOGOG_LEAK_DETECTION + Static().s_pfFree( ptr ); + } + + int MemoryAllocations() + { +#ifdef LOGOG_LEAK_DETECTION + LockAllocationsMutex(); + size_t nSize = s_Allocations.size(); + + if ( nSize != 0 ) + LOGOG_COUT << _LG("Total active allocations: ") << nSize << std::endl; + + UnlockAllocationsMutex(); + return (int)nSize; +#else // LOGOG_LEAK_DETECTION + return -1; +#endif // LOGOG_LEAK_DETECTION + } + + int ReportMemoryAllocations() + { +#ifdef LOGOG_LEAK_DETECTION + LockAllocationsMutex(); + + if ( s_Allocations.size() == 0 ) + { + LOGOG_COUT << _LG("No memory allocations outstanding.") << std::endl; + } + else + { + for ( AllocationsType::iterator it = s_Allocations.begin(); + it != s_Allocations.end(); + it++ ) + { + LOGOG_COUT << _LG("Memory allocated at ") << it->first << + _LG(" with size ") << it->second << + _LG(" bytes ") << std::endl; + } + } + + UnlockAllocationsMutex(); +#endif + return MemoryAllocations(); + } + +} + diff --git a/Base/logog/src/logog.vcproj b/Base/logog/src/logog.vcproj new file mode 100644 index 0000000000000000000000000000000000000000..f80a5d75e08d9b275f30cc07e76f1944aa2fbcba --- /dev/null +++ b/Base/logog/src/logog.vcproj @@ -0,0 +1,323 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="logog" + ProjectGUID="{85857E80-2122-4DD8-9BA9-B90CC10D65B0}" + RootNamespace="logog" + SccProjectName="Perforce Project" + SccLocalPath=".." + SccProvider="MSSCCI:Perforce SCM" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="4" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="../include" + PreprocessorDefinitions="WIN32;_DEBUG;_LIB" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="4" + WarnAsError="true" + DebugInformationFormat="4" + ShowIncludes="false" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="4" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="../include" + PreprocessorDefinitions="WIN32;NDEBUG;_LIB" + RuntimeLibrary="2" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="4" + WarnAsError="true" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLibrarianTool" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\api.cpp" + > + </File> + <File + RelativePath=".\checkpoint.cpp" + > + </File> + <File + RelativePath=".\formatter.cpp" + > + </File> + <File + RelativePath=".\lobject.cpp" + > + </File> + <File + RelativePath=".\lstring.cpp" + > + </File> + <File + RelativePath=".\message.cpp" + > + </File> + <File + RelativePath=".\mutex.cpp" + > + </File> + <File + RelativePath=".\node.cpp" + > + </File> + <File + RelativePath=".\platform.cpp" + > + </File> + <File + RelativePath=".\socket.cpp" + > + </File> + <File + RelativePath=".\statics.cpp" + > + </File> + <File + RelativePath=".\target.cpp" + > + </File> + <File + RelativePath=".\timer.cpp" + > + </File> + <File + RelativePath=".\topic.cpp" + > + </File> + <File + RelativePath=".\unittest.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + <File + RelativePath="..\include\api.hpp" + > + </File> + <File + RelativePath="..\include\checkpoint.hpp" + > + </File> + <File + RelativePath="..\include\const.hpp" + > + </File> + <File + RelativePath="..\include\formatter.hpp" + > + </File> + <File + RelativePath="..\include\logog.hpp" + > + </File> + <File + RelativePath="..\include\macro.hpp" + > + </File> + <File + RelativePath="..\include\message.hpp" + > + </File> + <File + RelativePath="..\include\mutex.hpp" + > + </File> + <File + RelativePath="..\include\node.hpp" + > + </File> + <File + RelativePath="..\include\object.hpp" + > + </File> + <File + RelativePath="..\include\platform.hpp" + > + </File> + <File + RelativePath="..\include\socket.hpp" + > + </File> + <File + RelativePath="..\include\statics.hpp" + > + </File> + <File + RelativePath="..\include\string.hpp" + > + </File> + <File + RelativePath="..\include\target.hpp" + > + </File> + <File + RelativePath="..\include\thread.hpp" + > + </File> + <File + RelativePath="..\include\timer.hpp" + > + </File> + <File + RelativePath="..\include\topic.hpp" + > + </File> + <File + RelativePath="..\include\unittest.hpp" + > + </File> + <Filter + Name="Proprietary" + > + <File + RelativePath="..\include\proprietary\ps3.hpp" + > + </File> + <File + RelativePath="..\include\proprietary\xbox360.hpp" + > + </File> + </Filter> + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/Base/logog/src/lstring.cpp b/Base/logog/src/lstring.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5ab7c03112a9cdc8925f7d0fdd10ac8e24761a98 --- /dev/null +++ b/Base/logog/src/lstring.cpp @@ -0,0 +1,497 @@ + /* + * \file lstring.cpp + */ + +#include "logog.hpp" + +namespace logog { + + String::String() + { + Initialize(); + } + + String::~String() + { + Free(); + } + + void String::Free() + { + if ( m_pBuffer && ( m_bIsConst == false )) + { + Deallocate( (void *)m_pBuffer ); + m_pBuffer = m_pEndOfBuffer = m_pOffset = NULL; + } + + if ( m_pKMP ) + { + Deallocate( (void *)m_pKMP ); + m_pKMP = NULL; + } + } + + size_t String::Length( const LOGOG_CHAR *chars ) + { + unsigned int len = 0; + + while ( *chars++ ) + len++; + + return len; + } + + String & String::operator=( const String & other ) + { + Free(); + Initialize(); + assign( other ); + return *this; + } + + String & String::operator=( const LOGOG_CHAR *pstr ) + { + Free(); + Initialize(); + assign( pstr ); + return *this; + } + + String::String( const String &other ) + { + Initialize(); + assign( other ); + } + + String::String( const LOGOG_CHAR *pstr ) + { + Initialize(); + assign( pstr ); + } + + size_t String::size() const + { + return ( m_pOffset - m_pBuffer ); + } + + void String::clear() + { + m_pOffset = m_pBuffer; + } + + size_t String::reserve( size_t nSize ) + { + if ( nSize == (unsigned int)( m_pOffset - m_pBuffer )) + return nSize; + + if ( nSize == 0 ) + { + if ( m_pBuffer && ( *m_pBuffer != (LOGOG_CHAR)NULL )) + Deallocate( (void *)m_pBuffer ); + + Initialize(); + return 0; + } + + LOGOG_CHAR *pNewBuffer = (LOGOG_CHAR *)Allocate( sizeof( LOGOG_CHAR ) * nSize ); + LOGOG_CHAR *pNewOffset = pNewBuffer; + LOGOG_CHAR *pNewEnd = pNewBuffer + nSize; + + LOGOG_CHAR *pOldOffset = m_pOffset; + + if ( pOldOffset != NULL ) + { + while (( pNewOffset < pNewEnd ) && ( *pOldOffset != (LOGOG_CHAR)NULL )) + *pNewOffset++ = *pOldOffset++; + } + + if (( m_pBuffer != NULL ) && ( m_bIsConst == false )) + Deallocate( m_pBuffer ); + + m_pBuffer = pNewBuffer; + m_pOffset = pNewBuffer; + m_pEndOfBuffer = pNewEnd; + + return ( m_pOffset - m_pBuffer ); + } + + size_t String::reserve_for_int() + { + reserve( 32 ); + return 32; + } + + String::operator const LOGOG_CHAR *() const + { + return m_pBuffer; + } + + const LOGOG_CHAR* String::c_str() const + { + return m_pBuffer; + } + + size_t String::assign( const String &other ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( m_bIsConst ) + cout << "Can't reassign const string!" << endl; +#endif + LOGOG_CHAR *pOther = other.m_pBuffer; + + if ( pOther == NULL ) + return 0; + + size_t othersize = other.size(); + + reserve( othersize + 1 ); + m_pOffset = m_pBuffer; + + for ( unsigned int t = 0; t <= othersize ; t++ ) + *m_pOffset++ = *pOther++; + + return this->size(); + } + + size_t String::assign( const int value ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( m_bIsConst ) + cout << "Can't reassign const string!" << endl; +#endif + + int number = value; + m_pOffset = m_pBuffer; + + int bSign = value; + + if (( bSign = number) < 0) + number = -number; + + do + { + *m_pOffset++ = _LG("0123456789")[ number % 10 ]; + } + while( number /= 10 ); + + if (bSign < 0) + *m_pOffset++ = '-'; + + *m_pOffset = (LOGOG_CHAR)'\0'; + + reverse( m_pBuffer, m_pOffset - 1 ); + + return ( m_pOffset - m_pBuffer ); + } + + size_t String::assign( const LOGOG_CHAR *other, const LOGOG_CHAR *pEnd /*= NULL */ ) + { + size_t len; + + if ( pEnd == NULL ) + len = Length( other ); + else + len = ( pEnd - other ); + /** This constant decides whether assigning a LOGOG_CHAR * to a String will cause the String to use the previous buffer + * in place, or create a new buffer and copy the results. + */ +#ifdef LOGOG_COPY_CONST_CHAR_ARRAY_ON_ASSIGNMENT + reserve( len + 1 ); + + for (unsigned int t = 0; t <= len; t++ ) + *m_pOffset++ = *other++; +#else // LOGOG_COPY_CONST_CHAR_ARRAY_ON_ASSIGNMENT + +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( m_bIsConst ) + cout << "Can't reassign const string!" << endl; +#endif + /* In this case we don't copy the buffer, just reuse it */ + m_pBuffer = const_cast< LOGOG_CHAR *>( other ); + m_pOffset = m_pBuffer + len + 1; + m_pEndOfBuffer = m_pOffset; + m_bIsConst = true; + +#endif // LOGOG_COPY_CONST_CHAR_ARRAY_ON_ASSIGNMENT + + return (int) len; + } + size_t String::append( const String &other ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( m_bIsConst ) + cout << "Can't reassign const string!" << endl; +#endif + + return append( other.m_pBuffer ); + } + + size_t String::append( const LOGOG_CHAR *other ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( m_bIsConst ) + cout << "Can't reassign const string!" << endl; +#endif + if ( other == NULL ) + return 0; + + while (( m_pOffset < m_pEndOfBuffer ) && ( *other != (LOGOG_CHAR)NULL )) + *m_pOffset++ = *other++; + + return ( m_pOffset - m_pBuffer ); + } + + size_t String::append( const LOGOG_CHAR c ) + { + if ( m_pOffset < m_pEndOfBuffer ) + *m_pOffset++ = c; + + return ( m_pOffset - m_pBuffer ); + } + void String::reverse( LOGOG_CHAR* pStart, LOGOG_CHAR* pEnd ) + { + LOGOG_CHAR temp; + + while( pEnd > pStart) + { + temp=*pEnd, *pEnd-- =*pStart, *pStart++=temp; + } + } + + bool String::is_valid() const + { + return ( m_pBuffer != NULL ); + } + + size_t String::find( String &other ) const + { + if ( is_valid() && other.is_valid()) + { + // KMP solution + // String *pThis = const_cast< String *>(this); + // return pThis->KMP( other.m_pBuffer, other.size()); + LOGOG_CHAR *pFound; + +#ifdef LOGOG_UNICODE + pFound = wcsstr( m_pBuffer, other.m_pBuffer ); +#else // LOGOG_UNICODE + pFound = strstr( m_pBuffer, other.m_pBuffer ); +#endif + + if ( pFound != NULL ) + { + return ( pFound - m_pBuffer ); + } + + return npos; + } + + return npos; + } + + void String::format( const LOGOG_CHAR *cFormatString, ... ) + { + va_list args; + + va_start(args, cFormatString); + format_va( cFormatString, args ); + va_end( args ); + } + + void String::format_va( const LOGOG_CHAR *cFormatString, va_list args ) + { + int nActualSize = -1, nAttemptedSize; + LOGOG_CHAR *pszFormatted = NULL; + + Free(); + + /* Estimate length of output; don't pull in strlen() if we can help it */ + int nEstLength = 0; + const LOGOG_CHAR *pCurChar = cFormatString; + while ( *pCurChar++ ) + nEstLength++; + + if ( nEstLength == 0 ) + { + clear(); + return; + } + + /** nAttemptedSize is now a guess at an appropriate size, which is about + ** two times the number of LOGOG_CHARs in the incoming format string. + **/ + nAttemptedSize = nEstLength * 2 * sizeof( LOGOG_CHAR ); + + /* Some *printf implementations, such as msvc's, return -1 on failure. + * Others, such as gcc, return the number + * of characters actually formatted on failure. Deal with either case here. + */ + for ( ; ; ) + { + /** We'll allocate that number of bytes. NOTE that this has less of a chance + ** of working on a Unicode build. + **/ + pszFormatted = (LOGOG_CHAR *)Allocate( nAttemptedSize ); + if ( !pszFormatted ) + { + LOGOG_INTERNAL_FAILURE; + } + + *pszFormatted = (LOGOG_CHAR)'\0'; + + va_list argsCopy; + + /** The va_list structure is not standardized across all platforms; in particular + ** Microsoft seems to have problem with the concept. + **/ +#if defined( va_copy ) + va_copy( argsCopy, args ); +#elif defined( __va_copy ) + __va_copy( argsCopy, args ); +#else + memcpy( &argsCopy, &args, sizeof(va_list) ); +#endif + +#ifdef LOGOG_UNICODE + /** At this point, nSizeInWords will contain the number of words permitted in the + ** output buffer. It takes into account space for appending a null character in the output + ** buffer as well. + **/ + int nSizeInWords = (nAttemptedSize / sizeof( LOGOG_CHAR )); +#endif + /** The nActualSize value receives different things on different platforms. + ** On some platforms it receives -1 on failure; on other platforms + ** it receives the number of LOGOG_CHARs actually formatted (excluding + ** the trailing NULL). + **/ + +#ifdef LOGOG_FLAVOR_WINDOWS +#ifdef LOGOG_UNICODE + nActualSize = _vsnwprintf_s( pszFormatted, nSizeInWords, _TRUNCATE, cFormatString, argsCopy ); +#else // LOGOG_UNICODE + nActualSize = vsnprintf_s( pszFormatted, nAttemptedSize, _TRUNCATE, cFormatString, argsCopy ); +#endif // LOGOG_UNICODE +#else // LOGOG_FLAVOR_WINDOWS +#ifdef LOGOG_UNICODE + nActualSize = vswprintf( pszFormatted, nSizeInWords, cFormatString, argsCopy ); +#else // LOGOG_UNICODE + nActualSize = vsnprintf( pszFormatted, nAttemptedSize, cFormatString, argsCopy ); +#endif // LOGOG_UNICODE +#endif // LOGOG_FLAVOR_WINDOWS + + va_end( argsCopy ); + + /** Convert the number of LOGOG_CHARs actually formatted into bytes. This + ** does NOT include the trailing NULL. + **/ + if ( nActualSize != -1 ) + nActualSize *= sizeof( LOGOG_CHAR ); + + /** When we're doing the compare, we have to keep in mind that the nActualSize + ** does not include a null. We need to verify that the nAttemptedSize can hold all + ** of nActualSize PLUS the size of one null on this platform. A LOGOG_CHAR could + ** be 1, 2, or 4 bytes long. So nAttemptedSize must be greater or equal to nActualSize + ** less the size of one (null) LOGOG_CHAR in bytes. Also, the last + ** allocation may have failed altogether. + ** + **/ + if (( nAttemptedSize >= (nActualSize - (int)sizeof(LOGOG_CHAR))) && ( nActualSize != -1)) + break; + + /** That attempted allocation failed */ + Deallocate( pszFormatted ); + + /** If nActualSize has a positive value, it includes the number of bytes needed to hold + ** the formatted string; we'll add a LOGOG_CHAR size to the end for the next + ** allocation. If nActualSize has no meaningful value, we'll double the previous + ** size and try again. + **/ + if (nActualSize > 0) + { + nAttemptedSize = nActualSize + sizeof( LOGOG_CHAR ); + } + else + { + nAttemptedSize *= 2; + } + + } + + m_bIsConst = false; + assign( pszFormatted ); + /* We just allocated this string, which means it needs to be deallocated + * at shutdown time. The previous function may have changed the const + * setting for this string, which means we may need to change it back here... + * */ + m_bIsConst = false; + } + + void String::Initialize() + { + m_pBuffer = NULL; + m_pOffset = NULL; + m_pEndOfBuffer = NULL; + m_pKMP = NULL; + m_bIsConst = false; + } + + void String::preKmp( size_t m ) + { + ScopedLock sl( GetStringSearchMutex() ); + + size_t i, j; + + if ( m_pBuffer == NULL ) + return; + + if ( m_pKMP == NULL ) + { + m_pKMP = (size_t *)Allocate( sizeof( size_t ) * ( m + 1) ); + } + + i = 0; + j = *m_pKMP = (size_t)-1; + + while (i < m) + { + while (j > (size_t)-1 && m_pBuffer[i] != m_pBuffer[j]) + j = m_pKMP[j]; + i++; + j++; + if (m_pBuffer[i] == m_pBuffer[j]) + m_pKMP[i] = m_pKMP[j]; + else + m_pKMP[i] = j; + } + } + + size_t String::KMP( const LOGOG_CHAR *y, size_t n ) + { + size_t i, j; + + size_t m = size() - 1; // ignore NULL char + + /* Preprocessing */ + if ( m_pKMP == NULL ) + preKmp( m ); + + /* Searching */ + i = j = 0; + while (j < n) + { + while (i > (size_t)-1 && m_pBuffer[i] != y[j]) + i = m_pKMP[i]; + i++; + j++; + if (i >= m) + { + return (j - i); + // We would do this if we cared about multiple substrings + // i = m_pKMP[i]; + } + } + + return npos; + } +} + diff --git a/Base/logog/src/message.cpp b/Base/logog/src/message.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ffc38891a973323ca6e67ffa36564288a317b095 --- /dev/null +++ b/Base/logog/src/message.cpp @@ -0,0 +1,69 @@ +/* + * \file message.cpp + */ + +#include "logog.hpp" + +namespace logog +{ + Message::Message( const LOGOG_LEVEL_TYPE level, + const LOGOG_CHAR *sFileName , + const int nLineNumber, + const LOGOG_CHAR *sGroup, + const LOGOG_CHAR *sCategory, + const LOGOG_CHAR *sMessage, + const double dTimestamp, + bool *pbIsCreated ) : + Checkpoint( level, sFileName, nLineNumber, sGroup, sCategory, sMessage, dTimestamp ) + { + m_pbIsCreated = pbIsCreated; + + if ( pbIsCreated != NULL ) + *pbIsCreated = true; + + /* Messages are always sources, so there's no need to call Initialize() here */ + // Initialize(); + + /* NOTE! The message is typically assigned to a checkpoint AFTER it's been published. + * Ergo a message needs to be unpublished from, and published to, all filters + * iff one of those filters is searching for a substring of that message. + */ + PublishToMultiple( AllFilters() ); + } + + Message::~Message() + { + if ( m_pbIsCreated != NULL ) + *m_pbIsCreated = false; + } + + + bool Message::Republish() + { + UnpublishToMultiple( AllFilters() ); + return PublishToMultiple( AllFilters() ); + } + + Mutex &GetMessageCreationMutex() + { + Statics *pStatic = &Static(); + + if ( pStatic->s_pMessageCreationMutex == NULL ) + pStatic->s_pMessageCreationMutex = new Mutex(); + + return *( pStatic->s_pMessageCreationMutex ); + } + + void DestroyMessageCreationMutex() + { + Statics *pStatic = &Static(); + + if ( pStatic->s_pMessageCreationMutex != NULL ) + { + delete pStatic->s_pMessageCreationMutex; + pStatic->s_pMessageCreationMutex = NULL; + } + } +} + + diff --git a/Base/logog/src/mutex.cpp b/Base/logog/src/mutex.cpp new file mode 100644 index 0000000000000000000000000000000000000000..730a5c42d88e29ca2567db73ab9be70a036e2f40 --- /dev/null +++ b/Base/logog/src/mutex.cpp @@ -0,0 +1,92 @@ + +/* + * \file mutex.cpp + */ + +#include "logog.hpp" + +namespace logog { + + Mutex::Mutex() LOGOG_MUTEX_CTOR( m_Mutex ) + { + LOGOG_MUTEX_INIT(&m_Mutex); + } + + Mutex::Mutex( const Mutex & ) + { + LOGOG_MUTEX_INIT(&m_Mutex); + } + + Mutex & Mutex::operator = (const Mutex &) + { + LOGOG_MUTEX_INIT(&m_Mutex); + return *this; + } + + Mutex::~Mutex() + { + LOGOG_MUTEX_DELETE(&m_Mutex); + } + + void Mutex::MutexLock() + { + LOGOG_MUTEX_LOCK(&m_Mutex); + } + + void Mutex::MutexUnlock() + { + LOGOG_MUTEX_UNLOCK(&m_Mutex); + } + + ScopedLock::ScopedLock( Mutex &mutex ) + { + m_pMutex = &mutex; + m_pMutex->MutexLock(); + } + + ScopedLock::~ScopedLock() + { + m_pMutex->MutexUnlock(); + } + +#ifdef LOGOG_LEAK_DETECTION + Mutex s_AllocationsMutex; + void LockAllocationsMutex() + { + s_AllocationsMutex.MutexLock(); + } + void UnlockAllocationsMutex() + { + s_AllocationsMutex.MutexUnlock(); + } +#endif // LOGOG_LEAK_DETECTION + + Mutex &GetStringSearchMutex() + { + Statics *pStatic = &Static(); + Mutex **ppStringSearchMutex = (Mutex **)&( pStatic->s_pStringSearchMutex ); + +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( pStatic == NULL ) + LOGOG_INTERNAL_FAILURE; +#endif + if ( *ppStringSearchMutex == NULL ) + *ppStringSearchMutex = new Mutex(); + + return *(( Mutex *)( *ppStringSearchMutex )); + } + + void DestroyStringSearchMutex() + { + Statics *pStatic = &Static(); + Mutex **ppStringSearchMutex = (Mutex **)&( pStatic->s_pStringSearchMutex ); + + if ( *ppStringSearchMutex != NULL ) + { + delete *ppStringSearchMutex; + *ppStringSearchMutex = NULL; + } + } + +} + diff --git a/Base/logog/src/node.cpp b/Base/logog/src/node.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2c161609be1bb54884a1f89bf9ca16272ec80d2c --- /dev/null +++ b/Base/logog/src/node.cpp @@ -0,0 +1,351 @@ + +/* + * \file node.cpp + */ + +#include "logog.hpp" + +namespace logog { + + LockableNodesType & LockableNodesType::operator = (const LockableNodesType &other) + { + /* This function is only used at shutdown. */ + LockableNodesType::const_iterator it; + + it = other.begin(); + while ( it != other.end()) + { + this->insert( *it ); + ++it; + } + + return *this; + } + + LockableNodesType &GetStaticNodes( void ** pvLocation ) + { + if ( *pvLocation == NULL ) + *pvLocation = new LockableNodesType(); + + return *(( LockableNodesType *)( *pvLocation )); + + } + + LockableNodesType &AllNodes() + { + return GetStaticNodes( &(Static().s_pAllNodes) ); + } + + LockableNodesType &AllSubscriberNodes() + { + return GetStaticNodes( &(Static().s_pAllSubscriberNodes ) ); + } + + LockableNodesType &AllFilters() + { + return GetStaticNodes( &(Static().s_pAllFilterNodes ) ); + } + + LockableNodesType &AllTargets() + { + return GetStaticNodes( &(Static().s_pAllTargets ) ); + } + + Node::Node() + { + AllNodes().insert( this ); + } + + Node::~Node() + { + Clear(); + AllNodes().erase( this ); + } + + void Node::Initialize() + { + if ( CanSubscribe() ) + { + LockableNodesType *pSubscriberNodes = &AllSubscriberNodes(); + + { + ScopedLock sl( *pSubscriberNodes ); + pSubscriberNodes->insert( this ); + } + + /* This branch is taken iff this node can both subscribe and publish */ + if ( CanPublish() ) + { + LockableNodesType *pFilterNodes = &AllFilters(); + { + ScopedLock sl( *pFilterNodes ); + pFilterNodes->insert( this ); + } + } + } + } + + bool Node::CanPublish() const + { + return true; + } + + bool Node::CanSubscribe() const + { + return true; + } + + bool Node::CanSubscribeTo( const Node & ) + { + return CanSubscribe(); + } + + bool Node::IsTopic() const + { + return false; + } + + bool Node::PublishTo( Node &subscriber ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( &subscriber == this ) + LOGOG_INTERNAL_FAILURE; +#endif + bool bWasInserted; + + { + ScopedLock sl( m_Subscribers ); + bWasInserted = ( m_Subscribers.insert( &subscriber ) ).second; + } + + if ( bWasInserted ) + subscriber.SubscribeTo( *this ); + + return bWasInserted; + } + + bool Node::PublishToMultiple( LockableNodesType &nodes ) + { + LockableNodesType::iterator it; + + bool bWasPublished = false; + + nodes.MutexLock(); + it = nodes.begin(); + + while ( it != nodes.end() ) + { + if ( PublishTo( **it ) == true ) + bWasPublished = true; + + it++; + } + + nodes.MutexUnlock(); + + return bWasPublished; + } + + bool Node::UnpublishTo( Node &subscriber ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( &subscriber == this ) + LOGOG_INTERNAL_FAILURE; +#endif + bool bWasRemoved = false; + + { + ScopedLock sl( m_Subscribers ); + NodesType::iterator it; + + if ( ( it = m_Subscribers.find( &subscriber) ) != m_Subscribers.end() ) + { + bWasRemoved = true; + m_Subscribers.erase( it ); + } + } + + if ( bWasRemoved ) + subscriber.UnsubscribeTo( *this ); + + return bWasRemoved; + } + + bool Node::UnpublishToMultiple( LockableNodesType &nodes ) + { + LockableNodesType::iterator it; + + bool bWasUnpublished = false; + + nodes.MutexLock(); + it = nodes.begin(); + + while ( it != nodes.end() ) + { + if ( UnpublishTo( **it ) == true ) + bWasUnpublished = true; + + it++; + } + + nodes.MutexUnlock(); + + return bWasUnpublished; + } + + bool Node::SubscribeTo( Node &publisher ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( &publisher == this ) + LOGOG_INTERNAL_FAILURE; +#endif + bool bWasInserted; + + { + ScopedLock sl( m_Publishers ); + bWasInserted = ( m_Publishers.insert( &publisher ) ).second; + } + + if ( bWasInserted ) + publisher.PublishTo( *this ); + + return bWasInserted; + } + + bool Node::SubscribeToMultiple( LockableNodesType &nodes ) + { + LockableNodesType::iterator it; + + bool bWasSubscribed = false; + + nodes.MutexLock(); + it = nodes.begin(); + + while ( it != nodes.end() ) + { + if ( SubscribeTo( **it ) == true ) + bWasSubscribed = true; + + it++; + } + + nodes.MutexUnlock(); + + return bWasSubscribed; + } + + bool Node::UnsubscribeTo( Node &publisher ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( &publisher == this ) + LOGOG_INTERNAL_FAILURE; +#endif + bool bWasRemoved = false; + + { + ScopedLock sl( m_Publishers ); + NodesType::iterator it; + + if ( ( it = m_Publishers.find( &publisher ) ) != m_Publishers.end() ) + { + bWasRemoved = true; + m_Publishers.erase( it ); + } + } + + if ( bWasRemoved ) + publisher.UnpublishTo( *this ); + + return bWasRemoved; + } + + bool Node::UnsubscribeToMultiple( LockableNodesType &nodes ) + { + LockableNodesType::iterator it; + + bool bWasUnsubscribed = false; + + nodes.MutexLock(); + it = nodes.begin(); + + while ( it != nodes.end() ) + { + if ( UnsubscribeTo( **it ) == true ) + bWasUnsubscribed = true; + + it++; + } + + nodes.MutexUnlock(); + + return bWasUnsubscribed; + } + + void Node::Clear() + { + { + ScopedLock sl( m_Publishers ); + m_Publishers.clear(); + } + { + ScopedLock sl( m_Subscribers ); + m_Publishers.clear(); + } + } + + + void DestroyNodesList( void **pvList ) + { + LockableNodesType **ppNodesList = (LockableNodesType **)pvList; + + if ( *ppNodesList == NULL ) + return; + + (*ppNodesList)->clear(); + delete *ppNodesList; + *ppNodesList = NULL; + } + + void DestroyAllNodes() + { + Statics *pStatics = &Static(); + + LockableNodesType *pAllNodes = ( LockableNodesType *)pStatics->s_pAllNodes; + + if ( pAllNodes == NULL ) + return; + + /** Destroy all the node groups, but don't destroy their contents -- we'll do that as the next step. */ + DestroyNodesList( &(pStatics->s_pAllSubscriberNodes )); + DestroyNodesList( &(pStatics->s_pAllFilterNodes )); + DestroyNodesList( &(pStatics->s_pAllTargets )); + + /* We have to copy the AllNodes because destroying each node will remove it from AllNodes. Fortunately + * this only happens at shutdown, so we don't have to worry about efficiency. + */ + LockableNodesType nodes = *pAllNodes; + + LockableNodesType::iterator it; + + it = nodes.begin(); + + while ( it != nodes.end() ) + { + delete *it; + it++; + } + + nodes.clear(); + + #ifdef LOGOG_INTERNAL_DEBUGGING + if ( pAllNodes->size() != 0 ) + cout << "Not all nodes were deleted at shutdown -- memory leak may have occurred" << endl; + #endif + + pAllNodes->clear(); // just in case + delete pAllNodes; + pStatics->s_pAllNodes = NULL; + } + +} + diff --git a/Base/logog/src/platform.cpp b/Base/logog/src/platform.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d191d10560bce86a951af98f73a881d67a57447f --- /dev/null +++ b/Base/logog/src/platform.cpp @@ -0,0 +1,11 @@ + /* + * \file platform.cpp + */ + +#include "logog.hpp" + +namespace logog { + + bool sb_AvoidLinkError4221_platform_cpp = false; +} + diff --git a/Base/logog/src/socket.cpp b/Base/logog/src/socket.cpp new file mode 100644 index 0000000000000000000000000000000000000000..533de0b7d8ddf86be880705cd30af791db5d0817 --- /dev/null +++ b/Base/logog/src/socket.cpp @@ -0,0 +1,10 @@ +/* + * \file socket.cpp + */ + +#include "logog.hpp" + +namespace logog { + bool sb_AvoidLinkError4221_socket_cpp = false; +} + diff --git a/Base/logog/src/statics.cpp b/Base/logog/src/statics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..05ec6bdcfdd88a922f7e2791f28e60d7852af7ca --- /dev/null +++ b/Base/logog/src/statics.cpp @@ -0,0 +1,57 @@ + /* + * \file statics.cpp + */ + +#include "logog.hpp" + +namespace logog { + + Statics::Statics() + { + s_pAllNodes = NULL; + s_pAllSubscriberNodes = NULL; + s_pAllFilterNodes = NULL; + s_pAllTargets = NULL; + s_pTimer = NULL; + s_pDefaultFormatter = NULL; + s_pDefaultFilter = NULL; + s_pStringSearchMutex = NULL; + s_pMessageCreationMutex = NULL; + s_pfMalloc = NULL; + s_pfFree = NULL; + s_pSelf = this; + s_nSockets = 0; + } + + void Statics::Reset() + { + DestroyGlobalTimer(); + DestroyDefaultFormatter(); + s_pDefaultFilter = NULL; // This will be destroyed on the next step + DestroyAllNodes(); + DestroyStringSearchMutex(); + DestroyMessageCreationMutex(); + s_pfMalloc = NULL; + s_pfFree = NULL; + s_nSockets = 0; + } + + Statics::~Statics() + { + Reset(); + } + + Statics s_Statics; + + Statics &Static() + { + return s_Statics; + } + + void DestroyStatic() + { + s_Statics.~Statics(); + } + +} + diff --git a/Base/logog/src/target.cpp b/Base/logog/src/target.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7bc26943a0c5f7d3079e76418024b48b567d0e4b --- /dev/null +++ b/Base/logog/src/target.cpp @@ -0,0 +1,342 @@ + /* + * \file target.cpp + */ + +#include "logog.hpp" + +#include <iostream> + +namespace logog { + + Target::Target() : + m_bNullTerminatesStrings( true ) + { + SetFormatter( GetDefaultFormatter() ); + LockableNodesType *pAllTargets = &AllTargets(); + + { + ScopedLock sl( *pAllTargets ); + pAllTargets->insert( this ); + } + + SubscribeToMultiple( AllFilters() ); + } + + + Target::~Target() + { + LockableNodesType *pAllTargets = &AllTargets(); + + UnsubscribeToMultiple( AllFilters() ); + + { + ScopedLock sl( *pAllTargets ); + pAllTargets->erase( this ); + } + } + + void Target::SetFormatter( Formatter &formatter ) + { + m_pFormatter = &formatter; + } + + Formatter & Target::GetFormatter() const + { + return *m_pFormatter; + } + + int Target::Receive( const Topic &topic ) + { + ScopedLock sl( m_MutexReceive ); + return Output( m_pFormatter->Format( topic, *this ) ); + } + + int Cerr::Output( const LOGOG_STRING &data ) + { + LOGOG_CERR << (const LOGOG_CHAR *)data; + + return 0; + } +//! [Cout] + int Cout::Output( const LOGOG_STRING &data ) + { + LOGOG_COUT << (const LOGOG_CHAR *)data; + + return 0; + } +//! [Cout] + + int OutputDebug::Output( const LOGOG_STRING &data ) + { +#ifdef LOGOG_FLAVOR_WINDOWS +#ifdef LOGOG_UNICODE + OutputDebugStringW( (const LOGOG_CHAR *)data ); +#else + OutputDebugStringA( (const LOGOG_CHAR *)data ); +#endif // LOGOG_UNICODE +#endif // LOGOG_FLAVOR_WINDOWS + return 0; + } + + LogFile::LogFile(const char *sFileName) : + m_bFirstTime( true ), + m_bOpenFailed( false ), + m_pFile( NULL ) + { + m_bNullTerminatesStrings = false; + +#ifdef LOGOG_UNICODE + m_bWriteUnicodeBOM = true; +#else // LOGOG_UNICODE + m_bWriteUnicodeBOM = false; +#endif // LOGOG_UNICODE + + int nNameLength = 0; + + const char *sNameCount = sFileName; + while ( *sNameCount++ != '\0' ) + nNameLength++; + + // add one for trailing null + nNameLength++; + + m_pFileName = (char *)Object::Allocate( nNameLength ); + + char *m_pOut = m_pFileName; + while ( ( *m_pOut++ = *sFileName++) != '\0' ) + ; + } + + LogFile::~LogFile() + { + if ( m_pFile ) + fclose( m_pFile ); + + Object::Deallocate( m_pFileName ); + } + + int LogFile::Open() + { + int nError = 1; // preset this in case LOGOG_FLAVOR_WINDOWS is not defined + + bool bFileAlreadyExists = false; + FILE *fpTest; + +#ifdef LOGOG_FLAVOR_WINDOWS + nError = fopen_s( &fpTest, m_pFileName, "r"); // ignore the error code +#else // LOGOG_FLAVOR_WINDOWS + fpTest = fopen( m_pFileName, "r"); +#endif // LOGOG_FLAVOR_WINDOWS + + if ( fpTest != NULL ) + { + fclose( fpTest ); + bFileAlreadyExists = true; + } + + /** Windows tries to be clever and help us with converting line feeds + ** to carriage returns when writing a text file. This causes problems + ** when writing a Unicode file as Windows helpfully inserts a single-byte + ** 0x0D between the return and line feed on write. So we open and operate + ** the output in binary mode only. + **/ +#ifdef LOGOG_FLAVOR_WINDOWS +#ifdef LOGOG_UNICODE + nError = fopen_s( &m_pFile, m_pFileName, "ab, ccs=UNICODE" ); +#else // LOGOG_UNICODE + nError = fopen_s( &m_pFile, m_pFileName, "ab" ); +#endif // LOGOG_UNICODE + if ( nError != 0 ) + return nError; +#else // LOGOG_FLAVOR_WINDOWS + m_pFile = fopen( m_pFileName, "ab+" ); +#endif // LOGOG_FLAVOR_WINDOWS + + if ( m_pFile == NULL ) + m_bOpenFailed = true; // and no further outputs will work + else + { +#ifdef LOGOG_UNICODE + if ( !bFileAlreadyExists ) + { + WriteUnicodeBOM(); + } +#endif + } + + return ( m_pFile ? 0 : -1 ); + } + + int LogFile::Output( const LOGOG_STRING &data ) + { + if ( m_bOpenFailed ) + return -1; + + int result = 0; + if ( m_bFirstTime ) + { + result = Open(); + if ( result != 0 ) + return result; + + m_bFirstTime = false; + } + + return InternalOutput( data.size(), data.c_str()); + } + + int LogFile::InternalOutput( size_t nSize, const LOGOG_CHAR *pData ) + { + size_t result; + + result = fwrite( pData, sizeof( LOGOG_CHAR ), nSize, m_pFile ); + + if ( (size_t)result != nSize ) + return -1; + + return 0; + } + + void LogFile::WriteUnicodeBOM() + { + static union { + int i; + char c[4]; + } bDetectEndian = {0x01020304}; + + bool bIsLittleEndian = ( bDetectEndian.c[0] != 1 ); + + switch ( sizeof( LOGOG_CHAR )) + { + case 1: + // This could be a UTF-8 BOM but technically very few systems support + // sizeof( wchar_t ) == sizeof( char ). So for now we're not going + // to write a BOM in these cases. + break; + + case 2: + if ( bIsLittleEndian ) + InternalOutput( 1, (const LOGOG_CHAR *)"\xFF\xFE" ); // little endian UTF-16LE + else + InternalOutput( 1, (const LOGOG_CHAR *)"\xFE\xFF" ); // big endian UTF-16BE + + break; + + case 4: + if ( bIsLittleEndian ) + InternalOutput( 1, (const LOGOG_CHAR *)"\xFF\xFE\x00\x00" ); // little endian UTF-32LE + else + InternalOutput( 1, (const LOGOG_CHAR *)"\x00\x00\xFE\xFF" ); // big endian UTF-32BE + + break; + + default: + // No idea what that character size is; do nothing + break; + } + } + + LogBuffer::LogBuffer( Target *pTarget , + size_t s ) : + m_pStart( NULL ), + m_nSize( 0 ) + { + m_pOutputTarget = pTarget; + Allocate( s ); + } + + LogBuffer::~LogBuffer() + { + Dump(); + Deallocate(); + } + + void LogBuffer::SetTarget( Target &t ) + { + m_pOutputTarget = &t; + } + + int LogBuffer::Insert( const LOGOG_CHAR *pChars, size_t size ) + { + if (( m_pCurrent + size ) >= m_pEnd ) + Dump(); + + if ( size > (size_t)( m_pEnd - m_pStart )) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + cerr << "Cannot insert string into buffer; string is larger than buffer. Allocate a larger size for the LogBuffer." << endl; +#endif + return -1; // can't fit this into buffer; punt it + } + + // Store the size of this string in the buffer + size_t *pSize; + pSize = ( size_t *)m_pCurrent; + *pSize = size; + m_pCurrent = (LOGOG_CHAR *)++pSize; + + while ( size-- ) + *m_pCurrent++ = *pChars++; + + return 0; + } + + int LogBuffer::Dump() + { + LOGOG_CHAR *pCurrent = m_pStart; + size_t *pSize; + int nError; + + if ( m_pOutputTarget == NULL ) + return -1; + + // We have to lock the output target here, as we do an end run around its Receive() function */ + ScopedLock sl( m_pOutputTarget->m_MutexReceive ); + + while ( pCurrent < m_pCurrent ) + { + String sOut; + // Get the size of this entry + pSize = ( size_t * )pCurrent; + // Move past that entry into the data area + pCurrent = ( LOGOG_CHAR * )( pSize + 1 ); + + sOut.assign( pCurrent, pCurrent + *pSize - 1 ); + + if ( m_pOutputTarget ) + { + nError = m_pOutputTarget->Output( sOut ); + if ( nError != 0 ) + return nError; + } + + pCurrent += *pSize; + } + + // reset buffer + m_pCurrent = m_pStart; + + return 0; + } + + int LogBuffer::Output( const LOGOG_STRING &data ) + { + return Insert( &(*data), data.size() ); + } + + void LogBuffer::Allocate( size_t size ) + { + m_nSize = size; + m_pCurrent = m_pStart = (LOGOG_CHAR *)Object::Allocate( size * sizeof( LOGOG_CHAR )); + m_pEnd = m_pStart + size; + } + + void LogBuffer::Deallocate() + { + if ( m_pStart ) + Object::Deallocate( m_pStart ); + + m_nSize = 0; + } +} + diff --git a/Base/logog/src/timer.cpp b/Base/logog/src/timer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..accaacac1d6b128e9cccf70948f423777fc1f2d8 --- /dev/null +++ b/Base/logog/src/timer.cpp @@ -0,0 +1,80 @@ + +/* + * \file timer.cpp + */ + +#include "logog.hpp" + +namespace logog { + + Timer::Timer() + { + m_fStartTime = 0.0f; + +#ifdef LOGOG_FLAVOR_WINDOWS + LARGE_INTEGER TicksPerSecond; + QueryPerformanceFrequency( &TicksPerSecond ); + m_fTicksPerMicrosecond = (DOUBLE)TicksPerSecond.QuadPart * 0.000001; +#endif + Set( 0.0f ); + } + +//! [TimerGet] + logog::LOGOG_TIME Timer::Get() + { +#ifdef LOGOG_FLAVOR_WINDOWS + LARGE_INTEGER liTime; + QueryPerformanceCounter( &liTime ); + + double dusec; + dusec =( liTime.QuadPart / m_fTicksPerMicrosecond ); + + return ( dusec / 1000000.0f ) - m_fStartTime; +#endif + +#ifdef LOGOG_FLAVOR_POSIX +#ifdef LOGOG_TARGET_PS3 + LOGOG_PS3_GET_TIME; +#else // LOGOG_TARGET_PS3 + // General Posix implementation + timeval tv; + gettimeofday( &tv, 0 ); + return ((double) (tv.tv_sec) + ((double)(tv.tv_usec ) / 1000000.0 ) - m_fStartTime); +#endif // LOGOG_TARGET_PS3 +#endif + } +//! [TimerGet] + + void Timer::Set( LOGOG_TIME time ) + { + m_fStartTime = time + Get(); + } + + Timer &GetGlobalTimer() + { + Statics *pStatic = &Static(); + +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( pStatic == NULL ) + LOGOG_INTERNAL_FAILURE; +#endif + + if ( pStatic->s_pTimer == NULL ) + pStatic->s_pTimer = new Timer(); + + return *(pStatic->s_pTimer ); + } + + void DestroyGlobalTimer() + { + Statics *pStatic = &Static(); + Timer *pGlobalTimer = pStatic->s_pTimer; + + if ( pGlobalTimer != NULL ) + delete pGlobalTimer; + + pStatic->s_pTimer = NULL; + } + +} + diff --git a/Base/logog/src/topic.cpp b/Base/logog/src/topic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..619aa305c745b1cbc4306e41491a65934c465283 --- /dev/null +++ b/Base/logog/src/topic.cpp @@ -0,0 +1,454 @@ + +/* + * \file topic.cpp + */ + +#include "logog.hpp" + +namespace logog { + + void SetDefaultLevel( LOGOG_LEVEL_TYPE level ) + { + Filter *pDefaultFilter = &GetDefaultFilter(); + + pDefaultFilter->Level( level ); + } + + Topic::Topic( const LOGOG_LEVEL_TYPE level , + const LOGOG_CHAR *sFileName , + const int nLineNumber , + const LOGOG_CHAR *sGroup , + const LOGOG_CHAR *sCategory , + const LOGOG_CHAR *sMessage , + const double dTimestamp ) + { + m_TopicFlags = 0; + + if ( sFileName != NULL ) + { + m_vStringProps[ TOPIC_FILE_NAME ] = sFileName; + m_TopicFlags |= TOPIC_FILE_NAME_FLAG; + } + + if ( sGroup != NULL ) + { + m_vStringProps[ TOPIC_GROUP ] = sGroup; + m_TopicFlags |= TOPIC_GROUP_FLAG; + } + + if ( sCategory != NULL ) + { + m_vStringProps[ TOPIC_CATEGORY ] = sCategory; + m_TopicFlags |= TOPIC_CATEGORY_FLAG; + } + + if ( sMessage != NULL ) + { + m_vStringProps[ TOPIC_MESSAGE ] = sMessage; + m_TopicFlags |= TOPIC_MESSAGE_FLAG; + } + + m_vIntProps[ TOPIC_LEVEL ] = level; + + if ( level != LOGOG_LEVEL_ALL ) + { + m_TopicFlags |= TOPIC_LEVEL_FLAG; + } + + m_vIntProps[ TOPIC_LINE_NUMBER ] = nLineNumber; + + if ( nLineNumber != 0 ) + { + m_TopicFlags |= TOPIC_LINE_NUMBER_FLAG; + } + + m_tTime = dTimestamp; + + if ( dTimestamp != 0.0f ) //-V550 + m_TopicFlags |= TOPIC_TIMESTAMP_FLAG; + } + + bool Topic::IsTopic() const + { + return true; + } + + int Topic::Send( const Topic &node ) + { + LockableNodesType::iterator it; + + { + ScopedLock sl( m_Subscribers ); + it = m_Subscribers.begin(); + } + + /* Iterate over the subscribers, but only addressing the subscribers group while locking it */ + Topic *pCurrentTopic; + Node *pCurrentNode; + m_Subscribers.MutexLock(); + int nError = 0; + + while ( it != m_Subscribers.end() ) + { + pCurrentNode = *it; + + if ( pCurrentNode->IsTopic() == false ) + continue; + + pCurrentTopic = ( Topic * )pCurrentNode; + + if ( pCurrentTopic ) + nError += pCurrentTopic->Receive( node ); + + it++; + } + + m_Subscribers.MutexUnlock(); + + return nError; + } + + int Topic::Transmit() + { + return Send( *this ); + } + + int Topic::Receive( const Topic &node ) + { + /* Default implementation -- send it on to all children */ + return Send( node ); + } + + bool Topic::CanSubscribeTo( const Node &otherNode ) + { + if ( CanSubscribe() == false ) + return false; + + if ( otherNode.IsTopic() == false ) + return false; + + Topic *pTopic = ( Topic * )&otherNode; + + /* This function will change from topic class to topic class. */ + return CanSubscribeCheckTopic( *pTopic ); + } + + bool Topic::CanSubscribeCheckTopic( const Topic &other ) + { + /* This is the generic comparison case. We'll want to optimize this function for other types + * of topics. + */ + + /* Check topics in likely order of disinterest */ + if ( m_TopicFlags & TOPIC_LEVEL_FLAG ) + { + /* Topic levels are less interesting the larger the numbers are. */ + if ( other.m_vIntProps[ TOPIC_LEVEL ] > m_vIntProps[ TOPIC_LEVEL ] ) + return false; + } + + if ( m_TopicFlags & TOPIC_GROUP_FLAG ) + { + /* If our topic is not a substring of the publisher's topic, ignore this */ + if (( other.m_vStringProps[ TOPIC_GROUP ] ).find( m_vStringProps[ TOPIC_GROUP ] ) == LOGOG_STRING::npos ) + return false; + } + + if ( m_TopicFlags & TOPIC_CATEGORY_FLAG ) + { + /* If our topic is not a substring of the publisher's topic, ignore this */ + if (( other.m_vStringProps[ TOPIC_CATEGORY ] ).find( m_vStringProps[ TOPIC_CATEGORY ] ) == LOGOG_STRING::npos ) + return false; + } + + if ( m_TopicFlags & TOPIC_FILE_NAME_FLAG ) + { + /* If our topic is not a substring of the publisher's file name, ignore this. */ + if (( other.m_vStringProps[ TOPIC_FILE_NAME ] ).find( m_vStringProps[ TOPIC_FILE_NAME ] ) == LOGOG_STRING::npos ) + return false; + } + + if ( m_TopicFlags & TOPIC_LINE_NUMBER_FLAG ) + { + /* If our line number doesn't equal theirs, ignore this */ + if ( other.m_vIntProps[ TOPIC_LINE_NUMBER ] != m_vIntProps[ TOPIC_LINE_NUMBER ] ) + return false; + } + + if ( m_TopicFlags & TOPIC_MESSAGE_FLAG ) + { + /* If our topic is not a substring of the publisher's file name, ignore this. */ + if (( other.m_vStringProps[ TOPIC_MESSAGE ] ).find( m_vStringProps[ TOPIC_MESSAGE ] ) == LOGOG_STRING::npos ) + return false; + } + + if ( m_TopicFlags & TOPIC_TIMESTAMP_FLAG ) + { + /* Timestamps are only interesting if they're greater than or equal to ours. */ + if ( other.m_tTime < m_tTime ) + return false; + } + + /* all tests passed */ + return true; + } + + bool Topic::PublishTo( Node &subscriber ) + { +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( &subscriber == this ) + LOGOG_INTERNAL_FAILURE; +#endif + bool bWasInserted; + + /** Additional checking may be required first -- can the subscriber handle this publishing? */ + if ( subscriber.IsTopic() ) + { + Topic *pSubscriber = (Topic *)&subscriber; + + if ( pSubscriber->CanSubscribeTo( *this ) == false ) + return false; + } + + { + ScopedLock sl( m_Subscribers ); + bWasInserted = ( m_Subscribers.insert( &subscriber ) ).second; + } + + if ( bWasInserted ) + subscriber.SubscribeTo( *this ); + + return bWasInserted; + } + + void Topic::Format( const LOGOG_CHAR *cFormatMessage, ... ) + { + va_list args; + + va_start( args, cFormatMessage ); + m_vStringProps[ TOPIC_MESSAGE ].format_va( cFormatMessage, args ); + va_end( args ); + + m_TopicFlags |= TOPIC_MESSAGE_FLAG; + } + + const LOGOG_STRING & Topic::FileName() const + { + return m_vStringProps[ TOPIC_FILE_NAME ]; + } + + void Topic::FileName( const LOGOG_STRING &s ) + { + m_vStringProps[ TOPIC_FILE_NAME ] = s; + m_TopicFlags |= TOPIC_FILE_NAME_FLAG; + } + + const LOGOG_STRING & Topic::Message() const + { + return m_vStringProps[ TOPIC_MESSAGE ]; + } + + void Topic::Message( const LOGOG_STRING &s ) + { + m_vStringProps[ TOPIC_MESSAGE ] = s; + m_TopicFlags |= TOPIC_MESSAGE_FLAG; + } + + const LOGOG_STRING & Topic::Category() const + { + return m_vStringProps[ TOPIC_CATEGORY ]; + } + + void Topic::Category( const LOGOG_STRING &s ) + { + m_vStringProps[ TOPIC_CATEGORY ] = s; + m_TopicFlags |= TOPIC_CATEGORY_FLAG; + } + + const LOGOG_STRING & Topic::Group() const + { + return m_vStringProps[ TOPIC_GROUP ]; + } + + void Topic::Group( const LOGOG_STRING &s ) + { + m_vStringProps[ TOPIC_GROUP ] = s; + m_TopicFlags |= TOPIC_GROUP_FLAG; + } + + int Topic::LineNumber() const + { + return m_vIntProps[ TOPIC_LINE_NUMBER ]; + } + + void Topic::LineNumber( const int num ) + { + m_vIntProps[ TOPIC_LINE_NUMBER ] = num; + m_TopicFlags |= TOPIC_LINE_NUMBER_FLAG; + } + + LOGOG_LEVEL_TYPE Topic::Level() const + { + return ( LOGOG_LEVEL_TYPE )m_vIntProps[ TOPIC_LEVEL ]; + } + + void Topic::Level( LOGOG_LEVEL_TYPE level ) + { + m_vIntProps[ TOPIC_LEVEL ] = level; + m_TopicFlags |= TOPIC_LEVEL_FLAG; + } + + logog::LOGOG_TIME Topic::Timestamp() const + { + return m_tTime; + } + + void Topic::Timestamp( const LOGOG_TIME t ) + { + m_tTime = t; + m_TopicFlags |= TOPIC_TIMESTAMP_FLAG; + } + + TOPIC_FLAGS Topic::GetTopicFlags() const + { + return m_TopicFlags; + } + + +/********************************************************/ + + Filter::Filter( const LOGOG_LEVEL_TYPE level , + const LOGOG_CHAR *sFileName , + const int nLineNumber , + const LOGOG_CHAR *sGroup , + const LOGOG_CHAR *sCategory , + const LOGOG_CHAR *sMessage , + const double dTimestamp ) : + Topic( level, sFileName, nLineNumber, sGroup, sCategory, sMessage, dTimestamp ) + { + Statics *pStatic = &Static(); + +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( pStatic == NULL ) + LOGOG_INTERNAL_FAILURE; +#endif + + if ( pStatic->s_pDefaultFilter == NULL ) + pStatic->s_pDefaultFilter = this; + + PublishToMultiple( AllTargets() ); + + LockableNodesType *pFilterNodes = &AllFilters(); + + { + ScopedLock sl( *pFilterNodes ); + pFilterNodes->insert( this ); + } + } + + Filter &GetDefaultFilter() + { + Statics *pStatic = &Static(); + +#ifdef LOGOG_INTERNAL_DEBUGGING + if ( pStatic == NULL ) + LOGOG_INTERNAL_FAILURE; +#endif + + if ( pStatic->s_pDefaultFilter == NULL ) + { + pStatic->s_pDefaultFilter = new Filter( LOGOG_LEVEL ); + } + + return *((Filter *)(pStatic->s_pDefaultFilter)); + } + + TopicGroup::TopicGroup( const LOGOG_CHAR *sGroup ) : + Topic( LOGOG_LEVEL_ALL, NULL, 0, sGroup ) + { + } + + bool TopicGroup::CanSubscribeCheckTopic( const Topic &other ) + { + if ( m_TopicFlags & TOPIC_LEVEL_FLAG ) + { + /* Topic levels are less interesting the larger the numbers are. */ + if ( other.m_vIntProps[ TOPIC_LEVEL ] > m_vIntProps[ TOPIC_LEVEL ] ) + return false; + } + + return true; + } + + TopicLevel::TopicLevel( const LOGOG_LEVEL_TYPE level ) : + Topic( level ) + { + } + + + bool TopicLevel::CanSubscribeCheckTopic( const Topic &other ) + { + /* Check topics in likely order of disinterest */ + if ( m_TopicFlags & TOPIC_LEVEL_FLAG ) + { + /* Topic levels are less interesting the larger the numbers are. */ + if ( other.m_vIntProps[ TOPIC_LEVEL ] > m_vIntProps[ TOPIC_LEVEL ] ) + return false; + } + + /* all tests passed */ + return true; + } + + TopicSource::TopicSource( const LOGOG_LEVEL_TYPE level , + const LOGOG_CHAR *sFileName, + const int nLineNumber, + const LOGOG_CHAR *sGroup, + const LOGOG_CHAR *sCategory, + const LOGOG_CHAR *sMessage, + const double dTimestamp ) : + Topic( level, sFileName, nLineNumber, sGroup, sCategory, sMessage, dTimestamp ) + { + } + + bool TopicSource::SubscribeTo( Node & ) + { + return false; + } + + bool TopicSource::UnsubscribeTo( Node & ) + { + return false; + } + + bool TopicSource::CanSubscribe() const + { + return false; + } + + bool TopicSink::IsTopic() const + { + return true; + } + + void TopicSink::Initialize() + { + + } + + bool TopicSink::PublishTo( Node & ) + { + return false; + } + + bool TopicSink::UnpublishTo( Node & ) + { + return false; + } + + bool TopicSink::CanPublish() const + { + return false; + } +} + diff --git a/Base/logog/src/unittest.cpp b/Base/logog/src/unittest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c5863cff60de2194eb7db8ca0b63bf343f1b7d4e --- /dev/null +++ b/Base/logog/src/unittest.cpp @@ -0,0 +1,128 @@ + +/* + * \file unittest.cpp + */ + +#define LOGOG_UNIT_TESTING 1 + +#include "logog.hpp" + +namespace logog +{ + +TestRegistryType &LogogTestRegistry() +{ + static TestRegistryType *pRegistry = new TestRegistryType(); + return *pRegistry; +} + +TestSignup::TestSignup( UnitTest *pTest ) +{ + m_pTest = pTest; + LogogTestRegistry().push_back( pTest ); +} + +UnitTest::UnitTest( const TestNameType &sTestName ) +{ + m_sTestName = sTestName; + m_pTestSignup = new TestSignup( this ); +} + +UnitTest::~UnitTest() +{ + FreeInternals(); +} + +void UnitTest::FreeInternals() +{ + if ( m_pTestSignup ) + delete m_pTestSignup; + + m_pTestSignup = NULL; +} + +/** Returns the name of this UnitTest provided at construction time. */ +TestNameType &UnitTest::GetName() +{ + return m_sTestName; +} + +/** Executes all currently registered tests and prints a report of success or failure. */ +int RunAllTests() +{ + using namespace std; + + int nTests = 0, nTestsSucceeded = 0; + int nTestResult; + int nFailures = 0; + +#ifdef LOGOG_UNICODE + wostream *pOut; +#else // LOGOG_UNICODE + ostream *pOut; +#endif // LOGOG_UNICODE + + pOut = &(LOGOG_COUT); + + nTests = (int) LogogTestRegistry().size(); + + if ( nTests == 0 ) + { + *pOut << _LG("No tests currently defined.") << endl; + return 1; + } + + for ( TestRegistryType::iterator it = LogogTestRegistry().begin(); + it != LogogTestRegistry().end(); + ++it ) + { + (*pOut) << _LG("Test ") << (*it)->GetName() << _LG(" running... ") << endl; + nTestResult = (*it)->RunTest(); + + (*pOut) << _LG("Test ") << (*it)->GetName(); + + if ( nTestResult == 0 ) + { + *pOut << _LG(" successful.") << endl; + nTestsSucceeded++; + } + else + { + *pOut << _LG(" failed!") << endl; + nFailures++; + } + + /* Validate that no allocations are currently outstanding. Make sure to handle the case + * where leak detection is disabled */ + int nMemoryTestResult = ReportMemoryAllocations(); + + if ( nMemoryTestResult != -1 ) + { + (*pOut) << _LG("Test ") << (*it)->GetName() << _LG(" has ") << nMemoryTestResult << + _LG(" memory allocations outstanding at end of test.") << endl; + nFailures += nMemoryTestResult; + } + } + + *pOut << _LG("Testing complete, ") + << nTests << _LG(" total tests, ") + << nTestsSucceeded << _LG(" tests succeeded, ") + << ( nTests - nTestsSucceeded ) << _LG(" failed") + << endl; + + return nFailures; +} + +/** Should remove all memory allocated during unit testing. */ +void ShutdownTests() +{ + TestRegistryType::iterator it; + + for ( it = LogogTestRegistry().begin(); it != LogogTestRegistry().end(); it++ ) + (*it)->FreeInternals(); + + delete &LogogTestRegistry(); +} + +} + diff --git a/Base/logog/test/CMakeLists.txt b/Base/logog/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f818a2e4fb04e7a99e7909fd43aa88df3890225f --- /dev/null +++ b/Base/logog/test/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable( test test.cpp ) + diff --git a/Base/logog/test/test.cpp b/Base/logog/test/test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bb34ebeb2271ae16cc8ba365bc6078ac1cf33a8 --- /dev/null +++ b/Base/logog/test/test.cpp @@ -0,0 +1,837 @@ +/** Only define this macro in source files that create unit tests. This setting brings in the std namespace, so its + ** use is not recommended outside of unit tests. */ +#define LOGOG_UNIT_TESTING 1 + +/** Change this to higher constants to exponentially increase test difficulty. */ +const int TEST_STRESS_LEVEL = 1; + +#include "logog.hpp" + +#include <cstdio> + +#include <fcntl.h> + +#ifdef LOGOG_FLAVOR_WINDOWS +#include <io.h> +#endif + +using namespace logog; +using namespace std; + +UNITTEST( SimpleLocking ) +{ +//! [SimpleLocking] + LOGOG_INITIALIZE(); + + { + logog::Mutex m; + + { + logog::ScopedLock s1( m ); + LOGOG_COUT << _LG("Lock is on") << endl; + } + + logog::Mutex *pM = new logog::Mutex(); + + { + logog::ScopedLock s1( *pM ); + LOGOG_COUT << _LG("Lock is on") << endl; + } + + delete pM; + + LOGOG_COUT << _LG("Lock unlocked") << endl; + + logog::Message m1; + + } + + LOGOG_SHUTDOWN(); + + return 0; +//! [SimpleLocking] +} + +int _s_ThreadLockingTest = 0; + +void LockingThread( void *pvMutex ) +{ + const int NUM_TRIALS = 10 * TEST_STRESS_LEVEL; + Mutex *pMutex = (Mutex *)pvMutex; + + if ( pMutex == NULL ) + { + _s_ThreadLockingTest = 1; + LOGOG_COUT << _LG("LockingThread received a NULL argument!") << endl; + return; + } + + for ( int t = 0; t < NUM_TRIALS; t++ ) + { + { + ScopedLock sl( *pMutex ); + INFO( _LG("Thread acquired lock on trial %d"), t); + } + + INFO( _LG("Thread released lock %d"), t ); + } +} + + +UNITTEST( Subscription ) +{ + int nResult = 0; + +//! [Subscription] + LOGOG_INITIALIZE(); + + { + Topic n1, n2; + + // should succeed + if ( n1.SubscribeTo( n2 ) != true ) + nResult++; + + // should indicate no insertion took place + if ( n2.PublishTo( n1 ) != false ) + nResult++; + + n2.Transmit(); + + if ( n2.UnpublishTo( n1 ) != true ) + nResult++; + + if ( n1.UnsubscribeTo( n2 ) != false ) + nResult++; + } + + LOGOG_SHUTDOWN(); + +//! [Subscription] + + return nResult; +} + +UNITTEST( GlobalNodelist ) +{ + LOGOG_INITIALIZE(); + + const int MAX_NODES = 10 * TEST_STRESS_LEVEL; + + LOGOG_VECTOR< Topic *> vTopics; + + for (int t = 0; t < MAX_NODES; t++ ) + vTopics.push_back( new Topic ); + + for ( int i = 0; i < MAX_NODES; i++ ) + for ( int j = i + 1; j < MAX_NODES; j++ ) + vTopics.at( i )->SubscribeTo( *vTopics.at( j ) ); + + LockableNodesType *pNodes = ( LockableNodesType * )Static().s_pAllNodes; + + // For this test we assume that the default Filter has always been created. + if ( pNodes->size() != ( MAX_NODES + 1)) + { + LOGOG_COUT << _LG("Incorrect number of nodes!") << endl; + return 1; + } + + // Try having that master node send a message to all other nodes! + vTopics.at( MAX_NODES - 1)->Transmit(); + + // Let's try leaving all the nodes allocated and let logog clean them all up. + LOGOG_SHUTDOWN(); + + if ( Static().s_pAllNodes != NULL ) + { + LOGOG_COUT << _LG("Could not delete all nodes!") << endl; + return 1; + } + + return 0; +} + + +UNITTEST( TimerTest ) +{ + const int NUM_TRIALS = 10 * TEST_STRESS_LEVEL; + const int WAIT_TIME = 1000000 * TEST_STRESS_LEVEL; + + Timer time; + LOGOG_TIME fCurrent = time.Get(); + + for ( int t = 0; t < NUM_TRIALS; t++ ) + { + /* do busy work */ + int k = 0; + for ( int i = 0; i < WAIT_TIME; i++ ) + k = k + 1; // fixes a lint warning + + if ( time.Get() < fCurrent ) + { + LOGOG_COUT << _LG("Timer error! Non monotonic timer behavior") << endl; + return 1; + } + fCurrent = time.Get(); + LOGOG_COUT << _LG("Current reported time: ") << fCurrent << endl; + } + + return 0; +} + +UNITTEST( TopicTest1 ) +{ + LOGOG_INITIALIZE(); + + { + Topic t1( LOGOG_LEVEL_WARN, + _LG( "file1.cpp" ), 50 ); + Topic t2( LOGOG_LEVEL_WARN ); + Topic t3( LOGOG_LEVEL_ERROR, + _LG( "file2.cpp" ), 100 ); + Topic t4( LOGOG_LEVEL_WARN, NULL, 0, + _LG( "Group" ), + _LG( "Category" ), + _LG( "Message" ), + 30.0f); + Topic t5( LOGOG_LEVEL_CRITICAL, NULL, 0, + _LG( "GroupGROUP" ), + _LG( "Important Category" ), + _LG( "Your Message Here"), + 150.0f); + + if (t1.CanSubscribeTo( t2 ) == true) + { + LOGOG_COUT << _LG("Subscription test failed; t1 can subscribe to t2") << endl; + return 1; + } + + if (t2.CanSubscribeTo( t1 ) == false ) + { + LOGOG_COUT << _LG("Subscription test failed; t2 can't subscribe to t1") << endl; + return 1; + } + + if ( t1.CanSubscribeTo( t3 ) == true ) + { + LOGOG_COUT << _LG("Subscription test failed; t1 can subscribe to t3") << endl; + return 1; + } + + if (t2.CanSubscribeTo( t3 ) == false ) + { + LOGOG_COUT << _LG("Subscription test failed; t2 can't subscribe to t3") << endl; + return 1; + } + + if (t4.CanSubscribeTo( t5 ) == false ) + { + LOGOG_COUT << _LG("Subscription test failed; t4 can't subscribe to t5") << endl; + return 1; + } + + if ( t5.CanSubscribeTo( t4 ) == true ) + { + LOGOG_COUT << _LG("Subscription test failed; t5 can subscribe to t4") << endl; + return 1; + } + } + + LOGOG_SHUTDOWN(); + + return 0; +} + +UNITTEST( Checkpoint1 ) +{ + LOGOG_INITIALIZE(); + + { + Checkpoint check( LOGOG_LEVEL_ALL, _LG( __FILE__ ), __LINE__, + _LG("Group"), _LG("Category"), _LG("Message"), 1.0f ); + + Topic t; + + Cerr cerrobj; + Cerr cerrgnu; + OutputDebug outdebug; + + FormatterGCC f; + cerrgnu.SetFormatter( f ); + + check.PublishTo( t ); + t.PublishToMultiple( AllTargets() ); + + LOGOG_COUT << _LG("Setup complete; ready to transmit ") << endl; + check.Transmit(); + } + + LOGOG_SHUTDOWN(); + + return 0; +} + +UNITTEST( FormatString1 ) +{ + LOGOG_INITIALIZE(); + { + String s; + + s.format( _LG("This is a test message.\n")); + LOGOG_COUT << s.c_str(); + + s.format( _LG("This is a test message: %d %x %f\n"), 1234, 0xf00d, 1.234f ); + LOGOG_COUT << s.c_str(); + + const LOGOG_CHAR *p = _LG("Here is a string"); + +#ifdef LOGOG_UNICODE + s.format( _LG("Here are six strings: %ls %ls %ls %ls %ls %ls \n"), p,p,p,p,p,p ); +#else // LOGOG_UNICODE + s.format( _LG("Here are six strings: %s %s %s %s %s %s \n"), p,p,p,p,p,p ); +#endif + + LOGOG_COUT << s.c_str(); + } + + LOGOG_SHUTDOWN(); + + return 0; +} + +UNITTEST( FormatTopic1 ) +{ + LOGOG_INITIALIZE(); + { + Cout out; + LogFile f( "log.txt"); + Message m; + + m.PublishToMultiple( AllTargets() ); + + m.Format( _LG("This is a test message: %d %x %f"), 1234, 0xf00d, 1.234f ); + m.Transmit(); + + const char *p = "Here is a string"; + + m.Format( _LG("Here are six strings: %s %s %s %s %s %s"), p,p,p,p,p,p ); + m.Transmit(); + } + + LOGOG_SHUTDOWN(); + + return 0; +} + +UNITTEST( ThreadLocking ) +{ + LOGOG_INITIALIZE(); + { + Cout out; // only one of these please + + const int NUM_THREADS = 10 * TEST_STRESS_LEVEL; + + LOGOG_VECTOR< Thread *> vpThreads; + Mutex mSharedMutex; + + for ( int t = 0; t < NUM_THREADS; t++ ) + vpThreads.push_back( new Thread( (Thread::ThreadStartLocationType) LockingThread,&mSharedMutex )); + + for ( int t = 0; t < NUM_THREADS; t++ ) + vpThreads[ t ]->Start(); + + for ( int t = 0; t < NUM_THREADS; t++ ) + Thread::WaitFor( *vpThreads[ t ]); + + for ( int t = 0; t < NUM_THREADS; t++ ) + delete vpThreads[t]; + + } + + LOGOG_SHUTDOWN(); + + return _s_ThreadLockingTest; +} + +//! [FormatterCustom1] +class FormatterCustom : public FormatterMSVC +{ + virtual TOPIC_FLAGS GetTopicFlags( const Topic &topic ) + { + return ( Formatter::GetTopicFlags( topic ) & + ~( TOPIC_FILE_NAME_FLAG | TOPIC_LINE_NUMBER_FLAG )); + } +}; + +UNITTEST( CustomFormatter ) +{ + LOGOG_INITIALIZE(); + { + Cout out; + FormatterCustom customFormat; + + out.SetFormatter( customFormat ); + + INFO( _LG( "No file and line number info is provided with this output.") ); + + /* The following output is produced: + * info: No file and line number info is provided with this output. + */ + } + LOGOG_SHUTDOWN(); + + return 0; +} +//! [FormatterCustom1] + +UNITTEST( HelloLogog ) +{ + //! [HelloLogog] + + /* The LOGOG_INITIALIZE() function must be called before we call + * any other logog functions. + */ + LOGOG_INITIALIZE(); + + { + /* In order to see any output, we have to instance a Target object, + * such as a Cerr or a Cout. Additionally, we have to destroy + * this object before calling LOGOG_SHUTDOWN(). This + * is why we have this object within these enclosing brackets. + */ + Cout out; + + /* Send some debugging information to any targets that have + * been instanced. + */ + /* If you're just getting started, and you haven't defined + * LOGOG_UNICODE, then ASCII logging is easy and works + * out of the box. + */ +#ifndef LOGOG_UNICODE + INFO("Hello, logog!"); + WARN("This is a warning"); +#endif // !LOGOG_UNICODE + + /* The _LG() macro around static text permits the given text to + * display correctly in both Unicode and ASCII builds of the + * logog library. + */ + ERR( _LG( "This is an error") ); + DBUG( _LG( "This is debugging info") ); + + /* The Cout object is destroyed here because it falls out of + * scope. */ + } + + /* Call LOGOG_SHUTDOWN() at the termination of your program to free + * all memory allocated by logog. Make sure no logog objects exist + * when you call LOGOG_SHUTDOWN(). + */ + LOGOG_SHUTDOWN(); + + /* Depending on your compiler, the output of the preceding code is + * something like: + * + * test.cpp(373) : info: Hello, logog! + * test.cpp(374) : warning: This is a warning + * test.cpp(375) : error: This is an error + * test.cpp(376) : debug: This is debugging info + */ + //! [HelloLogog] + return 0; +} + + + +UNITTEST( GroupCategory1 ) +{ +//! [GroupCategory1] + /* + The following example produces something like: + .\test.cpp(364) : emergency: {Graphics} [Unrecoverable] The graphics card has been destroyed + .\test.cpp(368) : warning: {Graphics} [Recoverable] The graphics card has been replaced + .\test.cpp(372) : warning: {Audio} [Recoverable] The headphones are unplugged + .\test.cpp(377) : info: Everything's back to normal + */ + + LOGOG_INITIALIZE(); + + { + Cerr err; + +#undef LOGOG_GROUP +#undef LOGOG_CATEGORY +#define LOGOG_GROUP "Graphics" +#define LOGOG_CATEGORY "Unrecoverable" + + EMERGENCY(_LG("The graphics card has been destroyed")); + +#undef LOGOG_CATEGORY +#define LOGOG_CATEGORY "Recoverable" + + WARN(_LG("The graphics card has been replaced")); + +#undef LOGOG_GROUP +#define LOGOG_GROUP "Audio" + + WARN(_LG("The headphones are unplugged")); + +#undef LOGOG_CATEGORY +#undef LOGOG_GROUP +#define LOGOG_CATEGORY NULL +#define LOGOG_GROUP NULL + + INFO(_LG("Everything's back to... %s!"), _LG("normal")); + } + + LOGOG_SHUTDOWN(); + //! [GroupCategory1] + return 0; +} + +UNITTEST( GroupCategory2 ) +{ +//! [GroupCategory2] + LOGOG_INITIALIZE(); + + { + GetDefaultFilter().Category(_LG("Unrecoverable")); + Cerr err; + + WARN(_LG("Logging messages in the Unrecoverable category...")); + + +#undef LOGOG_GROUP +#undef LOGOG_CATEGORY +#define LOGOG_GROUP "Graphics" +#define LOGOG_CATEGORY "Unrecoverable" + + EMERGENCY(_LG("The graphics card has been destroyed")); + +#undef LOGOG_CATEGORY +#define LOGOG_CATEGORY "Recoverable" + + WARN(_LG("The graphics card has been replaced")); + +#undef LOGOG_GROUP +#define LOGOG_GROUP "Audio" + + WARN(_LG("The headphones are unplugged")); + +#undef LOGOG_CATEGORY +#undef LOGOG_GROUP +#define LOGOG_CATEGORY NULL +#define LOGOG_GROUP NULL + + } + + LOGOG_SHUTDOWN(); +//! [GroupCategory2] + return 0; +} + +UNITTEST( GroupCategory3 ) +{ + LOGOG_INITIALIZE(); + + { + /* Assigning a group twice does not leak memory. */ + GetDefaultFilter().Group( _LG( "Controller" )); + GetDefaultFilter().Group( _LG( "Graphics" )); + + Cerr err; + WARN(_LG("This message won't happen because it's not in the Graphics group")); + +#undef LOGOG_GROUP +#undef LOGOG_CATEGORY +#define LOGOG_GROUP "Graphics" +#define LOGOG_CATEGORY "Unrecoverable" + + EMERGENCY(_LG("The graphics card has been destroyed")); + +#undef LOGOG_CATEGORY +#define LOGOG_CATEGORY "Recoverable" + + WARN(_LG("The graphics card has been replaced")); + +#undef LOGOG_GROUP +#define LOGOG_GROUP "Audio" + + WARN(_LG("The headphones are unplugged")); + +#undef LOGOG_CATEGORY +#undef LOGOG_GROUP +#define LOGOG_CATEGORY NULL +#define LOGOG_GROUP NULL + } + + LOGOG_SHUTDOWN(); + return 0; +} + +UNITTEST( GroupCategory4 ) +{ +//! [GroupCategory4] + LOGOG_INITIALIZE(); + + { + GetDefaultFilter().Group(_LG("Graphics")); + Filter filter; + filter.Group(_LG("Audio")); + Cerr err; + + WARN(_LG("This message won't happen because it's not in the Graphics group")); + +#undef LOGOG_GROUP +#undef LOGOG_CATEGORY +#define LOGOG_GROUP "Graphics" +#define LOGOG_CATEGORY "Unrecoverable" + + EMERGENCY(_LG("The graphics card has been destroyed")); + +#undef LOGOG_CATEGORY +#define LOGOG_CATEGORY "Recoverable" + + WARN(_LG("The graphics card has been replaced")); + +#undef LOGOG_GROUP +#define LOGOG_GROUP "Audio" + + WARN(_LG("The headphones are unplugged")); + +#undef LOGOG_GROUP +#define LOGOG_GROUP "Inputs" + + WARN(_LG("The inputs have been yanked off but this fact won't be reported!")); + +#undef LOGOG_CATEGORY +#undef LOGOG_GROUP +#define LOGOG_CATEGORY NULL +#define LOGOG_GROUP NULL + } + + LOGOG_SHUTDOWN(); +//! [GroupCategory4] + return 0; +} + + +UNITTEST( Info1 ) +{ + LOGOG_INITIALIZE(); + { + Cout out; + + for ( int i = 0; i < 2; i++ ) + { + for ( int t = 0; t < 10; t++ ) + { + INFO( _LG("t is now: %d"), t ); + if ( t > 8 ) + WARN(_LG("t is pretty high now!")); + } + + DBUG(_LG("This warning isn't very interesting")); + EMERGENCY(_LG("THIS IS SUPER IMPORTANT!")); + } + + LOGOG_SET_LEVEL( LOGOG_LEVEL_DEBUG ); + LOGOG_DEBUG(_LG("Messages instantiated for the first time will be called now. However, debug messages")); + LOGOG_DEBUG(_LG("instantiated before the LOGOG_SET_LEVEL won't be transmitted.")); + + } + + LOGOG_SHUTDOWN(); + + return 0; +} + +void GeneratePseudoRandomErrorMessages() +{ + int pr = 0xf8d92347; + int ps; + + for ( int t = 0; t < TEST_STRESS_LEVEL * 10; t++ ) + { + pr = pr * 0xd9381381 + 0x13d7b; + ps = pr % ( (1 << 19) - 1 ); + + ERR( _LG("We must inform you of this pseudo-random error code: %d"), ps ); + + // Do a non specific amount of busy work. + int swap1 = 0, swap2 = 1; + for ( int i = 0; i < ps; i++ ) + { + int swap3 = swap1; + swap1 = swap2; + swap2 = swap3; + } + } + +} + +UNITTEST( ImmediateLogging ) +{ + LOGOG_INITIALIZE(); + + { + LogFile logFile( "log.txt" ); + + GeneratePseudoRandomErrorMessages(); + } + + LOGOG_SHUTDOWN(); + + return 0; +} + +#ifdef LOGOG_UNICODE +UNITTEST( UnicodeLogFile ) +{ + LOGOG_INITIALIZE(); + + { + LogFile logFile( "unicode.txt" ); + + // see http://blogs.msdn.com/b/michkap/archive/2008/03/18/8306597.aspx + INFO(L"\x043a\x043e\x0448\x043a\x0430 \x65e5\x672c\x56fd"); + WARN(L"\x043a\x043e\x0448\x043a\x0430 \x65e5\x672c\x56fd"); + ERR(L"\x043a\x043e\x0448\x043a\x0430 \x65e5\x672c\x56fd"); + } + + LOGOG_SHUTDOWN(); + + return 0; +} +#endif // LOGOG_UNICODE + +UNITTEST( DeferredCoutLogging ) +{ + LOGOG_INITIALIZE(); + + { + Cout out; + LogBuffer logBuffer( &out ); + + // Make sure that out does not receive messages via the general filter mechanism. + out.UnsubscribeToMultiple( AllFilters() ); + + for ( int i = 1; i <= 10; i++ ) + { + ERR(_LG("This is error %d of 10"), i); + + int q = 27832; + + for ( int j = 0; j < TEST_STRESS_LEVEL * 10000000; j++ ) + q *= q; + } + } + + LOGOG_SHUTDOWN(); + + return 0; +} + +UNITTEST( DeferredFileLogging ) +{ +//! [DeferredFileLogging] + LOGOG_INITIALIZE(); + + { + LogFile logFile( "log.txt" ); + LogBuffer logBuffer( &logFile ); + + /* Because the LogBuffer is not writing to a line device a la stdout or stderr, it does not need to + * send null terminated strings to its destination (the LogFile). This is a particular peculiarity + * of having a buffering target to a serial-type target, such as a socket or a file. + */ + logBuffer.SetNullTerminatesStrings( false ); + + // Make sure that the log file does not receive messages via the general filter mechanism. + logFile.UnsubscribeToMultiple( AllFilters() ); + + for ( int i = 1; i <= 20; i++ ) + { + WARN(_LG("This is warning %d of 20"), i); + + int q = 27832; + for ( int j = 0; j < TEST_STRESS_LEVEL * 10000000; j++ ) + q *= q; + } + } + + LOGOG_SHUTDOWN(); +//! [DeferredFileLogging] + return 0; +} + +UNITTEST( DateAndTime ) +{ +//! [DateAndTimeLogging] + LOGOG_INITIALIZE(); + + { + Cerr err; + + Formatter *pFormatter = &GetDefaultFormatter(); + + pFormatter->SetShowTimeOfDay( true ); + + for ( int i = 1; i <= 20; i++ ) + { + WARN(_LG("This is warning %d of 20... but with time!"), i); + + int q = 27832; + for ( int j = 0; j < TEST_STRESS_LEVEL * 10000000; j++ ) + q *= q; + } + } + + LOGOG_SHUTDOWN(); +//! [DateAndTimeLogging] + + return 0; +} + + +#ifndef LOGOG_TARGET_PS3 +int DoPlatformSpecificTestInitialization() +{ + return 0; +} +#endif +int main( int , char ** ) +{ +#ifdef LOGOG_LEAK_DETECTION_WINDOWS + _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF ); +#endif // LOGOG_LEAK_DETECTION_WINDOWS + +//! [WindowsUnicodeSetup] +#ifdef LOGOG_UNICODE +#ifdef LOGOG_FLAVOR_WINDOWS + _setmode(_fileno(stdout), _O_U16TEXT); + _setmode(_fileno(stderr), _O_U16TEXT); +#endif // LOGOG_FLAVOR_WINDOWS +#endif // LOGOG_UNICODE +//! [WindowsUnicodeSetup] + + int nPlatformSpecific; + nPlatformSpecific = DoPlatformSpecificTestInitialization(); + if ( nPlatformSpecific != 0) + return nPlatformSpecific; + + int nResult; + nResult = RunAllTests(); + ShutdownTests(); + +#ifdef LOGOG_LEAK_DETECTION_WINDOWS + _CrtMemState crtMemState; + _CrtMemCheckpoint( &crtMemState ); + _CrtMemDumpStatistics( &crtMemState ); + _CrtDumpMemoryLeaks(); +#endif // LOGOG_LEAK_DETECTION_WINDOWS + + return nResult; +} + diff --git a/Base/logog/test/test.sln b/Base/logog/test/test.sln new file mode 100644 index 0000000000000000000000000000000000000000..5e7b2d0feb6b733d20d3f852e2b3d25e9ccd7b8a --- /dev/null +++ b/Base/logog/test/test.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcproj", "{FFD188AD-0FE5-4808-90E0-EC528324FFAD}" + ProjectSection(ProjectDependencies) = postProject + {85857E80-2122-4DD8-9BA9-B90CC10D65B0} = {85857E80-2122-4DD8-9BA9-B90CC10D65B0} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "logog", "..\src\logog.vcproj", "{85857E80-2122-4DD8-9BA9-B90CC10D65B0}" +EndProject +Global + GlobalSection(SourceCodeControl) = preSolution + SccNumberOfProjects = 2 + SccProjectUniqueName0 = test.vcproj + SccProjectName0 = Perforce\u0020Project + SccLocalPath0 = . + SccProvider0 = MSSCCI:Perforce\u0020SCM + SccProjectUniqueName1 = ..\\src\\logog.vcproj + SccProjectName1 = Perforce\u0020Project + SccLocalPath1 = .. + SccProvider1 = MSSCCI:Perforce\u0020SCM + SccProjectFilePathRelativizedFromConnection1 = src\\ + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FFD188AD-0FE5-4808-90E0-EC528324FFAD}.Debug|Win32.ActiveCfg = Debug|Win32 + {FFD188AD-0FE5-4808-90E0-EC528324FFAD}.Debug|Win32.Build.0 = Debug|Win32 + {FFD188AD-0FE5-4808-90E0-EC528324FFAD}.Release|Win32.ActiveCfg = Release|Win32 + {FFD188AD-0FE5-4808-90E0-EC528324FFAD}.Release|Win32.Build.0 = Release|Win32 + {85857E80-2122-4DD8-9BA9-B90CC10D65B0}.Debug|Win32.ActiveCfg = Debug|Win32 + {85857E80-2122-4DD8-9BA9-B90CC10D65B0}.Debug|Win32.Build.0 = Debug|Win32 + {85857E80-2122-4DD8-9BA9-B90CC10D65B0}.Release|Win32.ActiveCfg = Release|Win32 + {85857E80-2122-4DD8-9BA9-B90CC10D65B0}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Base/logog/test/test.vcproj b/Base/logog/test/test.vcproj new file mode 100644 index 0000000000000000000000000000000000000000..8b14c91680cd2955a83cc9e5847e690fa57137ca --- /dev/null +++ b/Base/logog/test/test.vcproj @@ -0,0 +1,231 @@ +<?xml version="1.0" encoding="Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="9.00" + Name="test" + ProjectGUID="{FFD188AD-0FE5-4808-90E0-EC528324FFAD}" + RootNamespace="test" + SccProjectName="Perforce Project" + SccLocalPath="." + SccProvider="MSSCCI:Perforce SCM" + Keyword="Win32Proj" + TargetFrameworkVersion="196613" + > + <Platforms> + <Platform + Name="Win32" + /> + </Platforms> + <ToolFiles> + </ToolFiles> + <Configurations> + <Configuration + Name="Debug|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="0" + AdditionalIncludeDirectories="..\include" + PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE" + MinimalRebuild="true" + BasicRuntimeChecks="3" + RuntimeLibrary="3" + UsePrecompiledHeader="0" + WarningLevel="4" + Detect64BitPortabilityProblems="false" + DebugInformationFormat="4" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="2" + GenerateDebugInformation="true" + SubSystem="1" + RandomizedBaseAddress="0" + DataExecutionPrevention="0" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + HeapVerification="2" + HandleVerification="2" + LocksVerification="2" + PageHeapConserveMemory="false" + PageHeapProtectionLocation="1" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + <Configuration + Name="Release|Win32" + OutputDirectory="$(SolutionDir)$(ConfigurationName)" + IntermediateDirectory="$(ConfigurationName)" + ConfigurationType="1" + CharacterSet="1" + WholeProgramOptimization="1" + > + <Tool + Name="VCPreBuildEventTool" + /> + <Tool + Name="VCCustomBuildTool" + /> + <Tool + Name="VCXMLDataGeneratorTool" + /> + <Tool + Name="VCWebServiceProxyGeneratorTool" + /> + <Tool + Name="VCMIDLTool" + /> + <Tool + Name="VCCLCompilerTool" + Optimization="2" + EnableIntrinsicFunctions="true" + AdditionalIncludeDirectories="..\include" + PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE" + RuntimeLibrary="2" + EnableFunctionLevelLinking="true" + UsePrecompiledHeader="0" + WarningLevel="4" + DebugInformationFormat="3" + /> + <Tool + Name="VCManagedResourceCompilerTool" + /> + <Tool + Name="VCResourceCompilerTool" + /> + <Tool + Name="VCPreLinkEventTool" + /> + <Tool + Name="VCLinkerTool" + LinkIncremental="1" + GenerateDebugInformation="true" + SubSystem="1" + OptimizeReferences="2" + EnableCOMDATFolding="2" + TargetMachine="1" + /> + <Tool + Name="VCALinkTool" + /> + <Tool + Name="VCManifestTool" + /> + <Tool + Name="VCXDCMakeTool" + /> + <Tool + Name="VCBscMakeTool" + /> + <Tool + Name="VCFxCopTool" + /> + <Tool + Name="VCAppVerifierTool" + /> + <Tool + Name="VCPostBuildEventTool" + /> + </Configuration> + </Configurations> + <References> + </References> + <Files> + <Filter + Name="Source Files" + Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx" + UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" + > + <File + RelativePath=".\test.cpp" + > + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl;inc;xsd" + UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" + > + </Filter> + <Filter + Name="Resource Files" + Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav" + UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}" + > + </Filter> + <File + RelativePath="..\doc\overview.dox" + > + <FileConfiguration + Name="Debug|Win32" + > + <Tool + Name="VCCustomBuildTool" + Description="Generating doxygen output" + CommandLine="$(InputDir)make-doxygen.bat
" + Outputs="$(InputDir)html\index.html" + /> + </FileConfiguration> + <FileConfiguration + Name="Release|Win32" + ExcludedFromBuild="true" + > + <Tool + Name="VCCustomBuildTool" + Description="Generating doxygen output" + CommandLine="$(InputDir)make-doxygen.bat
" + Outputs="$(InputDir)html\index.html" + /> + </FileConfiguration> + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> diff --git a/Base/logog/test/test.vcproj.lnt b/Base/logog/test/test.vcproj.lnt new file mode 100644 index 0000000000000000000000000000000000000000..5fd30307fecb5cefe593734b3dc23ed815f10632 --- /dev/null +++ b/Base/logog/test/test.vcproj.lnt @@ -0,0 +1,11 @@ +/* -dConfiguration= ... none provided */ +-D_UNICODE // 27: CharacterSet = "1" +-DUNICODE // 27: CharacterSet = "" +-i"..\include" // 47: AdditionalIncludeDirectories = "..\include" +-DWIN32 // 48: PreprocessorDefinitions = "WIN32;_DEBUG;_CONSOLE" +-D_DEBUG // 48: PreprocessorDefinitions = "" +-D_CONSOLE // 48: PreprocessorDefinitions = "" +-D_MT // 51: RuntimeLibrary = "3" +-D_DEBUG // 51: RuntimeLibrary = "" +-D_DLL // 51: RuntimeLibrary = "" +.\test.cpp // 180: RelativePath = ".\test.cpp" diff --git a/SimpleTests/MatrixTests/CMakeLists.txt b/SimpleTests/MatrixTests/CMakeLists.txt index e31f2b12a3af522e6b2903f47863100c18181914..fad2305d98d13b8939e12e1d9d0712001985cadd 100644 --- a/SimpleTests/MatrixTests/CMakeLists.txt +++ b/SimpleTests/MatrixTests/CMakeLists.txt @@ -11,8 +11,9 @@ ENDIF (CMAKE_USE_PTHREADS_INIT ) INCLUDE_DIRECTORIES( . - ../../Base/ - ../../MathLib/ + ${CMAKE_SOURCE_DIR}/Base/ + ${CMAKE_SOURCE_DIR}/Base/logog/include + ${CMAKE_SOURCE_DIR}/MathLib/ ) # Create the executable @@ -22,6 +23,7 @@ ADD_EXECUTABLE( MatMult ${HEADERS} ) SET_TARGET_PROPERTIES(MatMult PROPERTIES FOLDER SimpleTests) +TARGET_LINK_LIBRARIES(MatMult logog) # Create the executable ADD_EXECUTABLE( MatTestRemoveRowsCols diff --git a/SimpleTests/MatrixTests/MatMult.cpp b/SimpleTests/MatrixTests/MatMult.cpp index 95bcea98f3260b56d47b9d2aea57323bee0d0a74..569451bc8ed6c977e8fa3d3f51cbe1c8ddacb711 100644 --- a/SimpleTests/MatrixTests/MatMult.cpp +++ b/SimpleTests/MatrixTests/MatMult.cpp @@ -9,6 +9,7 @@ #include "LinAlg/Sparse/CRSMatrixPThreads.h" #include "RunTimeTimer.h" #include "CPUTimeTimer.h" +#include "logog.hpp" #ifdef _OPENMP #include <omp.h> @@ -16,8 +17,12 @@ int main(int argc, char *argv[]) { + LOGOG_INITIALIZE(); + logog::Cout* logogCout = new logog::Cout; + if (argc < 4) { std::cout << "Usage: " << argv[0] << " num_of_threads matrix number_of_multiplications resultfile" << std::endl; + INFO("Usage: %s num_of_threads matrix number_of_multiplications resultfile", argv[0]); exit (1); } @@ -31,30 +36,22 @@ int main(int argc, char *argv[]) std::string fname_mat (argv[2]); - bool verbose (true); - // *** reading matrix in crs format from file std::ifstream in(fname_mat.c_str(), std::ios::in | std::ios::binary); double *A(NULL); unsigned *iA(NULL), *jA(NULL), n; if (in) { - if (verbose) { - std::cout << "reading matrix from " << fname_mat << " ... " << std::flush; - } + DBUG("reading matrix from %s ...", fname_mat.c_str()); RunTimeTimer timer; timer.start(); CS_read(in, n, iA, jA, A); timer.stop(); - if (verbose) { - std::cout << "ok, " << timer.elapsed() << " s" << std::endl; - } + DBUG("ok, %n s", timer.elapsed()); } else { - std::cout << "error reading matrix from " << fname_mat << std::endl; + ERR("error reading matrix from %s", fname_mat.c_str()); } unsigned nnz(iA[n]); - if (verbose) { - std::cout << "Parameters read: n=" << n << ", nnz=" << nnz << std::endl; - } + INFO("Parameters read: n=%n, nnz=%n", n, nnz); #ifdef _OPENMP omp_set_num_threads(n_threads); @@ -63,7 +60,7 @@ int main(int argc, char *argv[]) MathLib::CRSMatrix<double, unsigned> mat (n, iA, jA, A); #endif // CRSMatrixPThreads<double> mat (n, iA, jA, A, n_threads); - std::cout << mat.getNRows() << " x " << mat.getNCols() << std::endl; + INFO("%n x %n", mat.getNRows(), mat.getNCols()); double *x(new double[n]); double *y(new double[n]); @@ -71,9 +68,7 @@ int main(int argc, char *argv[]) for (unsigned k(0); k<n; ++k) x[k] = 1.0; - if (verbose) { - std::cout << "matrix vector multiplication with Toms amuxCRS (" << n_threads << " threads) ... " << std::flush; - } + DBUG("matrix vector multiplication with Toms amuxCRS (%n threads) ...", n_threads); RunTimeTimer run_timer; CPUTimeTimer cpu_timer; run_timer.start(); @@ -84,26 +79,27 @@ int main(int argc, char *argv[]) cpu_timer.stop(); run_timer.stop(); - if (verbose) { - std::cout << "done [" << cpu_timer.elapsed() << " sec cpu time], [" - << run_timer.elapsed() << " sec run time]" << std::endl; - std::cout << "CPU time: " << cpu_timer.elapsed() << std::endl; - std::cout << "wclock time: " << run_timer.elapsed() << std::endl; - } else { - if (argc == 5) { - std::ofstream result_os (argv[4], std::ios::app); - if (result_os) { - result_os << cpu_timer.elapsed() << "\t" << run_timer.elapsed() << std::endl; - } - result_os.close(); - } else { - std::cout << cpu_timer.elapsed() << "\t" << run_timer.elapsed() << std::endl; + DBUG("done [%n sec cpu time], [%n sec run time]", cpu_timer.elapsed(), run_timer.elapsed()); + DBUG("CPU time: %n", cpu_timer.elapsed()); + DBUG("wclock time: %n", run_timer.elapsed()); + + if (argc == 5) { + std::ofstream result_os (argv[4], std::ios::app); + if (result_os) { + result_os << cpu_timer.elapsed() << "\t" << run_timer.elapsed() << std::endl; } + result_os.close(); + } else { + INFO("%n \t %n", cpu_timer.elapsed(), run_timer.elapsed()); } + delete [] x; delete [] y; + delete logogCout; + LOGOG_SHUTDOWN(); + return 0; } diff --git a/scripts/cmake/ProjectSetup.cmake b/scripts/cmake/ProjectSetup.cmake index 43d955296f359bf696e3000453876d2790a3b97f..53984c425469a1b4bf0babf482aefb39b674eda1 100644 --- a/scripts/cmake/ProjectSetup.cmake +++ b/scripts/cmake/ProjectSetup.cmake @@ -1,3 +1,14 @@ # Set build directories SET( EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin ) -SET( LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib ) \ No newline at end of file +SET( LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib ) + +# Logging level +IF(NOT DEFINED OGS_LOG_LEVEL) + IF(CMAKE_BUILD_TYPE STREQUAL "Debug") + ADD_DEFINITIONS(-DLOGOG_LEVEL=LOGOG_LEVEL_DEBUG) + ELSE() + ADD_DEFINITIONS(-DLOGOG_LEVEL=LOGOG_LEVEL_INFO) + ENDIF() # CMAKE_BUILD_TYPE = Debug +ELSE() + ADD_DEFINITIONS(-DLOGOG_LEVEL=${OGS_LOG_LEVEL}) +ENDIF() # NOT DEFINED OGS_LOG_LEVEL