This small utility library allows you to register types at one place of your
program and retrieve their names at runtime at other places. The library is
thought to be used for simple debugging and writing test-suites based on c++
templates. Currently it supports standard output streams
(std::ostream) and standard strings (std::string) as outputs.
The project is at very early stage. Few things already work quite well, but much of the work is in front, and much of the existing code/interfaces might be subject to changes. The current status is thus experimental.
Things that work:
- fundamental c++ types (fundamental c++ types are pre-registered),
- function pointers (displayed as hex address) and registered function pointers (displayed as function name),
- registering custom classes and structures,
- registering template classes (with type and non-type parameters),
- handling cv-qualified types, references, pointers and array extents for each registered type without additional user's effort,
- direct output to standard streams and standard strings, this also works with
boost::unit_test::lazy_ostreamused byBOOST_TEST_MESSAGE();
Things that are missing:
- preregistered types from most standard library, e.g. std::string, and so on,
- registering function types, function templates, and so on,
- support for templates as template parameters in macros used to register types, stream is possible with functions which return type name written to
std::string
The typeout library has following dependencies:
- Scons, if you need to build unit tests, examples or documentation,
gcc(4.8.2 or later) orclang(3.3 or later), if you need to build unit tests or examples,- Boost Config is used to configure the internals and features of the library, you need it each time you use the typeout library,
- Boost Preprocessor is used by several preprocessor macros, you need it each time you use the typeout library,
- Boost Test Library is used by test suite, you'll need it if you plan to compile (and run) test suites,
- Doxygen used to generate API documentation, you'll need it if you plan to compile API documentation,
Example installation on debian/ubuntu:
apt-get install g++-4.8forgcc, orapt-get install clang libstdc++-4.8-devforclang,apt-get install libboost-test-dev libboost-devfor any compiler,apt-get install scons,apt-get install doxygen, if you need build documenation,
Note: we don't use libc++-dev (a library provided by LLVM) because boost
testing framework is compiled against libstdc++ and we must use the same.
If you ever used Boost Test Library, you may know the test case templates
(for example BOOST_AUTO_TEST_CASE_TEMPLATE()). Normally you feed the test
case template from a meta-sequence of types and it performs series of checks on
each type from the sequence. The problem is, that when something goes wrong at
runtime and you start investigating the issue, the default output from test
runner may be not informative. The test runner usually shows you just brief
result of failed checks without information about the (type) name of the
element from meta-sequence for which the test failed, or (with
--log_level=test_suite for example) the crucial information is encoded in
mangled type names that are not human readable (at least in some compilers,
e.g. g++). One possible remedy is to provide user-readable messages with full
information about currently processed types.
The usage example of BOOST_AUTO_TEST_CASE_TEMPLATE() (from its documentation) is:
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>
typedef boost::mpl::list<int,long,unsigned char> test_types;
BOOST_AUTO_TEST_CASE_TEMPLATE(my_test, T, test_types)
{
BOOST_CHECK_EQUAL( sizeof(T), (unsigned)4 );
}
The question is, what you'll learn from the output of this test case when it
fails (it is designed to fail on most platforms, consider the result of
sizeof(unsigned char)==4)? Well, you'll be informed that:
Running 3 test cases... a.cpp(10): error in "my_test<l>": check sizeof(T) == (unsigned)4 failed [8 != 4] a.cpp(10): error in "my_test<h>": check sizeof(T) == (unsigned)4 failed [1 != 4] *** 2 failures detected in test suite "example"
Now it's time to start guessing, what was the T type at the time of
failure. If your test suite is compiled with gcc, then with flag
--log_level=test_suite you'll be see what follows
Running 3 test cases... Entering test suite "example" Entering test case "my_test<i>" Leaving test case "my_test<i>" Entering test case "my_test<l>" a.cpp(10): error in "my_test<l>": check sizeof(T) == (unsigned)4 failed [8 != 4] Leaving test case "my_test<l>" Entering test case "my_test<h>" a.cpp(10): error in "my_test<h>": check sizeof(T) == (unsigned)4 failed [1 != 4] Leaving test case "my_test<h>" Leaving test suite "example" *** 2 failures detected in test suite "example"
Judge yourself if it is more informative than the output without any additional debug information and imagine the debugging for types which come from instantiation of complicated templates.
The typeout library provides utilities which basically allow you to generate meaningful messages containing previously registered type names, and can be used to resolve above debugging issues.
Consider the test case from previous section. With typeout you may easily augment the test case output with your own messages containing type names. For example:
#define BOOST_TEST_MODULE example
#include <boost/test/included/unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/mpl/list.hpp>
#include <typeout/typeout.hpp>
typedef boost::mpl::list<int,long,unsigned char> test_types;
BOOST_AUTO_TEST_CASE_TEMPLATE(my_test, T, test_types)
{
using typeout::_ostream::_;
BOOST_TEST_MESSAGE(__FILE__ << "(" << __LINE__ << "): note: running my_test<T> with T = " << _<T> << ";");
BOOST_CHECK_MESSAGE( (sizeof(T) == (unsigned)4), "check sizeof(" << _<T> << ") == (unsigned)4 failed [" << sizeof(T) << " != 4]" );
}
Now, the output from test runner with --log_level=message is like:
Running 3 test cases... /tmp/a.cpp(12): note: running my_test<T> with T = int; /tmp/a.cpp(12): note: running my_test<T> with T = long int; /tmp/a.cpp(13): error in "my_test<l>": check sizeof(long int) == (unsigned)4 failed [8 != 4] /tmp/a.cpp(12): note: running my_test<T> with T = unsigned char; /tmp/a.cpp(13): error in "my_test<h>": check sizeof(unsigned char) == (unsigned)4 failed [1 != 4]
As you see, it's much easier to guess, that the test suite failed for long
int and unsigned char types.
API documentation may be generated with:
scons api-doc
The generated docs may be found under build/doc/typeout/html/index.html.
Tests may be compiled with:
scons unit-test
This creates single program which runs all defined unit tests. The program is
build/test/runtest or build/test/runtest.exe. On Linux platforms the
tests may be run by bin/runtest. The tests runner accepts flags defined by
Boost Test Library documentation.
Examples may be compiled with:
scons
The compiled binaries may be found under build/examples/.
Top level source directory contains following subdirs:
Directory Description bin/contains mainainer scripts, build/this is main (default) variant directory, all the results of compilation go there; the directory is created by scons, HOWTO/several HOWTO documents are placed here, debian/debian packaging files (currently empty), rpm/rpm packaging files (currently empty) site_scons/extensions used by scons, src/main source tree with source files to be compiled, template/templates for source files,
@COPYRIGHT@
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