C++ iostream using OutputDebugString()
Win32 API. A bit part of this code uses C++11 features, which are explicit instantiation declaration and default template parameter for function templates. Thus, VC11(VS2012) can not be used.
Typical usage: header only, macro style, with lock, and appropriate charcter type
#define DEBUG // if DEBUG is not defined, they become no-ops.
#include "odstream.hpp"
ODS(<< "TEST" << std::endl); // for char
WODS(<< L"TEST" << std::endl); // for wchar_t
This library can use as a header-only library or an ordinary library. By default, header-only usage is chosen because of no need of preparation. To use as an ordinary library, you need to define YAK_DEBUG_NO_HEADER_ONLY
when including odstream.hpp
,
make
builds libodstream.a
. Include odstream.hpp
and link with libodstream.a
nmake
builds odstream.lib
and odstream_s.lib
. odstream.lib
is for -MD
runtime option and odstream_s.lib
for -MT
runtime option. Include odstream.hpp
and link with an appropriate library.
-
Direct style
*yak::debug::ods() << "Trace messages"; yak::debug::ods_flush(); // *yak::debug::ods() << "Trace messages" << std::flush; // requires #include <iostream>
If
DEBUG
macro is defined, the message is output byOutputDebugString()
when a flush occurs. Otherwise, ignored. Instead of callingyak::debug::ods_flush()
, you can flush by using stream manipulators likestd::flush
andstd::endl
. However, such manipulators require includingiostream
header reagardless whetherDEBUG
is defined or not, and it might cause a burden of executable size.If you want to output messages regardless of whether
DEBUG
marcro is defined or not, you can useyak::debug_yes::ods()
. -
Macro style
ODS_NOFLUSH(<< "Trace messages1"); // without flush ODS(<< "Trace messages2"); // with flush
This is simliar as direct style but needs not including
iostream
whenDEBUG
is not defined, even if manipulators are used.To tell the truth, including
iostream
whenDEBUG
is defined is done byodstream.hpp
header, so you need not it by yourself. To defineODSTREAM_NO_INCLUDE_IOSTREAM
suppresses this behavior.
You can choose char type from char
and wchar_t
as follows:
// default is 'char'
*yak::debug::ods() << "char type is 'char'"; yak::debug::ods_flush();
*yak::debug::ods<char>() << "char type is 'char'"; yak::debug::ods_flush();
*yak::debug::ods<wchar_t>() << L"char type is 'wchar_t'"; yak::debug::ods_flush<wchar_t>()
// Macro style
ODS(<< "char type is 'char'")
ODS_NOFLUSH(<< "char type is 'char'")
WODS(<< L"char type is 'wchar_t'")
WODS_NOFLUSH(<< L"char type is 'wchar_t'")
This library uses CRITICAL_SECTION in Win32 API to avoid interleave of output in different expressions. An order of output by expressions is generally unspecified, but they are not interleaved. If you need not to use CRITICAL_SECTION, you can use underline-postfixed version of interfaces like ods_()
.
// Thread1
ODS(<< "Output" << 1 << " continues" << std::endl);
// Thread2
*yak::debug::ods() << "Output" << 2 << " continues" << std::endl;
// They should not be interleaved, so results should be either of the belows:
// Output1 continues
// Output2 continues
// or
// Output2 continues
// Output1 continues
Note that the lock is effective inside a full expression. If you have 2 or more full expressions, you may get interleaved results.
// Thread1
*yak::debug::ods() << "Output"; *yak::debug::ods() << 1;
*yak::debug::ods() << " continues"; *yak::debug::ods() << std::endl;
// Thread2
*yak::debug::ods() << "Output"; *yak::debug::ods() << 1;
*yak::debug::ods() << " continues"; *yak::debug::ods() << std::endl;
A returned value by ods()
is not always a pointer to std::ostream
. Dereference and to use stream operator: operator<<
can be always used but member functions can't. If you need to call member functions, you can use operator->(). However, considering lock lifetime, manipurator with stream operator chain might be better. On the other hand, more headers may be required even if DEBUG
is not defined, as described above.
*yak::debug::ods() << "OK"; // Always OK
yak::debug::ods_flush(); // Always OK
//(*yak::debug::ods()).flush(); // OK only if DEBUG is defined
yak::debug::ods()->flush(); // Always OK but #include <iostream> is necessary
*yak::debug::ods() << setw(5); // Always OK but #include <iomanip> is necessary
Here are examples of executable size in bytes. These vary in different options and environments, especially for header-only usage.
Cygwin | header-only DEBUG |
header-only no DEBUG |
no header-only DEBUG |
no header-only no DEBUG |
blank main |
---|---|---|---|---|---|
normal | 22,030 | 8,718 | 28,174 | 25,102 | 8,718 |
static link | 939,022 | 887,310 | 944,142 | 942,606 | 8,718 |
VC14 | header-only DEBUG |
header-only no DEBUG |
no header-only DEBUG |
no header-only no DEBUG |
blank main |
---|---|---|---|---|---|
normal | 43,520 | 8,704 | 43,520 | 9,728 | 8,192 |
static link | 203,264 | 86,528 | 203,264 | 117,760 | 86,016 |
Namespaces and Library Versioning by Hurb Sutter describes namespace alias and library versioning as "Option 2(d): Use an alias" at the bottom of the article. This is a convenient way for library versioning or option switching.
Because actual names of entities are kept so that we can link without re-compile even though different configuration co-exists in object files and can refer needed entities directly if necessary, like yak::debug_yes::ods()
. As desribed the article, there are limitations for namespace alias, such as specialization, re-opening namespace, and so on. These limitations are not concerns of this library. Thus, namespace alias is enough for this library.
If these limitations are actual concerns, we can use inline namespace introduced in C++11 like the followings:
#ifdef DEBUG
#define INLINE_YES inline
#define INLINE_NO
#else
#define INLINE_YES
#define INLINE_NO inline
#endif
namespace yak {
namespace debug {
INLINE_YES namespace yes {
}
INLINE_NO namespace no {
}
}
}
This software is distributed under the terms of a zlib/libpng License.
Copyright (c) 2013,2017 Yak! / Yasutaka ATARASHI
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
This notice may not be removed or altered from any source distribution.