[protozero] 01/06: Imported Upstream version 1.3.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Fri Feb 19 10:55:56 UTC 2016


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository protozero.

commit 4337a0879016ea80528a6756a0a0b84e6902eb67
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Fri Feb 19 11:32:44 2016 +0100

    Imported Upstream version 1.3.0
---
 .npmignore                                         |   33 +
 .travis.yml                                        |  150 +-
 CHANGELOG.md                                       |   21 +-
 CONTRIBUTING.md                                    |   17 +
 Makefile                                           |   12 +-
 README.md                                          |    2 +-
 build-appveyor.bat                                 |   12 +-
 common.gypi => gyp/common.gypi                     |    0
 gyp/protozero.gyp                                  |   37 +
 include/protozero/config.hpp                       |    4 +-
 include/protozero/exception.hpp                    |    8 +-
 include/protozero/pbf_builder.hpp                  |   14 +-
 include/protozero/pbf_message.hpp                  |    2 +-
 include/protozero/pbf_reader.hpp                   |   40 +-
 include/protozero/pbf_writer.hpp                   |  202 +-
 include/protozero/{pbf_types.hpp => types.hpp}     |    8 +-
 include/protozero/varint.hpp                       |    4 +-
 include/protozero/version.hpp                      |    6 +-
 package.json                                       |   10 +
 protozero.gyp                                      |   37 -
 test/include/catch.hpp                             | 3045 ++++++++++++--------
 test/include/scalar_access.hpp                     |  111 +
 test/include/test.hpp                              |    6 +
 test/t/{fixed32 => alignment}/test_cases.cpp       |   89 +-
 test/t/fixed32/{data-max-uint.pbf => data-max.pbf} |    0
 test/t/fixed32/data-min-uint.pbf                   |  Bin 5 -> 0 bytes
 test/t/fixed32/data-pos.pbf                        |  Bin 0 -> 5 bytes
 test/t/fixed32/test_cases.cpp                      |  109 +-
 test/t/fixed32/testcase.cpp                        |    8 +-
 test/t/fixed32/writer_test_cases.cpp               |    4 +-
 test/t/fixed64/{data-max-uint.pbf => data-max.pbf} |    0
 test/t/fixed64/data-min-uint.pbf                   |  Bin 9 -> 0 bytes
 test/t/fixed64/data-pos.pbf                        |  Bin 0 -> 9 bytes
 test/t/fixed64/test_cases.cpp                      |   79 +-
 test/t/fixed64/testcase.cpp                        |    8 +-
 test/t/float/test_cases.cpp                        |    6 +-
 test/t/int32/test_cases.cpp                        |  118 +-
 test/t/int64/data-overflow.pbf                     |    1 -
 test/t/int64/test_cases.cpp                        |  107 +-
 test/t/int64/testcase.cpp                          |   10 -
 test/t/message/test_cases.cpp                      |   46 +
 test/t/repeated_packed_bool/test_cases.cpp         |   36 +
 test/t/repeated_packed_fixed32/test_cases.cpp      |   72 +
 test/t/repeated_packed_int64/test_cases.cpp        |   38 +
 test/t/repeated_packed_sint64/test_cases.cpp       |   38 +
 test/t/rollback/test_cases.cpp                     |  214 ++
 test/t/sfixed32/{data-max-int.pbf => data-max.pbf} |    0
 test/t/sfixed32/{data-min-int.pbf => data-min.pbf} |  Bin
 .../data-max-uint.pbf => sfixed32/data-neg.pbf}    |    0
 test/t/sfixed32/data-pos.pbf                       |  Bin 0 -> 5 bytes
 test/t/sfixed32/test_cases.cpp                     |   68 +-
 test/t/sfixed32/testcase.cpp                       |   10 +-
 test/t/sfixed64/{data-max-int.pbf => data-max.pbf} |    0
 test/t/sfixed64/{data-min-int.pbf => data-min.pbf} |  Bin
 .../data-max-uint.pbf => sfixed64/data-neg.pbf}    |    0
 test/t/sfixed64/data-pos.pbf                       |  Bin 0 -> 9 bytes
 test/t/sfixed64/test_cases.cpp                     |   68 +-
 test/t/sfixed64/testcase.cpp                       |   10 +-
 test/t/sint32/test_cases.cpp                       |   98 +-
 test/t/sint64/test_cases.cpp                       |   98 +-
 test/t/skip/test_cases.cpp                         |   16 +-
 test/t/string/test_cases.cpp                       |    9 -
 test/t/uint32/test_cases.cpp                       |   68 +-
 test/t/uint64/test_cases.cpp                       |   68 +-
 test/t/wrong_type_access/test_cases.cpp            |   55 +
 tutorial.md                                        |  119 +-
 66 files changed, 3236 insertions(+), 2215 deletions(-)

diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..ef6dd71
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,33 @@
+coverage
+*.gcov
+out
+protozero.Makefile
+tests.target.mk
+*.o
+*.gcno
+*.gcno
+#Visual Studio files and folders
+Release
+Debug
+deps
+*.sdf
+*.sln
+*.suo
+*.vcxproj*
+*.7z
+test
+build-local.bat
+build-appveyor.bat
+bench
+notes.md
+coverity.sh
+protozero.gyp
+common.gypi
+tutorial.md
+CHANGELOG.md
+doc
+.travis.yml
+appveyor.yml
+Makefile
+.gitattributes
+.npmignore
diff --git a/.travis.yml b/.travis.yml
index a525b14..6ab690d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,58 +1,106 @@
-#-----------------------------------------------------------------------------
-#
-#  Configuration for continuous integration service at travis-ci.org
-#
-#-----------------------------------------------------------------------------
-
-language: cpp
+language: c
 
 sudo: false
 
+# Save common build configurations as shortcuts, so we can reference them later.
+addons_shortcuts:
+  addons_clang35: &clang35
+    apt:
+      sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5' ]
+      packages: [ 'clang-3.5', 'libprotobuf-dev','protobuf-compiler' ]
+  addons_clang38: &clang38
+    apt:
+      sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-precise' ]
+      packages: [ 'clang-3.8', 'libprotobuf-dev','protobuf-compiler' ]
+  addons_gcc49: &gcc47
+    apt:
+      sources: [ 'ubuntu-toolchain-r-test' ]
+      packages: [ 'g++-4.7', 'gcc-4.7', 'libprotobuf-dev','protobuf-compiler' ]
+  addons_gcc49: &gcc48
+    apt:
+      sources: [ 'ubuntu-toolchain-r-test' ]
+      packages: [ 'g++-4.8', 'gcc-4.8', 'libprotobuf-dev','protobuf-compiler' ]
+  addons_gcc49: &gcc49
+    apt:
+      sources: [ 'ubuntu-toolchain-r-test' ]
+      packages: [ 'g++-4.9', 'gcc-4.9', 'libprotobuf-dev','protobuf-compiler' ]
+  addons_gcc49: &gcc5
+    apt:
+      sources: [ 'ubuntu-toolchain-r-test' ]
+      packages: [ 'g++-5', 'gcc-5', 'libprotobuf-dev','protobuf-compiler' ]
+
 matrix:
-    include:
-        - os: linux
-          compiler: clang
-          env: INSTALL_CXX=clang++-3.5
-          addons:
-            apt:
-                sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.5']
-                packages: ['clang-3.5','libprotobuf-dev','protobuf-compiler']
-        - os: linux
-          compiler: clang
-          env: INSTALL_CXX=clang++-3.6
-          #env: INSTALL_CXX=clang++-3.6 COVERAGE=llvm-cov-3.6
-          addons:
-            apt:
-                sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6']
-                packages: ['clang-3.6','libprotobuf-dev','protobuf-compiler']
-        - os: linux
-          compiler: gcc
-          env: INSTALL_CXX=g++-4.8
-          addons:
-            apt:
-                sources: ['ubuntu-toolchain-r-test']
-                packages: ['g++-4.8','libprotobuf-dev','protobuf-compiler']
-        - os: linux
-          compiler: gcc
-          env: INSTALL_CXX=g++-4.9 COVERAGE=gcov-4.9
-          addons:
-            apt:
-                sources: ['ubuntu-toolchain-r-test']
-                packages: ['g++-4.9','libprotobuf-dev','protobuf-compiler']
-        - os: osx
-          compiler: clang
+  include:
+    - os: osx
+      osx_image: xcode6
+      compiler: clang
+    - os: osx
+      osx_image: xcode7
+      compiler: clang
+    - os: linux
+      compiler: "clang35"
+      env: CXX=clang++-3.5
+      addons: *clang35
+    - os: linux
+      compiler: "clang38"
+      env: CXX=clang++-3.8
+      addons: *clang38
+    - os: linux
+      compiler: "clang38"
+      env: CXX=clang++-3.8 CXX_STD=c++14
+      addons: *clang38
+    - os: linux
+      compiler: "clang38"
+      env: CXX=clang++-3.8 CXXFLAGS="-DPROTOZERO_DO_NOT_USE_BARE_POINTER=1"
+      addons: *clang38
+    - os: linux
+      compiler: "gcc47"
+      env: CXX=g++-4.7
+      addons: *gcc47
+    - os: linux
+      compiler: "gcc48"
+      env: CXX=g++-4.8
+      addons: *gcc48
+    - os: linux
+      compiler: "gcc49"
+      env: CXX=g++-4.9 COVERAGE=gcov-4.9
+      addons: *gcc49
+    - os: linux
+      compiler: "gcc49"
+      env: CXX=g++-4.9 CXX_STD=c++14
+      addons: *gcc49
+    - os: linux
+      compiler: "gcc49"
+      env: CXX=g++-4.9 CXXFLAGS="-DPROTOZERO_DO_NOT_USE_BARE_POINTER=1"
+      addons: *gcc49
+    - os: linux
+      compiler: "gcc5"
+      env: CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0"
+      addons: *gcc5
+    - os: linux
+      compiler: "gcc5"
+      env: CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=1"
+      addons: *gcc5
 
-before_script:
-    - if [ -n "${INSTALL_CXX}" ]; then CXX=${INSTALL_CXX}; fi
-    - if [[ $(uname -s) == 'Darwin' ]]; then brew install protobuf; fi;
-    - make test
+before_install:
+ - echo ${CXX}
+ - if [[ $(uname -s) == 'Linux' ]]; then
+     export PYTHONPATH=$(pwd)/.local/lib/python2.7/site-packages;
+   else
+     brew install protobuf;
+     export PYTHONPATH=$(pwd)/.local/lib/python/site-packages;
+   fi
+ - if [ -n "${COVERAGE}" ]; then
+     PYTHONUSERBASE=$(pwd)/.local pip install --user cpp-coveralls;
+   fi
 
-script:
-    - if [ -n "${COVERAGE}" ]; then
-        make clean;
-        CXXFLAGS="--coverage" LDFLAGS="--coverage" make test;
-        ${COVERAGE} -lp test/*tests.o test/t/*/*test_cases.o;
-        pip install --user cpp-coveralls;
-        ~/.local/bin/coveralls --no-gcov -i include/protozero;
-      fi
+install:
+ - make test
 
+script:
+ - if [ -n "${COVERAGE}" ]; then
+    make clean;
+    CXXFLAGS="--coverage ${CXXFLAGS}" LDFLAGS="--coverage ${LDFLAGS}" make test;
+    ${COVERAGE} -lp test/*tests.o test/t/*/*test_cases.o;
+    ./.local/bin/cpp-coveralls --no-gcov -i include/protozero;
+   fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60bc829..db457d1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,12 +8,24 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 ### Added
 
+### Changed
+
+### Fixed
+
+
+## [1.3.0] - 2016-02-18
+
+### Added
+
 - Added `config.hpp` header which now includes all the macro magic to
   configure the library for different architectures etc.
+- New way to create repeated packed fields without using an iterator.
+- Add `rollback()` function to `pbf_writer` for "manual" rollback.
 
 ### Changed
 
-### Fixed
+- Various test and documentation cleanups.
+- Rename `pbf_types.hpp` to `types.hpp`.
 
 
 ## [1.2.3] - 2015-11-30
@@ -46,8 +58,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 ### Added
 
-- pbf_message and pbf_builder template classes wrapping pbf_reader
-  and pbf_writer, respectively. The new classes are the preferred
+- `pbf_message` and `pbf_builder` template classes wrapping `pbf_reader`
+  and `pbf_writer`, respectively. The new classes are the preferred
   interface now.
 
 ### Changed
@@ -63,7 +75,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - Make pbf reader and writer code endianess-aware.
 
 
-[unreleased]: https://github.com/osmcode/libosmium/compare/v1.2.3...HEAD
+[unreleased]: https://github.com/osmcode/libosmium/compare/v1.3.0...HEAD
+[1.3.0]: https://github.com/osmcode/libosmium/compare/v1.2.3...v1.3.0
 [1.2.3]: https://github.com/osmcode/libosmium/compare/v1.2.2...v1.2.3
 [1.2.2]: https://github.com/osmcode/libosmium/compare/v1.2.1...v1.2.2
 [1.2.1]: https://github.com/osmcode/libosmium/compare/v1.2.0...v1.2.1
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..9da5be3
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,17 @@
+# Contributing to protozero
+
+## Releasing
+
+To release a new protozero version:
+
+ - Make sure all tests are passing on travis and appveyor
+ - Update version number in
+   - include/protozero/version.hpp
+   - package.json
+ - Update CHANGELOG.md
+ - Create a new tag and push to github `git push --tags`
+ - Publish to npm:
+   - First run `make testpack` to see what files will be published
+   - If you see any unwanted files, then add them to `.npmignore`
+   - Then run: `npm publish`
+
diff --git a/Makefile b/Makefile
index c760e0a..83aeb39 100644
--- a/Makefile
+++ b/Makefile
@@ -39,8 +39,8 @@ PROTO_FILES_O := $(subst .proto,.pb.o,$(PROTO_FILES))
 
 HPP_FILES := include/protozero/byteswap.hpp \
              include/protozero/exception.hpp \
+             include/protozero/types.hpp \
              include/protozero/varint.hpp \
-             include/protozero/pbf_types.hpp \
              include/protozero/pbf_reader.hpp \
              include/protozero/pbf_writer.hpp
 
@@ -49,7 +49,7 @@ LDFLAGS_PROTOBUF := $(shell pkg-config protobuf --libs-only-L)
 
 all: ./test/tests test/writer_tests
 
-./test/t/%/test_cases.o: test/t/%/test_cases.cpp $(HPP_FILES)
+./test/t/%/test_cases.o: test/t/%/test_cases.cpp test/include/test.hpp test/include/scalar_access.hpp $(HPP_FILES)
 	$(CXX) -c -Iinclude -Itest/include $(CXXFLAGS) $(COMMON_FLAGS) $(DEBUG_FLAGS) $< -o $@
 
 ./test/tests.o: test/tests.cpp $(HPP_FILES)
@@ -79,8 +79,8 @@ test: all
 
 iwyu: $(HPP_FILES) test/tests.cpp test/writer_tests.cpp
 	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/exception.hpp || true
+	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/types.hpp || true
 	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/varint.hpp || true
-	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/pbf_types.hpp || true
 	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/pbf_reader.hpp || true
 	iwyu -Xiwyu -- -std=c++11 -Iinclude include/protozero/pbf_writer.hpp || true
 	iwyu -Xiwyu -- -std=c++11 -Iinclude -Itest/include test/tests.cpp || true
@@ -112,5 +112,11 @@ clean:
 	rm -f ./*.gcov
 	rm -fr doc/doxygen_sqlite3.db doc/html coverage
 
+testpack:
+	rm -f ./*tgz
+	npm pack
+	tar -ztvf *tgz
+	rm -f ./*tgz
+
 .PHONY: all test iwyu check doc
 
diff --git a/README.md b/README.md
index 09bde83..e575022 100644
--- a/README.md
+++ b/README.md
@@ -97,7 +97,7 @@ See `test/README.md` for more details about the test.
 
 You can also use `gyp` to build the reader tests:
 
-    gyp --depth=. --build=Release
+    gyp gyp/protozero.gyp --depth=. --build=Release
     ./out/Release/tests
 
 This will clobber the `Makefile` from the repository! Instead of `Release` you
diff --git a/build-appveyor.bat b/build-appveyor.bat
index 8cc7430..d21177c 100644
--- a/build-appveyor.bat
+++ b/build-appveyor.bat
@@ -43,13 +43,13 @@ FOR /F %%x in ('find test/ -name "testcase.proto"') DO "deps\protobuf\%platform%
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
 REM note windows requires --generator-output to be absolute
-python deps/gyp/gyp_main.py protozero.gyp --depth=. -f msvs -G msvs_version=2015
+python deps/gyp/gyp_main.py gyp/protozero.gyp --depth=. -f msvs -G msvs_version=2015
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
 SET MSBUILD_PLATFORM=%platform%
 IF /I "%MSBUILD_PLATFORM%" == "x86" set MSBUILD_PLATFORM=Win32
 
-msbuild protozero.sln ^
+msbuild gyp/protozero.sln ^
 /nologo ^
 /maxcpucount:%NUMBER_OF_PROCESSORS% ^
 /p:BuildInParellel=true ^
@@ -58,12 +58,12 @@ msbuild protozero.sln ^
 /consoleloggerparameters:Summary
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
-ECHO running %configuration%\%platform%\tests.exe ...
-%configuration%\%platform%\tests.exe
+ECHO running gyp\%configuration%\%platform%\tests.exe ...
+gyp\%configuration%\%platform%\tests.exe
 :: IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
-ECHO running %configuration%\%platform%\writer_tests.exe ...
-%configuration%\%platform%\writer_tests.exe
+ECHO running gyp\%configuration%\%platform%\writer_tests.exe ...
+gyp\%configuration%\%platform%\writer_tests.exe
 IF %ERRORLEVEL% NEQ 0 GOTO ERROR
 
 GOTO DONE
diff --git a/common.gypi b/gyp/common.gypi
similarity index 100%
rename from common.gypi
rename to gyp/common.gypi
diff --git a/gyp/protozero.gyp b/gyp/protozero.gyp
new file mode 100644
index 0000000..7d14efb
--- /dev/null
+++ b/gyp/protozero.gyp
@@ -0,0 +1,37 @@
+{
+  "includes": [
+      "common.gypi"
+  ],
+  "targets": [
+    {
+      "target_name": "tests",
+      "type": "executable",
+      "sources": [
+        "<!@(find ../test/ -name '*.cpp' ! -name 'writer_tests.cpp' ! -name 'writer_test_cases.cpp' ! -name 'testcase.cpp')"
+      ],
+      "include_dirs": [
+        "../include/",
+        "../test/include/"
+      ]
+    },
+    {
+      "target_name": "writer_tests",
+      "type": "executable",
+      "sources": [
+        "../test/writer_tests.cpp",
+        "<!@(find ../test/ -name 'writer_test_cases.cpp')",
+        "<!@(find ../include/protozero/ -name '*.hpp' ! -name 'config.hpp' ! -name 'pbf_builder.hpp' ! -name 'pbf_message.hpp' ! -name 'version.hpp')",
+        "<!@(find ../test/ -name '*.pb.cc')",
+      ],
+      "include_dirs": [
+        "../",
+        "../include/",
+        "../test/include/",
+        "../deps/protobuf/include"
+      ],
+      "libraries" : [
+        "../deps/protobuf/$(PlatformTarget)/$(Configuration)/libprotobuf-lite.lib"
+      ]
+    }
+  ]
+}
diff --git a/include/protozero/config.hpp b/include/protozero/config.hpp
index 4086994..8465c96 100644
--- a/include/protozero/config.hpp
+++ b/include/protozero/config.hpp
@@ -40,7 +40,9 @@ documentation.
 // in this case.
 #if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
 # if !defined(__arm__) && !defined(_M_ARM)
-#  define PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
+#  ifndef PROTOZERO_DO_NOT_USE_BARE_POINTER
+#   define PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
+#  endif
 # endif
 #endif
 
diff --git a/include/protozero/exception.hpp b/include/protozero/exception.hpp
index 1229f7d..5c7ab54 100644
--- a/include/protozero/exception.hpp
+++ b/include/protozero/exception.hpp
@@ -29,7 +29,7 @@ namespace protozero {
  */
 struct exception : std::exception {
     /// Returns the explanatory string.
-    const char *what() const noexcept { return "pbf exception"; }
+    const char *what() const noexcept override { return "pbf exception"; }
 };
 
 /**
@@ -38,7 +38,7 @@ struct exception : std::exception {
  */
 struct varint_too_long_exception : exception {
     /// Returns the explanatory string.
-    const char *what() const noexcept { return "varint too long exception"; }
+    const char *what() const noexcept override { return "varint too long exception"; }
 };
 
 /**
@@ -47,7 +47,7 @@ struct varint_too_long_exception : exception {
  */
 struct unknown_pbf_wire_type_exception : exception {
     /// Returns the explanatory string.
-    const char *what() const noexcept { return "unknown pbf field type exception"; }
+    const char *what() const noexcept override { return "unknown pbf field type exception"; }
 };
 
 /**
@@ -60,7 +60,7 @@ struct unknown_pbf_wire_type_exception : exception {
  */
 struct end_of_buffer_exception : exception {
     /// Returns the explanatory string.
-    const char *what() const noexcept { return "end of buffer exception"; }
+    const char *what() const noexcept override { return "end of buffer exception"; }
 };
 
 } // end namespace protozero
diff --git a/include/protozero/pbf_builder.hpp b/include/protozero/pbf_builder.hpp
index 063fa9c..548f4ce 100644
--- a/include/protozero/pbf_builder.hpp
+++ b/include/protozero/pbf_builder.hpp
@@ -18,7 +18,7 @@ documentation.
 
 #include <type_traits>
 
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 #include <protozero/pbf_writer.hpp>
 
 namespace protozero {
@@ -26,8 +26,10 @@ namespace protozero {
 /**
  * The pbf_builder is used to write PBF formatted messages into a buffer. It
  * is based on the pbf_writer class and has all the same methods. The
- * difference is that whereever the pbf_writer class takes an integer tag,
- * this template class takes a tag of the template type T.
+ * difference is that while the pbf_writer class takes an integer tag,
+ * this template class takes a tag of the template type T. The idea is that
+ * T will be an enumeration value and this helps reduce the possibility of
+ * programming errors.
  *
  * Almost all methods in this class can throw an std::bad_alloc exception if
  * the std::string used as a buffer wants to resize.
@@ -77,7 +79,7 @@ public:
 #undef PROTOZERO_WRITER_WRAP_ADD_SCALAR
 /// @endcond
 
-    inline void add_bytes(T tag, const char* value, size_t size) {
+    inline void add_bytes(T tag, const char* value, std::size_t size) {
         pbf_writer::add_bytes(pbf_tag_type(tag), value, size);
     }
 
@@ -85,7 +87,7 @@ public:
         pbf_writer::add_bytes(pbf_tag_type(tag), value);
     }
 
-    inline void add_string(T tag, const char* value, size_t size) {
+    inline void add_string(T tag, const char* value, std::size_t size) {
         pbf_writer::add_string(pbf_tag_type(tag), value, size);
     }
 
@@ -97,7 +99,7 @@ public:
         pbf_writer::add_string(pbf_tag_type(tag), value);
     }
 
-    inline void add_message(T tag, const char* value, size_t size) {
+    inline void add_message(T tag, const char* value, std::size_t size) {
         pbf_writer::add_message(pbf_tag_type(tag), value, size);
     }
 
diff --git a/include/protozero/pbf_message.hpp b/include/protozero/pbf_message.hpp
index 7fef06f..45f01c1 100644
--- a/include/protozero/pbf_message.hpp
+++ b/include/protozero/pbf_message.hpp
@@ -19,7 +19,7 @@ documentation.
 #include <type_traits>
 
 #include <protozero/pbf_reader.hpp>
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 
 namespace protozero {
 
diff --git a/include/protozero/pbf_reader.hpp b/include/protozero/pbf_reader.hpp
index aced901..58b3884 100644
--- a/include/protozero/pbf_reader.hpp
+++ b/include/protozero/pbf_reader.hpp
@@ -25,7 +25,7 @@ documentation.
 
 #include <protozero/config.hpp>
 #include <protozero/exception.hpp>
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 #include <protozero/varint.hpp>
 
 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
@@ -94,11 +94,12 @@ class pbf_reader {
 #ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
 
     template <typename T>
-    inline std::pair<const T*, const T*> packed_fixed() {
-        protozero_assert(tag() != 0 && "call next() before accessing field value");
-        auto len = get_len_and_skip();
-        protozero_assert(len % sizeof(T) == 0);
-        return std::make_pair(reinterpret_cast<const T*>(m_data-len), reinterpret_cast<const T*>(m_data));
+    using const_fixed_iterator = const T*;
+
+    template <typename T>
+    inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> create_fixed_iterator_pair(const char* first, const char* last) {
+        return std::make_pair(reinterpret_cast<const T*>(first),
+                              reinterpret_cast<const T*>(last));
     }
 
 #else
@@ -157,16 +158,21 @@ class pbf_reader {
     }; // class const_fixed_iterator
 
     template <typename T>
+    inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> create_fixed_iterator_pair(const char* first, const char* last) {
+        return std::make_pair(const_fixed_iterator<T>(first, last),
+                              const_fixed_iterator<T>(last, last));
+    }
+
+#endif
+
+    template <typename T>
     inline std::pair<const_fixed_iterator<T>, const_fixed_iterator<T>> packed_fixed() {
         protozero_assert(tag() != 0 && "call next() before accessing field value");
         auto len = get_len_and_skip();
         protozero_assert(len % sizeof(T) == 0);
-        return std::make_pair(const_fixed_iterator<T>(m_data-len, m_data),
-                              const_fixed_iterator<T>(m_data, m_data));
+        return create_fixed_iterator_pair<T>(m_data-len, m_data);
     }
 
-#endif
-
     template <typename T> inline T get_varint();
     template <typename T> inline T get_svarint();
 
@@ -187,7 +193,7 @@ public:
      *
      * @post There is no current field.
      */
-    inline pbf_reader(const char *data, size_t length) noexcept;
+    inline pbf_reader(const char *data, std::size_t length) noexcept;
 
     /**
      * Construct a pbf_reader message from a data pointer and a length. The pointer
@@ -198,7 +204,7 @@ public:
      *
      * @post There is no current field.
      */
-    inline pbf_reader(std::pair<const char *, size_t> data) noexcept;
+    inline pbf_reader(std::pair<const char *, std::size_t> data) noexcept;
 
     /**
      * Construct a pbf_reader message from a std::string. A pointer to the string
@@ -247,8 +253,8 @@ public:
      * buffer. Of course you have to know reasonably well what data to expect
      * and how it is encoded for this number to have any meaning.
      */
-    size_t length() const noexcept {
-        return size_t(m_end - m_data);
+    std::size_t length() const noexcept {
+        return std::size_t(m_end - m_data);
     }
 
     /**
@@ -832,14 +838,14 @@ public:
 
 }; // class pbf_reader
 
-pbf_reader::pbf_reader(const char *data, size_t length) noexcept
+pbf_reader::pbf_reader(const char *data, std::size_t length) noexcept
     : m_data(data),
       m_end(data + length),
       m_wire_type(pbf_wire_type::unknown),
       m_tag(0) {
 }
 
-pbf_reader::pbf_reader(std::pair<const char *, size_t> data) noexcept
+pbf_reader::pbf_reader(std::pair<const char *, std::size_t> data) noexcept
     : m_data(data.first),
       m_end(data.first + data.second),
       m_wire_type(pbf_wire_type::unknown),
@@ -935,7 +941,7 @@ void pbf_reader::skip() {
             skip_bytes(4);
             break;
         default:
-            throw unknown_pbf_wire_type_exception();
+            protozero_assert(false && "can not be here because next() should have thrown already");
     }
 }
 
diff --git a/include/protozero/pbf_writer.hpp b/include/protozero/pbf_writer.hpp
index 2b78cb8..422e147 100644
--- a/include/protozero/pbf_writer.hpp
+++ b/include/protozero/pbf_writer.hpp
@@ -24,7 +24,7 @@ documentation.
 #include <string>
 
 #include <protozero/config.hpp>
-#include <protozero/pbf_types.hpp>
+#include <protozero/types.hpp>
 #include <protozero/varint.hpp>
 
 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
@@ -33,6 +33,14 @@ documentation.
 
 namespace protozero {
 
+namespace detail {
+
+    template <typename T> class packed_field_varint;
+    template <typename T> class packed_field_svarint;
+    template <typename T> class packed_field_fixed;
+
+} // end namespace detail
+
 /**
  * The pbf_writer is used to write PBF formatted messages into a buffer.
  *
@@ -41,9 +49,24 @@ namespace protozero {
  */
 class pbf_writer {
 
+    // A pointer to a string buffer holding the data already written to the
+    // PBF message. For default constructed writers or writers that have been
+    // rolled back, this is a nullptr.
     std::string* m_data;
+
+    // A pointer to a parent writer object if this is a submessage. If this
+    // is a top-level writer, it is a nullptr.
     pbf_writer* m_parent_writer;
-    size_t m_pos = 0;
+
+    // This is usually 0. If there is an open submessage, this is set in the
+    // parent to the rollback position, ie. the last position before the
+    // submessage was started. This is the position where the header of the
+    // submessage starts.
+    std::size_t m_rollback_pos = 0;
+
+    // This is usually 0. If there is an open submessage, this is set in the
+    // parent to the position where the data of the submessage is written to.
+    std::size_t m_pos = 0;
 
     inline void add_varint(uint64_t value) {
         protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
@@ -94,7 +117,9 @@ class pbf_writer {
             return;
         }
 
-        add_length_varint(tag, sizeof(T) * pbf_length_type(std::distance(first, last)));
+        auto length = std::distance(first, last);
+        add_length_varint(tag, sizeof(T) * pbf_length_type(length));
+        reserve(sizeof(T) * std::size_t(length));
 
         while (first != last) {
             add_fixed<T>(*first++);
@@ -132,16 +157,37 @@ class pbf_writer {
     // and a varint needs 8 bit for every 7 bit.
     static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
 
-    inline void open_submessage(pbf_tag_type tag) {
+    // If m_rollpack_pos is set to this special value, it means that when
+    // the submessage is closed, nothing needs to be done, because the length
+    // of the submessage has already been written correctly.
+    static const std::size_t size_is_known = std::numeric_limits<std::size_t>::max();
+
+    inline void open_submessage(pbf_tag_type tag, std::size_t size) {
         protozero_assert(m_pos == 0);
         protozero_assert(m_data);
-        add_field(tag, pbf_wire_type::length_delimited);
-        m_data->append(size_t(reserve_bytes), '\0');
+        if (size == 0) {
+            m_rollback_pos = m_data->size();
+            add_field(tag, pbf_wire_type::length_delimited);
+            m_data->append(std::size_t(reserve_bytes), '\0');
+        } else {
+            m_rollback_pos = size_is_known;
+            add_length_varint(tag, pbf_length_type(size));
+            reserve(size);
+        }
         m_pos = m_data->size();
     }
 
-    inline void close_submessage() {
+    inline void rollback_submessage() {
         protozero_assert(m_pos != 0);
+        protozero_assert(m_rollback_pos != size_is_known);
+        protozero_assert(m_data);
+        m_data->resize(m_rollback_pos);
+        m_pos = 0;
+    }
+
+    inline void commit_submessage() {
+        protozero_assert(m_pos != 0);
+        protozero_assert(m_rollback_pos != size_is_known);
         protozero_assert(m_data);
         auto length = pbf_length_type(m_data->size() - m_pos);
 
@@ -152,6 +198,18 @@ class pbf_writer {
         m_pos = 0;
     }
 
+    inline void close_submessage() {
+        protozero_assert(m_data);
+        if (m_pos == 0 || m_rollback_pos == size_is_known) {
+            return;
+        }
+        if (m_data->size() - m_pos == 0) {
+            rollback_submessage();
+        } else {
+            commit_submessage();
+        }
+    }
+
     inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
         add_field(tag, pbf_wire_type::length_delimited);
         add_varint(length);
@@ -161,7 +219,8 @@ public:
 
     /**
      * Create a writer using the given string as a data store. The pbf_writer
-     * stores a reference to that string and adds all data to it.
+     * stores a reference to that string and adds all data to it. The string
+     * doesn't have to be empty. The pbf_writer will just append data.
      */
     inline explicit pbf_writer(std::string& data) noexcept :
         m_data(&data),
@@ -185,12 +244,15 @@ public:
      *
      * @param parent_writer The pbf_writer
      * @param tag Tag (field number) of the field that will be written
+     * @param size Optional size of the submessage in bytes (use 0 for unknown).
+     *        Setting this allows some optimizations but is only possible in
+     *        a few very specific cases.
      */
-    inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag) :
+    inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
         m_data(parent_writer.m_data),
         m_parent_writer(&parent_writer),
         m_pos(0) {
-        m_parent_writer->open_submessage(tag);
+        m_parent_writer->open_submessage(tag, size);
     }
 
     /// A pbf_writer object can be copied
@@ -211,6 +273,26 @@ public:
         }
     }
 
+    /**
+     * Reserve size bytes in the underlying message store in addition to
+     * whatever the message store already holds. So unlike
+     * the `std::string::reserve()` method this is not an absolute size,
+     * but additional memory that should be reserved.
+     *
+     * @param size Number of bytes to reserve in underlying message store.
+     */
+    void reserve(std::size_t size) {
+        protozero_assert(m_data);
+        m_data->reserve(m_data->size() + size);
+    }
+
+    inline void rollback() {
+        protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent");
+        protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage");
+        m_parent_writer->rollback_submessage();
+        m_data = nullptr;
+    }
+
     ///@{
     /**
      * @name Scalar field writer functions
@@ -372,7 +454,7 @@ public:
      * @param value Pointer to value to be written
      * @param size Number of bytes to be written
      */
-    inline void add_bytes(pbf_tag_type tag, const char* value, size_t size) {
+    inline void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
         protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
         protozero_assert(m_data);
         protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
@@ -397,7 +479,7 @@ public:
      * @param value Pointer to value to be written
      * @param size Number of bytes to be written
      */
-    inline void add_string(pbf_tag_type tag, const char* value, size_t size) {
+    inline void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
         add_bytes(tag, value, size);
     }
 
@@ -429,7 +511,7 @@ public:
      * @param value Pointer to message to be written
      * @param size Length of the message
      */
-    inline void add_message(pbf_tag_type tag, const char* value, size_t size) {
+    inline void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
         add_bytes(tag, value, size);
     }
 
@@ -654,8 +736,102 @@ public:
 
     ///@}
 
+    template <typename T> friend class detail::packed_field_varint;
+    template <typename T> friend class detail::packed_field_svarint;
+    template <typename T> friend class detail::packed_field_fixed;
+
 }; // class pbf_writer
 
+namespace detail {
+
+    class packed_field {
+
+    protected:
+
+        pbf_writer m_writer;
+
+    public:
+
+        packed_field(pbf_writer& parent_writer, pbf_tag_type tag) :
+            m_writer(parent_writer, tag) {
+        }
+
+        packed_field(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) :
+            m_writer(parent_writer, tag, size) {
+        }
+
+        void rollback() {
+            m_writer.rollback();
+        }
+
+    }; // class packed_field
+
+    template <typename T>
+    class packed_field_fixed : public packed_field {
+
+    public:
+
+        packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag) :
+            packed_field(parent_writer, tag) {
+        }
+
+        packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) :
+            packed_field(parent_writer, tag, size * sizeof(T)) {
+        }
+
+        void add_element(T value) {
+            m_writer.add_fixed<T>(value);
+        }
+
+    }; // class packed_field_fixed
+
+    template <typename T>
+    class packed_field_varint : public packed_field {
+
+    public:
+
+        packed_field_varint(pbf_writer& parent_writer, pbf_tag_type tag) :
+            packed_field(parent_writer, tag) {
+        }
+
+        void add_element(T value) {
+            m_writer.add_varint(uint64_t(value));
+        }
+
+    }; // class packed_field_varint
+
+    template <typename T>
+    class packed_field_svarint : public packed_field {
+
+    public:
+
+        packed_field_svarint(pbf_writer& parent_writer, pbf_tag_type tag) :
+            packed_field(parent_writer, tag) {
+        }
+
+        void add_element(T value) {
+            m_writer.add_varint(encode_zigzag64(value));
+        }
+
+    }; // class packed_field_svarint
+
+} // end namespace detail
+
+using packed_field_bool     = detail::packed_field_varint<bool>;
+using packed_field_enum     = detail::packed_field_varint<int32_t>;
+using packed_field_int32    = detail::packed_field_varint<int32_t>;
+using packed_field_sint32   = detail::packed_field_svarint<int32_t>;
+using packed_field_uint32   = detail::packed_field_varint<uint32_t>;
+using packed_field_int64    = detail::packed_field_varint<int64_t>;
+using packed_field_sint64   = detail::packed_field_svarint<int64_t>;
+using packed_field_uint64   = detail::packed_field_varint<uint64_t>;
+using packed_field_fixed32  = detail::packed_field_fixed<uint32_t>;
+using packed_field_sfixed32 = detail::packed_field_fixed<int32_t>;
+using packed_field_fixed64  = detail::packed_field_fixed<uint64_t>;
+using packed_field_sfixed64 = detail::packed_field_fixed<int64_t>;
+using packed_field_float    = detail::packed_field_fixed<float>;
+using packed_field_double   = detail::packed_field_fixed<double>;
+
 } // end namespace protozero
 
 #endif // PROTOZERO_PBF_WRITER_HPP
diff --git a/include/protozero/pbf_types.hpp b/include/protozero/types.hpp
similarity index 91%
rename from include/protozero/pbf_types.hpp
rename to include/protozero/types.hpp
index 9f38584..6856b3d 100644
--- a/include/protozero/pbf_types.hpp
+++ b/include/protozero/types.hpp
@@ -1,5 +1,5 @@
-#ifndef PROTOZERO_PBF_TYPES_HPP
-#define PROTOZERO_PBF_TYPES_HPP
+#ifndef PROTOZERO_TYPES_HPP
+#define PROTOZERO_TYPES_HPP
 
 /*****************************************************************************
 
@@ -11,7 +11,7 @@ documentation.
 *****************************************************************************/
 
 /**
- * @file pbf_types.hpp
+ * @file types.hpp
  *
  * @brief Contains the declaration of low-level types used in the pbf format.
  */
@@ -46,4 +46,4 @@ namespace protozero {
 
 } // end namespace protozero
 
-#endif // PROTOZERO_PBF_TYPES_HPP
+#endif // PROTOZERO_TYPES_HPP
diff --git a/include/protozero/varint.hpp b/include/protozero/varint.hpp
index 27536fd..4242df9 100644
--- a/include/protozero/varint.hpp
+++ b/include/protozero/varint.hpp
@@ -25,13 +25,13 @@ namespace protozero {
 /**
  * The maximum length of a 64bit varint.
  */
-const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
+constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
 
 // from https://github.com/facebook/folly/blob/master/folly/Varint.h
 /**
  * Decode a 64bit varint.
  *
- * String exception guarantee: if there is an exception the data pointer will
+ * Strong exception guarantee: if there is an exception the data pointer will
  * not be changed.
  *
  * @param[in,out] data Pointer to pointer to the input data. After the function
diff --git a/include/protozero/version.hpp b/include/protozero/version.hpp
index 4f129ac..7b60e2e 100644
--- a/include/protozero/version.hpp
+++ b/include/protozero/version.hpp
@@ -11,12 +11,12 @@ documentation.
 *****************************************************************************/
 
 #define PROTOZERO_VERSION_MAJOR 1
-#define PROTOZERO_VERSION_MINOR 2
-#define PROTOZERO_VERSION_PATCH 3
+#define PROTOZERO_VERSION_MINOR 3
+#define PROTOZERO_VERSION_PATCH 0
 
 #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH)
 
-#define PROTOZERO_VERSION_STRING "1.2.3"
+#define PROTOZERO_VERSION_STRING "1.3.0"
 
 
 #endif // PROTOZERO_VERSION_HPP
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..41806a9
--- /dev/null
+++ b/package.json
@@ -0,0 +1,10 @@
+{
+    "name": "protozero",
+    "version": "1.3.0",
+    "description": "Minimalist protocol buffer decoder and encoder in C++",
+    "main": "./package.json",
+    "repository"   :  {
+      "type" : "git",
+      "url"  : "git://github.com/mapbox/protozero.git"
+    }
+}
diff --git a/protozero.gyp b/protozero.gyp
deleted file mode 100644
index c2b4f36..0000000
--- a/protozero.gyp
+++ /dev/null
@@ -1,37 +0,0 @@
-{
-  "includes": [
-      "common.gypi"
-  ],
-  "targets": [
-    {
-      "target_name": "tests",
-      "type": "executable",
-      "sources": [
-        "<!@(find ./test/ -name '*.cpp' ! -name 'writer_tests.cpp' ! -name 'writer_test_cases.cpp' ! -name 'testcase.cpp')"
-      ],
-      "include_dirs": [
-        "./include/",
-        "./test/include/"
-      ]
-    },
-    {
-      "target_name": "writer_tests",
-      "type": "executable",
-      "sources": [
-        "test/writer_tests.cpp",
-        "<!@(find ./test/ -name 'writer_test_cases.cpp')",
-        "<!@(find include/protozero/ -name '*.hpp' ! -name 'config.hpp' ! -name 'pbf_builder.hpp' ! -name 'pbf_message.hpp' ! -name 'version.hpp')",
-        "<!@(find ./test/ -name '*.pb.cc')",
-      ],
-      "include_dirs": [
-        ".",
-        "./include/",
-        "./test/include/",
-        "./deps/protobuf/include"
-      ],
-      "libraries" : [
-        "./deps/protobuf/$(PlatformTarget)/$(Configuration)/libprotobuf-lite.lib"
-      ]
-    }
-  ]
-}
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
index de61226..f042149 100644
--- a/test/include/catch.hpp
+++ b/test/include/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.2.1
- *  Generated: 2015-06-30 18:23:27.961086
+ *  Catch v1.3.3
+ *  Generated: 2016-01-22 07:51:36.661106
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -21,8 +21,6 @@
 
 // #included from: internal/catch_suppress_warnings.h
 
-#define TWOBLUECUBES_CATCH_SUPPRESS_WARNINGS_H_INCLUDED
-
 #ifdef __clang__
 #   ifdef __ICC // icpc defines the __clang__ macro
 #       pragma warning(push)
@@ -37,6 +35,7 @@
 #       pragma clang diagnostic ignored "-Wc++98-compat"
 #       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
 #       pragma clang diagnostic ignored "-Wswitch-enum"
+#       pragma clang diagnostic ignored "-Wcovered-switch-default"
 #    endif
 #elif defined __GNUC__
 #    pragma GCC diagnostic ignored "-Wvariadic-macros"
@@ -44,7 +43,6 @@
 #    pragma GCC diagnostic push
 #    pragma GCC diagnostic ignored "-Wpadded"
 #endif
-
 #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
 #  define CATCH_IMPL
 #endif
@@ -84,11 +82,19 @@
 // CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
 // CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
 // CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
 
 // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
 
 // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
 
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
 // In general each macro has a _NO_<feature name> form
 // (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
 // Many features, at point of detection, define an _INTERNAL_ macro, so they
@@ -130,10 +136,13 @@
 // GCC
 #ifdef __GNUC__
 
-#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) )
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
 #   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
 #endif
 
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
 #endif // __GNUC__
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -142,6 +151,7 @@
 
 #if (_MSC_VER >= 1600)
 #   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
 #endif
 
 #if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
@@ -151,6 +161,8 @@
 
 #endif // _MSC_VER
 
+////////////////////////////////////////////////////////////////////////////////
+
 // Use variadic macros if the compiler supports them
 #if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
     ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
@@ -165,7 +177,7 @@
 // C++ language feature support
 
 // catch all support for C++11
-#if (__cplusplus >= 201103L)
+#if defined(__cplusplus) && __cplusplus >= 201103L
 
 #  define CATCH_CPP11_OR_GREATER
 
@@ -193,6 +205,17 @@
 #    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
 #  endif
 
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+#    define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#    define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+#  endif
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#  endif
+
 #endif // __cplusplus >= 201103L
 
 // Now set the actual defines based on the above + anything the user has configured
@@ -212,7 +235,16 @@
 #   define CATCH_CONFIG_CPP11_TUPLE
 #endif
 #if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
-#define CATCH_CONFIG_VARIADIC_MACROS
+#   define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_UNIQUE_PTR
 #endif
 
 // noexcept support:
@@ -224,8 +256,36 @@
 #  define CATCH_NOEXCEPT_IS(x)
 #endif
 
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+#   define CATCH_NULL nullptr
+#else
+#   define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+#   define CATCH_OVERRIDE override
+#else
+#   define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+#   define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
 namespace Catch {
 
+    struct IConfig;
+
+    struct CaseSensitive { enum Choice {
+        Yes,
+        No
+    }; };
+
     class NonCopyable {
 #ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
         NonCopyable( NonCopyable const& )              = delete;
@@ -312,6 +372,9 @@ namespace Catch {
 
     void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
 
+    void seedRng( IConfig const& config );
+    unsigned int rngSeed();
+
     // Use this in variadic streaming macros to allow
     //    >> +StreamEndStop
     // as well as
@@ -397,7 +460,7 @@ namespace Catch {
     template<typename T>
     class Ptr {
     public:
-        Ptr() : m_p( NULL ){}
+        Ptr() : m_p( CATCH_NULL ){}
         Ptr( T* p ) : m_p( p ){
             if( m_p )
                 m_p->addRef();
@@ -413,7 +476,7 @@ namespace Catch {
         void reset() {
             if( m_p )
                 m_p->release();
-            m_p = NULL;
+            m_p = CATCH_NULL;
         }
         Ptr& operator = ( T* p ){
             Ptr temp( p );
@@ -426,12 +489,11 @@ namespace Catch {
             return *this;
         }
         void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
-        T* get() { return m_p; }
-        const T* get() const{ return m_p; }
+        T* get() const{ return m_p; }
         T& operator*() const { return *m_p; }
         T* operator->() const { return m_p; }
-        bool operator !() const { return m_p == NULL; }
-        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); }
+        bool operator !() const { return m_p == CATCH_NULL; }
+        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
 
     private:
         T* m_p;
@@ -528,9 +590,13 @@ namespace Catch {
     struct ITestCaseRegistry {
         virtual ~ITestCaseRegistry();
         virtual std::vector<TestCase> const& getAllTests() const = 0;
-        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const = 0;
-
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
     };
+
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
 }
 
 namespace Catch {
@@ -563,27 +629,32 @@ struct NameAndDesc {
     const char* description;
 };
 
+void registerTestCase
+    (   ITestCase* testCase,
+        char const* className,
+        NameAndDesc const& nameAndDesc,
+        SourceLineInfo const& lineInfo );
+
 struct AutoReg {
 
-    AutoReg(    TestFunction function,
-                SourceLineInfo const& lineInfo,
-                NameAndDesc const& nameAndDesc );
+    AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc );
 
     template<typename C>
-    AutoReg(    void (C::*method)(),
-                char const* className,
-                NameAndDesc const& nameAndDesc,
-                SourceLineInfo const& lineInfo ) {
-        registerTestCase(   new MethodTestCase<C>( method ),
-                            className,
-                            nameAndDesc,
-                            lineInfo );
-    }
-
-    void registerTestCase(  ITestCase* testCase,
-                            char const* className,
-                            NameAndDesc const& nameAndDesc,
-                            SourceLineInfo const& lineInfo );
+    AutoReg
+        (   void (C::*method)(),
+            char const* className,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
+
+        registerTestCase
+            (   new MethodTestCase<C>( method ),
+                className,
+                nameAndDesc,
+                lineInfo );
+    }
 
     ~AutoReg();
 
@@ -592,6 +663,11 @@ private:
     void operator= ( AutoReg const& );
 };
 
+void registerTestCaseFunction
+    (   TestFunction function,
+        SourceLineInfo const& lineInfo,
+        NameAndDesc const& nameAndDesc );
+
 } // end namespace Catch
 
 #ifdef CATCH_CONFIG_VARIADIC_MACROS
@@ -615,6 +691,10 @@ private:
         } \
         void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
 
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) );
+
 #else
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
@@ -636,6 +716,9 @@ private:
         } \
         void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
 
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) );
 #endif
 
 // #included from: internal/catch_capture.hpp
@@ -758,147 +841,468 @@ namespace Catch {
 
 } // end namespace Catch
 
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
 namespace Catch {
+namespace Matchers {
+    namespace Impl {
 
-    struct TestFailureException{};
+    namespace Generic {
+        template<typename ExpressionT> class AllOf;
+        template<typename ExpressionT> class AnyOf;
+        template<typename ExpressionT> class Not;
+    }
 
-    template<typename T> class ExpressionLhs;
+    template<typename ExpressionT>
+    struct Matcher : SharedImpl<IShared>
+    {
+        typedef ExpressionT ExpressionType;
 
-    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+        virtual ~Matcher() {}
+        virtual Ptr<Matcher> clone() const = 0;
+        virtual bool match( ExpressionT const& expr ) const = 0;
+        virtual std::string toString() const = 0;
 
-    struct CopyableStream {
-        CopyableStream() {}
-        CopyableStream( CopyableStream const& other ) {
-            oss << other.oss.str();
-        }
-        CopyableStream& operator=( CopyableStream const& other ) {
-            oss.str("");
-            oss << other.oss.str();
-            return *this;
+        Generic::AllOf<ExpressionT> operator && ( Matcher<ExpressionT> const& other ) const;
+        Generic::AnyOf<ExpressionT> operator || ( Matcher<ExpressionT> const& other ) const;
+        Generic::Not<ExpressionT> operator ! () const;
+    };
+
+    template<typename DerivedT, typename ExpressionT>
+    struct MatcherImpl : Matcher<ExpressionT> {
+
+        virtual Ptr<Matcher<ExpressionT> > clone() const {
+            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
         }
-        std::ostringstream oss;
     };
 
-    class ResultBuilder {
-    public:
-        ResultBuilder(  char const* macroName,
-                        SourceLineInfo const& lineInfo,
-                        char const* capturedExpression,
-                        ResultDisposition::Flags resultDisposition );
+    namespace Generic {
+        template<typename ExpressionT>
+        class Not : public MatcherImpl<Not<ExpressionT>, ExpressionT> {
+        public:
+            explicit Not( Matcher<ExpressionT> const& matcher ) : m_matcher(matcher.clone()) {}
+            Not( Not const& other ) : m_matcher( other.m_matcher ) {}
 
-        template<typename T>
-        ExpressionLhs<T const&> operator <= ( T const& operand );
-        ExpressionLhs<bool> operator <= ( bool value );
+            virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE {
+                return !m_matcher->match( expr );
+            }
 
-        template<typename T>
-        ResultBuilder& operator << ( T const& value ) {
-            m_stream.oss << value;
-            return *this;
-        }
+            virtual std::string toString() const CATCH_OVERRIDE {
+                return "not " + m_matcher->toString();
+            }
+        private:
+            Ptr< Matcher<ExpressionT> > m_matcher;
+        };
 
-        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
-        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+        template<typename ExpressionT>
+        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
+        public:
 
-        ResultBuilder& setResultType( ResultWas::OfType result );
-        ResultBuilder& setResultType( bool result );
-        ResultBuilder& setLhs( std::string const& lhs );
-        ResultBuilder& setRhs( std::string const& rhs );
-        ResultBuilder& setOp( std::string const& op );
+            AllOf() {}
+            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
 
-        void endExpression();
+            AllOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( !m_matchers[i]->match( expr ) )
+                        return false;
+                return true;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " and ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
 
-        std::string reconstructExpression() const;
-        AssertionResult build() const;
+            AllOf operator && ( Matcher<ExpressionT> const& other ) const {
+                AllOf allOfExpr( *this );
+                allOfExpr.add( other );
+                return allOfExpr;
+            }
 
-        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
-        void captureResult( ResultWas::OfType resultType );
-        void captureExpression();
-        void react();
-        bool shouldDebugBreak() const;
-        bool allowThrows() const;
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
 
-    private:
-        AssertionInfo m_assertionInfo;
-        AssertionResultData m_data;
-        struct ExprComponents {
-            ExprComponents() : testFalse( false ) {}
-            bool testFalse;
-            std::string lhs, rhs, op;
-        } m_exprComponents;
-        CopyableStream m_stream;
+        template<typename ExpressionT>
+        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
+        public:
 
-        bool m_shouldDebugBreak;
-        bool m_shouldThrow;
-    };
+            AnyOf() {}
+            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
 
-} // namespace Catch
+            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
+                m_matchers.push_back( matcher.clone() );
+                return *this;
+            }
+            virtual bool match( ExpressionT const& expr ) const
+            {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i )
+                    if( m_matchers[i]->match( expr ) )
+                        return true;
+                return false;
+            }
+            virtual std::string toString() const {
+                std::ostringstream oss;
+                oss << "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        oss << " or ";
+                    oss << m_matchers[i]->toString();
+                }
+                oss << " )";
+                return oss.str();
+            }
 
-// Include after due to circular dependency:
-// #included from: catch_expression_lhs.hpp
-#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+            AnyOf operator || ( Matcher<ExpressionT> const& other ) const {
+                AnyOf anyOfExpr( *this );
+                anyOfExpr.add( other );
+                return anyOfExpr;
+            }
 
-// #included from: catch_evaluate.hpp
-#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+        private:
+            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
+        };
 
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
-#endif
+    } // namespace Generic
 
-#include <cstddef>
+    template<typename ExpressionT>
+    Generic::AllOf<ExpressionT> Matcher<ExpressionT>::operator && ( Matcher<ExpressionT> const& other ) const {
+        Generic::AllOf<ExpressionT> allOfExpr;
+        allOfExpr.add( *this );
+        allOfExpr.add( other );
+        return allOfExpr;
+    }
 
-namespace Catch {
-namespace Internal {
+    template<typename ExpressionT>
+    Generic::AnyOf<ExpressionT> Matcher<ExpressionT>::operator || ( Matcher<ExpressionT> const& other ) const {
+        Generic::AnyOf<ExpressionT> anyOfExpr;
+        anyOfExpr.add( *this );
+        anyOfExpr.add( other );
+        return anyOfExpr;
+    }
 
-    enum Operator {
-        IsEqualTo,
-        IsNotEqualTo,
-        IsLessThan,
-        IsGreaterThan,
-        IsLessThanOrEqualTo,
-        IsGreaterThanOrEqualTo
-    };
+    template<typename ExpressionT>
+    Generic::Not<ExpressionT> Matcher<ExpressionT>::operator ! () const {
+        return Generic::Not<ExpressionT>( *this );
+    }
 
-    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
-    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
-    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
-    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
-    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
-    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
-    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+    namespace StdString {
 
-    template<typename T>
-    inline T& opCast(T const& t) { return const_cast<T&>(t); }
+        inline std::string makeString( std::string const& str ) { return str; }
+        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
 
-// nullptr_t support based on pull request #154 from Konstantin Baumann
-#ifdef CATCH_CONFIG_CPP11_NULLPTR
-    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
-#endif // CATCH_CONFIG_CPP11_NULLPTR
+        struct CasedString
+        {
+            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+            :   m_caseSensitivity( caseSensitivity ),
+                m_str( adjustString( str ) )
+            {}
+            std::string adjustString( std::string const& str ) const {
+                return m_caseSensitivity == CaseSensitive::No
+                    ? toLower( str )
+                    : str;
 
-    // So the compare overloads can be operator agnostic we convey the operator as a template
-    // enum, which is used to specialise an Evaluator for doing the comparison.
-    template<typename T1, typename T2, Operator Op>
-    class Evaluator{};
+            }
+            std::string toStringSuffix() const
+            {
+                return m_caseSensitivity == CaseSensitive::No
+                    ? " (case insensitive)"
+                    : "";
+            }
+            CaseSensitive::Choice m_caseSensitivity;
+            std::string m_str;
+        };
+
+        struct Equals : MatcherImpl<Equals, std::string> {
+            Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            :   m_data( str, caseSensitivity )
+            {}
+            Equals( Equals const& other ) : m_data( other.m_data ){}
+
+            virtual ~Equals();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_data.m_str == m_data.adjustString( expr );;
+            }
+            virtual std::string toString() const {
+                return "equals: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct Contains : MatcherImpl<Contains, std::string> {
+            Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+            Contains( Contains const& other ) : m_data( other.m_data ){}
+
+            virtual ~Contains();
+
+            virtual bool match( std::string const& expr ) const {
+                return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos;
+            }
+            virtual std::string toString() const {
+                return "contains: \"" + m_data.m_str  + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct StartsWith : MatcherImpl<StartsWith, std::string> {
+            StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+
+            StartsWith( StartsWith const& other ) : m_data( other.m_data ){}
+
+            virtual ~StartsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return startsWith( m_data.adjustString( expr ), m_data.m_str );
+            }
+            virtual std::string toString() const {
+                return "starts with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+
+        struct EndsWith : MatcherImpl<EndsWith, std::string> {
+            EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes )
+            : m_data( substr, caseSensitivity ){}
+            EndsWith( EndsWith const& other ) : m_data( other.m_data ){}
+
+            virtual ~EndsWith();
+
+            virtual bool match( std::string const& expr ) const {
+                return endsWith( m_data.adjustString( expr ), m_data.m_str );
+            }
+            virtual std::string toString() const {
+                return "ends with: \"" + m_data.m_str + "\"" + m_data.toStringSuffix();
+            }
+
+            CasedString m_data;
+        };
+    } // namespace StdString
+    } // namespace Impl
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    template<typename ExpressionT>
+    inline Impl::Generic::Not<ExpressionT> Not( Impl::Matcher<ExpressionT> const& m ) {
+        return Impl::Generic::Not<ExpressionT>( m );
+    }
+
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
+    }
+    template<typename ExpressionT>
+    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
+                                                    Impl::Matcher<ExpressionT> const& m2,
+                                                    Impl::Matcher<ExpressionT> const& m3 ) {
+        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
+    }
+
+    inline Impl::StdString::Equals      Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Equals( str, caseSensitivity );
+    }
+    inline Impl::StdString::Equals      Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity );
+    }
+    inline Impl::StdString::Contains    Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Contains( substr, caseSensitivity );
+    }
+    inline Impl::StdString::Contains    Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) {
+        return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
+        return Impl::StdString::StartsWith( substr );
+    }
+    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
+        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
+        return Impl::StdString::EndsWith( substr );
+    }
+    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
+        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
+    }
+
+} // namespace Matchers
+
+using namespace Matchers;
+
+} // namespace Catch
+
+namespace Catch {
+
+    struct TestFailureException{};
+
+    template<typename T> class ExpressionLhs;
+
+    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+    struct CopyableStream {
+        CopyableStream() {}
+        CopyableStream( CopyableStream const& other ) {
+            oss << other.oss.str();
+        }
+        CopyableStream& operator=( CopyableStream const& other ) {
+            oss.str("");
+            oss << other.oss.str();
+            return *this;
+        }
+        std::ostringstream oss;
+    };
+
+    class ResultBuilder {
+    public:
+        ResultBuilder(  char const* macroName,
+                        SourceLineInfo const& lineInfo,
+                        char const* capturedExpression,
+                        ResultDisposition::Flags resultDisposition,
+                        char const* secondArg = "" );
+
+        template<typename T>
+        ExpressionLhs<T const&> operator <= ( T const& operand );
+        ExpressionLhs<bool> operator <= ( bool value );
+
+        template<typename T>
+        ResultBuilder& operator << ( T const& value ) {
+            m_stream.oss << value;
+            return *this;
+        }
+
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& );
+        template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& );
+
+        ResultBuilder& setResultType( ResultWas::OfType result );
+        ResultBuilder& setResultType( bool result );
+        ResultBuilder& setLhs( std::string const& lhs );
+        ResultBuilder& setRhs( std::string const& rhs );
+        ResultBuilder& setOp( std::string const& op );
+
+        void endExpression();
+
+        std::string reconstructExpression() const;
+        AssertionResult build() const;
+
+        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+        void captureResult( ResultWas::OfType resultType );
+        void captureExpression();
+        void captureExpectedException( std::string const& expectedMessage );
+        void captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher );
+        void handleResult( AssertionResult const& result );
+        void react();
+        bool shouldDebugBreak() const;
+        bool allowThrows() const;
+
+    private:
+        AssertionInfo m_assertionInfo;
+        AssertionResultData m_data;
+        struct ExprComponents {
+            ExprComponents() : testFalse( false ) {}
+            bool testFalse;
+            std::string lhs, rhs, op;
+        } m_exprComponents;
+        CopyableStream m_stream;
+
+        bool m_shouldDebugBreak;
+        bool m_shouldThrow;
+    };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+    enum Operator {
+        IsEqualTo,
+        IsNotEqualTo,
+        IsLessThan,
+        IsGreaterThan,
+        IsLessThanOrEqualTo,
+        IsGreaterThanOrEqualTo
+    };
+
+    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
+    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
+    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
+    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
+    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
+    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
+    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+    template<typename T>
+    inline T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+    // So the compare overloads can be operator agnostic we convey the operator as a template
+    // enum, which is used to specialise an Evaluator for doing the comparison.
+    template<typename T1, typename T2, Operator Op>
+    class Evaluator{};
 
     template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs) {
-            return opCast( lhs ) ==  opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsNotEqualTo> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) != opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
-    struct Evaluator<T1, T2, IsLessThan> {
-        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) < opCast( rhs );
-        }
-    };
-    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs) {
+            return opCast( lhs ) ==  opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsNotEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) != opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return opCast( lhs ) < opCast( rhs );
+        }
+    };
+    template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsGreaterThan> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
             return opCast( lhs ) > opCast( rhs );
@@ -991,13 +1395,51 @@ namespace Internal {
         return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
     }
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+    // long long to unsigned X
+    template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // unsigned long long to X
+    template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+
+    // pointer to long long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
     // pointer to nullptr_t (when comparing against nullptr)
     template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
-        return Evaluator<T*, T*, Op>::evaluate( NULL, rhs );
+        return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
     }
     template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
-        return Evaluator<T*, T*, Op>::evaluate( lhs, NULL );
+        return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
     }
 #endif // CATCH_CONFIG_CPP11_NULLPTR
 
@@ -1095,6 +1537,11 @@ std::string toString( char value );
 std::string toString( signed char value );
 std::string toString( unsigned char value );
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
 std::string toString( std::nullptr_t );
 #endif
@@ -1107,7 +1554,7 @@ std::string toString( std::nullptr_t );
 
 namespace Detail {
 
-    extern std::string unprintableString;
+    extern const std::string unprintableString;
 
     struct BorgType {
         template<typename T> BorgType( T const& );
@@ -1190,7 +1637,7 @@ struct StringMaker<T*> {
     template<typename U>
     static std::string convert( U* p ) {
         if( !p )
-            return INTERNAL_CATCH_STRINGIFY( NULL );
+            return "NULL";
         else
             return Detail::rawMemoryToString( p );
     }
@@ -1200,7 +1647,7 @@ template<typename R, typename C>
 struct StringMaker<R C::*> {
     static std::string convert( R C::* p ) {
         if( !p )
-            return INTERNAL_CATCH_STRINGIFY( NULL );
+            return "NULL";
         else
             return Detail::rawMemoryToString( p );
     }
@@ -1472,6 +1919,7 @@ namespace Catch {
     class AssertionResult;
     struct AssertionInfo;
     struct SectionInfo;
+    struct SectionEndInfo;
     struct MessageInfo;
     class ScopedMessageBuilder;
     struct Counts;
@@ -1483,7 +1931,8 @@ namespace Catch {
         virtual void assertionEnded( AssertionResult const& result ) = 0;
         virtual bool sectionStarted(    SectionInfo const& sectionInfo,
                                         Counts& assertions ) = 0;
-        virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
         virtual void pushScopedMessage( MessageInfo const& message ) = 0;
         virtual void popScopedMessage( MessageInfo const& message ) = 0;
 
@@ -1604,16 +2053,16 @@ namespace Catch {
     } while( Catch::alwaysFalse() )
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS( expr, resultDisposition, macroName ) \
+#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
         if( __catchResult.allowThrows() ) \
             try { \
                 expr; \
                 __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
             } \
             catch( ... ) { \
-                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+                __catchResult.captureExpectedException( matcher ); \
             } \
         else \
             __catchResult.captureResult( Catch::ResultWas::Ok ); \
@@ -1666,41 +2115,26 @@ namespace Catch {
 ///////////////////////////////////////////////////////////////////////////////
 #define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \
     do { \
-        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg " " #matcher, resultDisposition ); \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
         try { \
-            std::string matcherAsString = ::Catch::Matchers::matcher.toString(); \
+            std::string matcherAsString = (matcher).toString(); \
             __catchResult \
                 .setLhs( Catch::toString( arg ) ) \
                 .setRhs( matcherAsString == Catch::Detail::unprintableString ? #matcher : matcherAsString ) \
                 .setOp( "matches" ) \
-                .setResultType( ::Catch::Matchers::matcher.match( arg ) ); \
+                .setResultType( (matcher).match( arg ) ); \
             __catchResult.captureExpression(); \
         } catch( ... ) { \
             __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
         } \
         INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::alwaysFalse() )
-
-// #included from: internal/catch_section.h
-#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
-
-// #included from: catch_section_info.h
-#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
-
-namespace Catch {
-
-    struct SectionInfo {
-        SectionInfo
-            (   SourceLineInfo const& _lineInfo,
-                std::string const& _name,
-                std::string const& _description = std::string() );
+    } while( Catch::alwaysFalse() )
 
-        std::string name;
-        std::string description;
-        SourceLineInfo lineInfo;
-    };
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
 
-} // end namespace Catch
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
 
 // #included from: catch_totals.hpp
 #define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
@@ -1772,6 +2206,31 @@ namespace Catch {
     };
 }
 
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& _description = std::string() );
+
+        std::string name;
+        std::string description;
+        SourceLineInfo lineInfo;
+    };
+
+    struct SectionEndInfo {
+        SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+        : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+        {}
+
+        SectionInfo sectionInfo;
+        Counts prevAssertions;
+        double durationInSeconds;
+    };
+
+} // end namespace Catch
+
 // #included from: catch_timer.h
 #define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
 
@@ -2012,6 +2471,8 @@ using namespace Generators;
 #define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
 
 #include <string>
+#include <vector>
+
 // #included from: catch_interfaces_registry_hub.h
 #define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
 
@@ -2036,7 +2497,8 @@ namespace Catch {
 
     struct IMutableRegistryHub {
         virtual ~IMutableRegistryHub();
-        virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0;
+        virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
+        virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
         virtual void registerTest( TestCase const& testInfo ) = 0;
         virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
     };
@@ -2048,364 +2510,144 @@ namespace Catch {
 
 }
 
-
 namespace Catch {
 
     typedef std::string(*exceptionTranslateFunction)();
 
+    struct IExceptionTranslator;
+    typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
+
     struct IExceptionTranslator {
         virtual ~IExceptionTranslator();
-        virtual std::string translate() const = 0;
+        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
     };
 
     struct IExceptionTranslatorRegistry {
         virtual ~IExceptionTranslatorRegistry();
 
-        virtual std::string translateActiveException() const = 0;
-    };
-
-    class ExceptionTranslatorRegistrar {
-        template<typename T>
-        class ExceptionTranslator : public IExceptionTranslator {
-        public:
-
-            ExceptionTranslator( std::string(*translateFunction)( T& ) )
-            : m_translateFunction( translateFunction )
-            {}
-
-            virtual std::string translate() const {
-                try {
-                    throw;
-                }
-                catch( T& ex ) {
-                    return m_translateFunction( ex );
-                }
-            }
-
-        protected:
-            std::string(*m_translateFunction)( T& );
-        };
-
-    public:
-        template<typename T>
-        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
-            getMutableRegistryHub().registerTranslator
-                ( new ExceptionTranslator<T>( translateFunction ) );
-        }
-    };
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
-    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
-    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
-    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
-
-// #included from: internal/catch_approx.hpp
-#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
-
-#include <cmath>
-#include <limits>
-
-namespace Catch {
-namespace Detail {
-
-    class Approx {
-    public:
-        explicit Approx ( double value )
-        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
-            m_scale( 1.0 ),
-            m_value( value )
-        {}
-
-        Approx( Approx const& other )
-        :   m_epsilon( other.m_epsilon ),
-            m_scale( other.m_scale ),
-            m_value( other.m_value )
-        {}
-
-        static Approx custom() {
-            return Approx( 0 );
-        }
-
-        Approx operator()( double value ) {
-            Approx approx( value );
-            approx.epsilon( m_epsilon );
-            approx.scale( m_scale );
-            return approx;
-        }
-
-        friend bool operator == ( double lhs, Approx const& rhs ) {
-            // Thanks to Richard Harris for his help refining this formula
-            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
-        }
-
-        friend bool operator == ( Approx const& lhs, double rhs ) {
-            return operator==( rhs, lhs );
-        }
-
-        friend bool operator != ( double lhs, Approx const& rhs ) {
-            return !operator==( lhs, rhs );
-        }
-
-        friend bool operator != ( Approx const& lhs, double rhs ) {
-            return !operator==( rhs, lhs );
-        }
-
-        Approx& epsilon( double newEpsilon ) {
-            m_epsilon = newEpsilon;
-            return *this;
-        }
-
-        Approx& scale( double newScale ) {
-            m_scale = newScale;
-            return *this;
-        }
-
-        std::string toString() const {
-            std::ostringstream oss;
-            oss << "Approx( " << Catch::toString( m_value ) << " )";
-            return oss.str();
-        }
-
-    private:
-        double m_epsilon;
-        double m_scale;
-        double m_value;
-    };
-}
-
-template<>
-inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
-    return value.toString();
-}
-
-} // end namespace Catch
-
-// #included from: internal/catch_matchers.hpp
-#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
-
-namespace Catch {
-namespace Matchers {
-    namespace Impl {
-
-    template<typename ExpressionT>
-    struct Matcher : SharedImpl<IShared>
-    {
-        typedef ExpressionT ExpressionType;
-
-        virtual ~Matcher() {}
-        virtual Ptr<Matcher> clone() const = 0;
-        virtual bool match( ExpressionT const& expr ) const = 0;
-        virtual std::string toString() const = 0;
-    };
-
-    template<typename DerivedT, typename ExpressionT>
-    struct MatcherImpl : Matcher<ExpressionT> {
-
-        virtual Ptr<Matcher<ExpressionT> > clone() const {
-            return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) );
-        }
-    };
-
-    namespace Generic {
-
-        template<typename ExpressionT>
-        class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AllOf() {}
-            AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AllOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( !m_matchers[i]->match( expr ) )
-                        return false;
-                return true;
-            }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        oss << " and ";
-                    oss << m_matchers[i]->toString();
-                }
-                oss << " )";
-                return oss.str();
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-        template<typename ExpressionT>
-        class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> {
-        public:
-
-            AnyOf() {}
-            AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {}
-
-            AnyOf& add( Matcher<ExpressionT> const& matcher ) {
-                m_matchers.push_back( matcher.clone() );
-                return *this;
-            }
-            virtual bool match( ExpressionT const& expr ) const
-            {
-                for( std::size_t i = 0; i < m_matchers.size(); ++i )
-                    if( m_matchers[i]->match( expr ) )
-                        return true;
-                return false;
-            }
-            virtual std::string toString() const {
-                std::ostringstream oss;
-                oss << "( ";
-                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
-                    if( i != 0 )
-                        oss << " or ";
-                    oss << m_matchers[i]->toString();
-                }
-                oss << " )";
-                return oss.str();
-            }
-
-        private:
-            std::vector<Ptr<Matcher<ExpressionT> > > m_matchers;
-        };
-
-    }
-
-    namespace StdString {
-
-        inline std::string makeString( std::string const& str ) { return str; }
-        inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); }
+        virtual std::string translateActiveException() const = 0;
+    };
 
-        struct Equals : MatcherImpl<Equals, std::string> {
-            Equals( std::string const& str ) : m_str( str ){}
-            Equals( Equals const& other ) : m_str( other.m_str ){}
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
 
-            virtual ~Equals();
+            ExceptionTranslator( std::string(*translateFunction)( T& ) )
+            : m_translateFunction( translateFunction )
+            {}
 
-            virtual bool match( std::string const& expr ) const {
-                return m_str == expr;
-            }
-            virtual std::string toString() const {
-                return "equals: \"" + m_str + "\"";
+            virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+                try {
+                    if( it == itEnd )
+                        throw;
+                    else
+                        return (*it)->translate( it+1, itEnd );
+                }
+                catch( T& ex ) {
+                    return m_translateFunction( ex );
+                }
             }
 
-            std::string m_str;
+        protected:
+            std::string(*m_translateFunction)( T& );
         };
 
-        struct Contains : MatcherImpl<Contains, std::string> {
-            Contains( std::string const& substr ) : m_substr( substr ){}
-            Contains( Contains const& other ) : m_substr( other.m_substr ){}
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+            getMutableRegistryHub().registerTranslator
+                ( new ExceptionTranslator<T>( translateFunction ) );
+        }
+    };
+}
 
-            virtual ~Contains();
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
+    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
+    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) != std::string::npos;
-            }
-            virtual std::string toString() const {
-                return "contains: \"" + m_substr + "\"";
-            }
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
 
-            std::string m_substr;
-        };
+#include <cmath>
+#include <limits>
 
-        struct StartsWith : MatcherImpl<StartsWith, std::string> {
-            StartsWith( std::string const& substr ) : m_substr( substr ){}
-            StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){}
+namespace Catch {
+namespace Detail {
 
-            virtual ~StartsWith();
+    class Approx {
+    public:
+        explicit Approx ( double value )
+        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+            m_scale( 1.0 ),
+            m_value( value )
+        {}
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) == 0;
-            }
-            virtual std::string toString() const {
-                return "starts with: \"" + m_substr + "\"";
-            }
+        Approx( Approx const& other )
+        :   m_epsilon( other.m_epsilon ),
+            m_scale( other.m_scale ),
+            m_value( other.m_value )
+        {}
 
-            std::string m_substr;
-        };
+        static Approx custom() {
+            return Approx( 0 );
+        }
 
-        struct EndsWith : MatcherImpl<EndsWith, std::string> {
-            EndsWith( std::string const& substr ) : m_substr( substr ){}
-            EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){}
+        Approx operator()( double value ) {
+            Approx approx( value );
+            approx.epsilon( m_epsilon );
+            approx.scale( m_scale );
+            return approx;
+        }
 
-            virtual ~EndsWith();
+        friend bool operator == ( double lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) );
+        }
 
-            virtual bool match( std::string const& expr ) const {
-                return expr.find( m_substr ) == expr.size() - m_substr.size();
-            }
-            virtual std::string toString() const {
-                return "ends with: \"" + m_substr + "\"";
-            }
+        friend bool operator == ( Approx const& lhs, double rhs ) {
+            return operator==( rhs, lhs );
+        }
 
-            std::string m_substr;
-        };
-    } // namespace StdString
-    } // namespace Impl
+        friend bool operator != ( double lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
 
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 );
-    }
-    template<typename ExpressionT>
-    inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1,
-                                                    Impl::Matcher<ExpressionT> const& m2,
-                                                    Impl::Matcher<ExpressionT> const& m3 ) {
-        return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 );
-    }
+        friend bool operator != ( Approx const& lhs, double rhs ) {
+            return !operator==( rhs, lhs );
+        }
 
-    inline Impl::StdString::Equals      Equals( std::string const& str ) {
-        return Impl::StdString::Equals( str );
-    }
-    inline Impl::StdString::Equals      Equals( const char* str ) {
-        return Impl::StdString::Equals( Impl::StdString::makeString( str ) );
-    }
-    inline Impl::StdString::Contains    Contains( std::string const& substr ) {
-        return Impl::StdString::Contains( substr );
-    }
-    inline Impl::StdString::Contains    Contains( const char* substr ) {
-        return Impl::StdString::Contains( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( std::string const& substr ) {
-        return Impl::StdString::StartsWith( substr );
-    }
-    inline Impl::StdString::StartsWith  StartsWith( const char* substr ) {
-        return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( std::string const& substr ) {
-        return Impl::StdString::EndsWith( substr );
-    }
-    inline Impl::StdString::EndsWith    EndsWith( const char* substr ) {
-        return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) );
-    }
+        Approx& epsilon( double newEpsilon ) {
+            m_epsilon = newEpsilon;
+            return *this;
+        }
 
-} // namespace Matchers
+        Approx& scale( double newScale ) {
+            m_scale = newScale;
+            return *this;
+        }
 
-using namespace Matchers;
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << "Approx( " << Catch::toString( m_value ) << " )";
+            return oss.str();
+        }
 
-} // namespace Catch
+    private:
+        double m_epsilon;
+        double m_scale;
+        double m_value;
+    };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+    return value.toString();
+}
+
+} // end namespace Catch
 
 // #included from: internal/catch_interfaces_tag_alias_registry.h
 #define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
@@ -2440,12 +2682,12 @@ namespace Catch {
     template<typename T>
     class Option {
     public:
-        Option() : nullableValue( NULL ) {}
+        Option() : nullableValue( CATCH_NULL ) {}
         Option( T const& _value )
         : nullableValue( new( storage ) T( _value ) )
         {}
         Option( Option const& _other )
-        : nullableValue( _other ? new( storage ) T( *_other ) : NULL )
+        : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL )
         {}
 
         ~Option() {
@@ -2469,7 +2711,7 @@ namespace Catch {
         void reset() {
             if( nullableValue )
                 nullableValue->~T();
-            nullableValue = NULL;
+            nullableValue = CATCH_NULL;
         }
 
         T& operator*() { return *nullableValue; }
@@ -2481,10 +2723,10 @@ namespace Catch {
             return nullableValue ? *nullableValue : defaultValue;
         }
 
-        bool some() const { return nullableValue != NULL; }
-        bool none() const { return nullableValue == NULL; }
+        bool some() const { return nullableValue != CATCH_NULL; }
+        bool none() const { return nullableValue == CATCH_NULL; }
 
-        bool operator !() const { return nullableValue == NULL; }
+        bool operator !() const { return nullableValue == CATCH_NULL; }
         operator SafeBool::type() const {
             return SafeBool::makeSafe( some() );
         }
@@ -2542,6 +2784,8 @@ namespace Catch {
 
         TestCaseInfo( TestCaseInfo const& other );
 
+        friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+
         bool isHidden() const;
         bool throws() const;
         bool okToFail() const;
@@ -2654,7 +2898,7 @@ namespace Catch {
 
     inline size_t registerTestMethods() {
         size_t noTestMethods = 0;
-        int noClasses = objc_getClassList( NULL, 0 );
+        int noClasses = objc_getClassList( CATCH_NULL, 0 );
 
         Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
         objc_getClassList( classes, noClasses );
@@ -2796,7 +3040,7 @@ return @ desc; \
 #pragma clang diagnostic ignored "-Wweak-vtables"
 #endif
 
-// #included from: ../catch_runner.hpp
+// #included from: ../catch_session.hpp
 #define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
 
 // #included from: internal/catch_commandline.hpp
@@ -2821,6 +3065,67 @@ return @ desc; \
 #pragma clang diagnostic ignored "-Wpadded"
 #endif
 
+// #included from: catch_wildcard_pattern.hpp
+#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+namespace Catch
+{
+    class WildcardPattern {
+        enum WildcardPosition {
+            NoWildcard = 0,
+            WildcardAtStart = 1,
+            WildcardAtEnd = 2,
+            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+        };
+
+    public:
+
+        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_wildcard( NoWildcard ),
+            m_pattern( adjustCase( pattern ) )
+        {
+            if( startsWith( m_pattern, "*" ) ) {
+                m_pattern = m_pattern.substr( 1 );
+                m_wildcard = WildcardAtStart;
+            }
+            if( endsWith( m_pattern, "*" ) ) {
+                m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+                m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+            }
+        }
+        virtual ~WildcardPattern();
+        virtual bool matches( std::string const& str ) const {
+            switch( m_wildcard ) {
+                case NoWildcard:
+                    return m_pattern == adjustCase( str );
+                case WildcardAtStart:
+                    return endsWith( adjustCase( str ), m_pattern );
+                case WildcardAtEnd:
+                    return startsWith( adjustCase( str ), m_pattern );
+                case WildcardAtBothEnds:
+                    return contains( adjustCase( str ), m_pattern );
+            }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+            throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+        }
+    private:
+        std::string adjustCase( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+        }
+        CaseSensitive::Choice m_caseSensitivity;
+        WildcardPosition m_wildcard;
+        std::string m_pattern;
+    };
+}
+
 #include <string>
 #include <vector>
 
@@ -2832,50 +3137,18 @@ namespace Catch {
             virtual bool matches( TestCaseInfo const& testCase ) const = 0;
         };
         class NamePattern : public Pattern {
-            enum WildcardPosition {
-                NoWildcard = 0,
-                WildcardAtStart = 1,
-                WildcardAtEnd = 2,
-                WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
-            };
-
         public:
-            NamePattern( std::string const& name ) : m_name( toLower( name ) ), m_wildcard( NoWildcard ) {
-                if( startsWith( m_name, "*" ) ) {
-                    m_name = m_name.substr( 1 );
-                    m_wildcard = WildcardAtStart;
-                }
-                if( endsWith( m_name, "*" ) ) {
-                    m_name = m_name.substr( 0, m_name.size()-1 );
-                    m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
-                }
-            }
+            NamePattern( std::string const& name )
+            : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+            {}
             virtual ~NamePattern();
             virtual bool matches( TestCaseInfo const& testCase ) const {
-                switch( m_wildcard ) {
-                    case NoWildcard:
-                        return m_name == toLower( testCase.name );
-                    case WildcardAtStart:
-                        return endsWith( toLower( testCase.name ), m_name );
-                    case WildcardAtEnd:
-                        return startsWith( toLower( testCase.name ), m_name );
-                    case WildcardAtBothEnds:
-                        return contains( toLower( testCase.name ), m_name );
-                }
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunreachable-code"
-#endif
-                throw std::logic_error( "Unknown enum" );
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+                return m_wildcardPattern.matches( toLower( testCase.name ) );
             }
         private:
-            std::string m_name;
-            WildcardPosition m_wildcard;
+            WildcardPattern m_wildcardPattern;
         };
+
         class TagPattern : public Pattern {
         public:
             TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
@@ -2886,6 +3159,7 @@ namespace Catch {
         private:
             std::string m_tag;
         };
+
         class ExcludedPattern : public Pattern {
         public:
             ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
@@ -3083,28 +3357,62 @@ namespace Catch {
 // #included from: catch_stream.h
 #define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
 
-#include <streambuf>
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
 
-#ifdef __clang__
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
+#include <streambuf>
 
 namespace Catch {
 
-    class Stream {
+    class StreamBufBase : public std::streambuf {
     public:
-        Stream();
-        Stream( std::streambuf* _streamBuf, bool _isOwned );
-        void release();
+        virtual ~StreamBufBase() CATCH_NOEXCEPT;
+    };
+}
 
-        std::streambuf* streamBuf;
+#include <streambuf>
+#include <ostream>
+#include <fstream>
 
-    private:
-        bool isOwned;
-    };
+namespace Catch {
 
     std::ostream& cout();
     std::ostream& cerr();
+
+    struct IStream {
+        virtual ~IStream() CATCH_NOEXCEPT;
+        virtual std::ostream& stream() const = 0;
+    };
+
+    class FileStream : public IStream {
+        mutable std::ofstream m_ofs;
+    public:
+        FileStream( std::string const& filename );
+        virtual ~FileStream() CATCH_NOEXCEPT;
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class CoutStream : public IStream {
+        mutable std::ostream m_os;
+    public:
+        CoutStream();
+        virtual ~CoutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class DebugOutStream : public IStream {
+        std::auto_ptr<StreamBufBase> m_streamBuf;
+        mutable std::ostream m_os;
+    public:
+        DebugOutStream();
+        virtual ~DebugOutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
 }
 
 #include <memory>
@@ -3132,6 +3440,7 @@ namespace Catch {
             showHelp( false ),
             showInvisibles( false ),
             forceColour( false ),
+            filenamesAsTags( false ),
             abortAfter( -1 ),
             rngSeed( 0 ),
             verbosity( Verbosity::Normal ),
@@ -3151,6 +3460,7 @@ namespace Catch {
         bool showHelp;
         bool showInvisibles;
         bool forceColour;
+        bool filenamesAsTags;
 
         int abortAfter;
         unsigned int rngSeed;
@@ -3160,11 +3470,11 @@ namespace Catch {
         ShowDurations::OrNot showDurations;
         RunTests::InWhatOrder runOrder;
 
-        std::string reporterName;
         std::string outputFilename;
         std::string name;
         std::string processName;
 
+        std::vector<std::string> reporterNames;
         std::vector<std::string> testsOrTags;
     };
 
@@ -3176,12 +3486,11 @@ namespace Catch {
     public:
 
         Config()
-        :   m_os( Catch::cout().rdbuf() )
         {}
 
         Config( ConfigData const& data )
         :   m_data( data ),
-            m_os( Catch::cout().rdbuf() )
+            m_stream( openStream() )
         {
             if( !data.testsOrTags.empty() ) {
                 TestSpecParser parser( ITagAliasRegistry::get() );
@@ -3192,12 +3501,6 @@ namespace Catch {
         }
 
         virtual ~Config() {
-            m_os.rdbuf( Catch::cout().rdbuf() );
-            m_stream.release();
-        }
-
-        void setFilename( std::string const& filename ) {
-            m_data.outputFilename = filename;
         }
 
         std::string const& getFilename() const {
@@ -3213,18 +3516,7 @@ namespace Catch {
 
         bool shouldDebugBreak() const { return m_data.shouldDebugBreak; }
 
-        void setStreamBuf( std::streambuf* buf ) {
-            m_os.rdbuf( buf ? buf : Catch::cout().rdbuf() );
-        }
-
-        void useStream( std::string const& streamName ) {
-            Stream stream = createStream( streamName );
-            setStreamBuf( stream.streamBuf );
-            m_stream.release();
-            m_stream = stream;
-        }
-
-        std::string getReporterName() const { return m_data.reporterName; }
+        std::vector<std::string> getReporterNames() const { return m_data.reporterNames; }
 
         int abortAfter() const { return m_data.abortAfter; }
 
@@ -3235,7 +3527,7 @@ namespace Catch {
 
         // IConfig interface
         virtual bool allowThrows() const        { return !m_data.noThrow; }
-        virtual std::ostream& stream() const    { return m_os; }
+        virtual std::ostream& stream() const    { return m_stream->stream(); }
         virtual std::string name() const        { return m_data.name.empty() ? m_data.processName : m_data.name; }
         virtual bool includeSuccessfulResults() const   { return m_data.showSuccessfulTests; }
         virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; }
@@ -3245,10 +3537,22 @@ namespace Catch {
         virtual bool forceColour() const { return m_data.forceColour; }
 
     private:
+
+        IStream const* openStream() {
+            if( m_data.outputFilename.empty() )
+                return new CoutStream();
+            else if( m_data.outputFilename[0] == '%' ) {
+                if( m_data.outputFilename == "%debug" )
+                    return new DebugOutStream();
+                else
+                    throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
+            }
+            else
+                return new FileStream( m_data.outputFilename );
+        }
         ConfigData m_data;
 
-        Stream m_stream;
-        mutable std::ostream m_os;
+        std::auto_ptr<IStream const> m_stream;
         TestSpec m_testSpec;
     };
 
@@ -3517,11 +3821,11 @@ namespace Clara {
         template<typename ConfigT>
         class BoundArgFunction {
         public:
-            BoundArgFunction() : functionObj( NULL ) {}
+            BoundArgFunction() : functionObj( CATCH_NULL ) {}
             BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
-            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {}
+            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CATCH_NULL ) {}
             BoundArgFunction& operator = ( BoundArgFunction const& other ) {
-                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL;
+                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL;
                 delete functionObj;
                 functionObj = newFunctionObj;
                 return *this;
@@ -3537,7 +3841,7 @@ namespace Clara {
             bool takesArg() const { return functionObj->takesArg(); }
 
             bool isSet() const {
-                return functionObj != NULL;
+                return functionObj != CATCH_NULL;
             }
         private:
             IArgFunction<ConfigT>* functionObj;
@@ -3755,12 +4059,7 @@ namespace Clara {
             }
         };
 
-        // NOTE: std::auto_ptr is deprecated in c++11/c++0x
-#if defined(__cplusplus) && __cplusplus > 199711L
-        typedef std::unique_ptr<Arg> ArgAutoPtr;
-#else
-        typedef std::auto_ptr<Arg> ArgAutoPtr;
-#endif
+        typedef CATCH_AUTO_PTR( Arg ) ArgAutoPtr;
 
         friend void addOptName( Arg& arg, std::string const& optName )
         {
@@ -3836,8 +4135,8 @@ namespace Clara {
                 m_arg->description = description;
                 return *this;
             }
-            ArgBuilder& detail( std::string const& detail ) {
-                m_arg->detail = detail;
+            ArgBuilder& detail( std::string const& _detail ) {
+                m_arg->detail = _detail;
                 return *this;
             }
 
@@ -3920,14 +4219,14 @@ namespace Clara {
                 maxWidth = (std::max)( maxWidth, it->commands().size() );
 
             for( it = itBegin; it != itEnd; ++it ) {
-                Detail::Text usage( it->commands(), Detail::TextAttributes()
+                Detail::Text usageText( it->commands(), Detail::TextAttributes()
                                                         .setWidth( maxWidth+indent )
                                                         .setIndent( indent ) );
                 Detail::Text desc( it->description, Detail::TextAttributes()
                                                         .setWidth( width - maxWidth - 3 ) );
 
-                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
-                    std::string usageCol = i < usage.size() ? usage[i] : "";
+                for( std::size_t i = 0; i < (std::max)( usageText.size(), desc.size() ); ++i ) {
+                    std::string usageCol = i < usageText.size() ? usageText[i] : "";
                     os << usageCol;
 
                     if( i < desc.size() && !desc[i].empty() )
@@ -4133,6 +4432,7 @@ namespace Catch {
         config.abortAfter = x;
     }
     inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+    inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
 
     inline void addWarning( ConfigData& config, std::string const& _warning ) {
         if( _warning == "NoAssertions" )
@@ -4226,7 +4526,7 @@ namespace Catch {
         cli["-r"]["--reporter"]
 //            .placeholder( "name[:filename]" )
             .describe( "reporter to use (defaults to console)" )
-            .bind( &ConfigData::reporterName, "name" );
+            .bind( &addReporterName, "name" );
 
         cli["-n"]["--name"]
             .describe( "suite name" )
@@ -4263,6 +4563,10 @@ namespace Catch {
             .describe( "load test names to run from a file" )
             .bind( &loadTestNamesFromFile, "filename" );
 
+        cli["-#"]["--filenames-as-tags"]
+            .describe( "adds a tag for the filename" )
+            .bind( &ConfigData::filenamesAsTags );
+
         // Less common commands which don't have a short form
         cli["--list-test-names-only"]
             .describe( "list all/matching test cases names only" )
@@ -4520,18 +4824,18 @@ namespace Catch {
 namespace Catch
 {
     struct ReporterConfig {
-        explicit ReporterConfig( Ptr<IConfig> const& _fullConfig )
+        explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
         :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
 
-        ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream )
+        ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
         :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
 
         std::ostream& stream() const    { return *m_stream; }
-        Ptr<IConfig> fullConfig() const { return m_fullConfig; }
+        Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
 
     private:
         std::ostream* m_stream;
-        Ptr<IConfig> m_fullConfig;
+        Ptr<IConfig const> m_fullConfig;
     };
 
     struct ReporterPreferences {
@@ -4733,6 +5037,7 @@ namespace Catch
 
         // The return value indicates if the messages buffer should be cleared:
         virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
         virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
         virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
         virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
@@ -4741,20 +5046,24 @@ namespace Catch
         virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
     };
 
-    struct IReporterFactory {
+    struct IReporterFactory : IShared {
         virtual ~IReporterFactory();
         virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
         virtual std::string getDescription() const = 0;
     };
 
     struct IReporterRegistry {
-        typedef std::map<std::string, IReporterFactory*> FactoryMap;
+        typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
+        typedef std::vector<Ptr<IReporterFactory> > Listeners;
 
         virtual ~IReporterRegistry();
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0;
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
         virtual FactoryMap const& getFactories() const = 0;
+        virtual Listeners const& getListeners() const = 0;
     };
 
+    Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
+
 }
 
 #include <limits>
@@ -4777,8 +5086,7 @@ namespace Catch {
         nameAttr.setInitialIndent( 2 ).setIndent( 4 );
         tagsAttr.setIndent( 6 );
 
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4806,8 +5114,7 @@ namespace Catch {
         if( !config.testSpec().hasFilters() )
             testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
         std::size_t matchedTests = 0;
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4847,8 +5154,7 @@ namespace Catch {
 
         std::map<std::string, TagInfo> tagCounts;
 
-        std::vector<TestCase> matchedTestCases;
-        getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, config, matchedTestCases );
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
         for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
                 it != itEnd;
                 ++it ) {
@@ -4919,7 +5225,7 @@ namespace Catch {
 
 } // end namespace Catch
 
-// #included from: internal/catch_runner_impl.hpp
+// #included from: internal/catch_run_context.hpp
 #define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
 
 // #included from: catch_test_case_tracker.hpp
@@ -4928,137 +5234,300 @@ namespace Catch {
 #include <map>
 #include <string>
 #include <assert.h>
+#include <vector>
 
 namespace Catch {
-namespace SectionTracking {
+namespace TestCaseTracking {
 
-    class TrackedSection {
+    struct ITracker : SharedImpl<> {
+        virtual ~ITracker();
 
-        typedef std::map<std::string, TrackedSection> TrackedSections;
+        // static queries
+        virtual std::string name() const = 0;
+
+        // dynamic queries
+        virtual bool isComplete() const = 0; // Successfully completed or failed
+        virtual bool isSuccessfullyCompleted() const = 0;
+        virtual bool isOpen() const = 0; // Started but not complete
+        virtual bool hasChildren() const = 0;
+
+        virtual ITracker& parent() = 0;
+
+        // actions
+        virtual void close() = 0; // Successfully complete
+        virtual void fail() = 0;
+        virtual void markAsNeedingAnotherRun() = 0;
+
+        virtual void addChild( Ptr<ITracker> const& child ) = 0;
+        virtual ITracker* findChild( std::string const& name ) = 0;
+        virtual void openChild() = 0;
+    };
+
+    class TrackerContext {
 
-    public:
         enum RunState {
             NotStarted,
             Executing,
-            ExecutingChildren,
-            Completed
+            CompletedCycle
         };
 
-        TrackedSection( std::string const& name, TrackedSection* parent )
-        :   m_name( name ), m_runState( NotStarted ), m_parent( parent )
-        {}
+        Ptr<ITracker> m_rootTracker;
+        ITracker* m_currentTracker;
+        RunState m_runState;
+
+    public:
 
-        RunState runState() const { return m_runState; }
+        static TrackerContext& instance() {
+            static TrackerContext s_instance;
+            return s_instance;
+        }
 
-        TrackedSection* findChild( std::string const& childName );
-        TrackedSection* acquireChild( std::string const& childName );
+        TrackerContext()
+        :   m_currentTracker( CATCH_NULL ),
+            m_runState( NotStarted )
+        {}
 
-        void enter() {
-            if( m_runState == NotStarted )
-                m_runState = Executing;
+        ITracker& startRun();
+
+        void endRun() {
+            m_rootTracker.reset();
+            m_currentTracker = CATCH_NULL;
+            m_runState = NotStarted;
         }
-        void leave();
 
-        TrackedSection* getParent() {
-            return m_parent;
+        void startCycle() {
+            m_currentTracker = m_rootTracker.get();
+            m_runState = Executing;
         }
-        bool hasChildren() const {
-            return !m_children.empty();
+        void completeCycle() {
+            m_runState = CompletedCycle;
         }
 
-    private:
-        std::string m_name;
-        RunState m_runState;
-        TrackedSections m_children;
-        TrackedSection* m_parent;
+        bool completedCycle() const {
+            return m_runState == CompletedCycle;
+        }
+        ITracker& currentTracker() {
+            return *m_currentTracker;
+        }
+        void setCurrentTracker( ITracker* tracker ) {
+            m_currentTracker = tracker;
+        }
     };
 
-    inline TrackedSection* TrackedSection::findChild( std::string const& childName ) {
-        TrackedSections::iterator it = m_children.find( childName );
-        return it != m_children.end()
-            ? &it->second
-            : NULL;
-    }
-    inline TrackedSection* TrackedSection::acquireChild( std::string const& childName ) {
-        if( TrackedSection* child = findChild( childName ) )
-            return child;
-        m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) );
-        return findChild( childName );
-    }
-    inline void TrackedSection::leave() {
-        for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end();
-                it != itEnd;
-                ++it )
-            if( it->second.runState() != Completed ) {
-                m_runState = ExecutingChildren;
-                return;
+    class TrackerBase : public ITracker {
+    protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
+        class TrackerHasName {
+            std::string m_name;
+        public:
+            TrackerHasName( std::string const& name ) : m_name( name ) {}
+            bool operator ()( Ptr<ITracker> const& tracker ) {
+                return tracker->name() == m_name;
             }
-        m_runState = Completed;
-    }
-
-    class TestCaseTracker {
+        };
+        typedef std::vector<Ptr<ITracker> > Children;
+        std::string m_name;
+        TrackerContext& m_ctx;
+        ITracker* m_parent;
+        Children m_children;
+        CycleState m_runState;
     public:
-        TestCaseTracker( std::string const& testCaseName )
-        :   m_testCase( testCaseName, NULL ),
-            m_currentSection( &m_testCase ),
-            m_completedASectionThisRun( false )
+        TrackerBase( std::string const& name, TrackerContext& ctx, ITracker* parent )
+        :   m_name( name ),
+            m_ctx( ctx ),
+            m_parent( parent ),
+            m_runState( NotStarted )
         {}
+        virtual ~TrackerBase();
 
-        bool enterSection( std::string const& name ) {
-            TrackedSection* child = m_currentSection->acquireChild( name );
-            if( m_completedASectionThisRun || child->runState() == TrackedSection::Completed )
-                return false;
+        virtual std::string name() const CATCH_OVERRIDE {
+            return m_name;
+        }
+        virtual bool isComplete() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully || m_runState == Failed;
+        }
+        virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully;
+        }
+        virtual bool isOpen() const CATCH_OVERRIDE {
+            return m_runState != NotStarted && !isComplete();
+        }
+        virtual bool hasChildren() const CATCH_OVERRIDE {
+            return !m_children.empty();
+        }
 
-            m_currentSection = child;
-            m_currentSection->enter();
-            return true;
+        virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
+            m_children.push_back( child );
+        }
+
+        virtual ITracker* findChild( std::string const& name ) CATCH_OVERRIDE {
+            Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( name ) );
+            return( it != m_children.end() )
+                ? it->get()
+                : CATCH_NULL;
         }
-        void leaveSection() {
-            m_currentSection->leave();
-            m_currentSection = m_currentSection->getParent();
-            assert( m_currentSection != NULL );
-            m_completedASectionThisRun = true;
+        virtual ITracker& parent() CATCH_OVERRIDE {
+            assert( m_parent ); // Should always be non-null except for root
+            return *m_parent;
         }
 
-        bool currentSectionHasChildren() const {
-            return m_currentSection->hasChildren();
+        virtual void openChild() CATCH_OVERRIDE {
+            if( m_runState != ExecutingChildren ) {
+                m_runState = ExecutingChildren;
+                if( m_parent )
+                    m_parent->openChild();
+            }
         }
-        bool isCompleted() const {
-            return m_testCase.runState() == TrackedSection::Completed;
+        void open() {
+            m_runState = Executing;
+            moveToThis();
+            if( m_parent )
+                m_parent->openChild();
         }
 
-        class Guard {
-        public:
-            Guard( TestCaseTracker& tracker ) : m_tracker( tracker ) {
-                m_tracker.enterTestCase();
+        virtual void close() CATCH_OVERRIDE {
+
+            // Close any still open children (e.g. generators)
+            while( &m_ctx.currentTracker() != this )
+                m_ctx.currentTracker().close();
+
+            switch( m_runState ) {
+                case NotStarted:
+                case CompletedSuccessfully:
+                case Failed:
+                    throw std::logic_error( "Illogical state" );
+
+                case NeedsAnotherRun:
+                    break;;
+
+                case Executing:
+                    m_runState = CompletedSuccessfully;
+                    break;
+                case ExecutingChildren:
+                    if( m_children.empty() || m_children.back()->isComplete() )
+                        m_runState = CompletedSuccessfully;
+                    break;
+
+                default:
+                    throw std::logic_error( "Unexpected state" );
+            }
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void fail() CATCH_OVERRIDE {
+            m_runState = Failed;
+            if( m_parent )
+                m_parent->markAsNeedingAnotherRun();
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
+            m_runState = NeedsAnotherRun;
+        }
+    private:
+        void moveToParent() {
+            assert( m_parent );
+            m_ctx.setCurrentTracker( m_parent );
+        }
+        void moveToThis() {
+            m_ctx.setCurrentTracker( this );
+        }
+    };
+
+    class SectionTracker : public TrackerBase {
+    public:
+        SectionTracker( std::string const& name, TrackerContext& ctx, ITracker* parent )
+        :   TrackerBase( name, ctx, parent )
+        {}
+        virtual ~SectionTracker();
+
+        static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) {
+            SectionTracker* section = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( name ) ) {
+                section = dynamic_cast<SectionTracker*>( childTracker );
+                assert( section );
             }
-            ~Guard() {
-                m_tracker.leaveTestCase();
+            else {
+                section = new SectionTracker( name, ctx, &currentTracker );
+                currentTracker.addChild( section );
             }
-        private:
-            Guard( Guard const& );
-            void operator = ( Guard const& );
-            TestCaseTracker& m_tracker;
-        };
+            if( !ctx.completedCycle() && !section->isComplete() ) {
 
-    private:
-        void enterTestCase() {
-            m_currentSection = &m_testCase;
-            m_completedASectionThisRun = false;
-            m_testCase.enter();
+                section->open();
+            }
+            return *section;
+        }
+    };
+
+    class IndexTracker : public TrackerBase {
+        int m_size;
+        int m_index;
+    public:
+        IndexTracker( std::string const& name, TrackerContext& ctx, ITracker* parent, int size )
+        :   TrackerBase( name, ctx, parent ),
+            m_size( size ),
+            m_index( -1 )
+        {}
+        virtual ~IndexTracker();
+
+        static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) {
+            IndexTracker* tracker = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( name ) ) {
+                tracker = dynamic_cast<IndexTracker*>( childTracker );
+                assert( tracker );
+            }
+            else {
+                tracker = new IndexTracker( name, ctx, &currentTracker, size );
+                currentTracker.addChild( tracker );
+            }
+
+            if( !ctx.completedCycle() && !tracker->isComplete() ) {
+                if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+                    tracker->moveNext();
+                tracker->open();
+            }
+
+            return *tracker;
         }
-        void leaveTestCase() {
-            m_testCase.leave();
+
+        int index() const { return m_index; }
+
+        void moveNext() {
+            m_index++;
+            m_children.clear();
         }
 
-        TrackedSection m_testCase;
-        TrackedSection* m_currentSection;
-        bool m_completedASectionThisRun;
+        virtual void close() CATCH_OVERRIDE {
+            TrackerBase::close();
+            if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+                m_runState = Executing;
+        }
     };
 
-} // namespace SectionTracking
+    inline ITracker& TrackerContext::startRun() {
+        m_rootTracker = new SectionTracker( "{root}", *this, CATCH_NULL );
+        m_currentTracker = CATCH_NULL;
+        m_runState = Executing;
+        return *m_rootTracker;
+    }
+
+} // namespace TestCaseTracking
 
-using SectionTracking::TestCaseTracker;
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+using TestCaseTracking::IndexTracker;
 
 } // namespace Catch
 
@@ -5174,15 +5643,12 @@ namespace Catch {
 
     public:
 
-        explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter )
-        :   m_runInfo( config->name() ),
+        explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter )
+        :   m_runInfo( _config->name() ),
             m_context( getCurrentMutableContext() ),
-            m_activeTestCase( NULL ),
-            m_config( config ),
-            m_reporter( reporter ),
-            m_prevRunner( m_context.getRunner() ),
-            m_prevResultCapture( m_context.getResultCapture() ),
-            m_prevConfig( m_context.getConfig() )
+            m_activeTestCase( CATCH_NULL ),
+            m_config( _config ),
+            m_reporter( reporter )
         {
             m_context.setRunner( this );
             m_context.setConfig( m_config );
@@ -5192,10 +5658,6 @@ namespace Catch {
 
         virtual ~RunContext() {
             m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
-            m_context.setRunner( m_prevRunner );
-            m_context.setConfig( NULL );
-            m_context.setResultCapture( m_prevResultCapture );
-            m_context.setConfig( m_prevConfig );
         }
 
         void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
@@ -5216,14 +5678,17 @@ namespace Catch {
             m_reporter->testCaseStarting( testInfo );
 
             m_activeTestCase = &testCase;
-            m_testCaseTracker = TestCaseTracker( testInfo.name );
 
             do {
+                m_trackerContext.startRun();
                 do {
+                    m_trackerContext.startCycle();
+                    m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, testInfo.name );
                     runCurrentTest( redirectedCout, redirectedCerr );
                 }
-                while( !m_testCaseTracker->isCompleted() && !aborting() );
+                while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
             }
+            // !TBD: deprecated - this will be replaced by indexed trackers
             while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
 
             Totals deltaTotals = m_totals.delta( prevTotals );
@@ -5234,8 +5699,8 @@ namespace Catch {
                                                         redirectedCerr,
                                                         aborting() ) );
 
-            m_activeTestCase = NULL;
-            m_testCaseTracker.reset();
+            m_activeTestCase = CATCH_NULL;
+            m_testCaseTracker = CATCH_NULL;
 
             return deltaTotals;
         }
@@ -5270,8 +5735,10 @@ namespace Catch {
             std::ostringstream oss;
             oss << sectionInfo.name << "@" << sectionInfo.lineInfo;
 
-            if( !m_testCaseTracker->enterSection( oss.str() ) )
+            ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, oss.str() );
+            if( !sectionTracker.isOpen() )
                 return false;
+            m_activeSections.push_back( &sectionTracker );
 
             m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
 
@@ -5282,30 +5749,40 @@ namespace Catch {
             return true;
         }
         bool testForMissingAssertions( Counts& assertions ) {
-            if( assertions.total() != 0 ||
-                    !m_config->warnAboutMissingAssertions() ||
-                    m_testCaseTracker->currentSectionHasChildren() )
+            if( assertions.total() != 0 )
+                return false;
+            if( !m_config->warnAboutMissingAssertions() )
+                return false;
+            if( m_trackerContext.currentTracker().hasChildren() )
                 return false;
             m_totals.assertions.failed++;
             assertions.failed++;
             return true;
         }
 
-        virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) {
-            if( std::uncaught_exception() ) {
-                m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) );
-                return;
-            }
-
-            Counts assertions = m_totals.assertions - prevAssertions;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) {
+            Counts assertions = m_totals.assertions - endInfo.prevAssertions;
             bool missingAssertions = testForMissingAssertions( assertions );
 
-            m_testCaseTracker->leaveSection();
+            if( !m_activeSections.empty() ) {
+                m_activeSections.back()->close();
+                m_activeSections.pop_back();
+            }
 
-            m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) );
+            m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
             m_messages.clear();
         }
 
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
+            if( m_unfinishedSections.empty() )
+                m_activeSections.back()->fail();
+            else
+                m_activeSections.back()->close();
+            m_activeSections.pop_back();
+
+            m_unfinishedSections.push_back( endInfo );
+        }
+
         virtual void pushScopedMessage( MessageInfo const& message ) {
             m_messages.push_back( message );
         }
@@ -5371,7 +5848,8 @@ namespace Catch {
             double duration = 0;
             try {
                 m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
-                TestCaseTracker::Guard guard( *m_testCaseTracker );
+
+                seedRng( *m_config );
 
                 Timer timer;
                 timer.start();
@@ -5391,6 +5869,7 @@ namespace Catch {
             catch(...) {
                 makeUnexpectedResultBuilder().useActiveException();
             }
+            m_testCaseTracker->close();
             handleUnfinishedSections();
             m_messages.clear();
 
@@ -5425,39 +5904,29 @@ namespace Catch {
         void handleUnfinishedSections() {
             // If sections ended prematurely due to an exception we stored their
             // infos here so we can tear them down outside the unwind process.
-            for( std::vector<UnfinishedSections>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+            for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
                         itEnd = m_unfinishedSections.rend();
                     it != itEnd;
                     ++it )
-                sectionEnded( it->info, it->prevAssertions, it->durationInSeconds );
+                sectionEnded( *it );
             m_unfinishedSections.clear();
         }
 
-        struct UnfinishedSections {
-            UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
-            : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
-            {}
-
-            SectionInfo info;
-            Counts prevAssertions;
-            double durationInSeconds;
-        };
-
         TestRunInfo m_runInfo;
         IMutableContext& m_context;
         TestCase const* m_activeTestCase;
-        Option<TestCaseTracker> m_testCaseTracker;
+        ITracker* m_testCaseTracker;
+        ITracker* m_currentSectionTracker;
         AssertionResult m_lastResult;
 
         Ptr<IConfig const> m_config;
         Totals m_totals;
         Ptr<IStreamingReporter> m_reporter;
         std::vector<MessageInfo> m_messages;
-        IRunner* m_prevRunner;
-        IResultCapture* m_prevResultCapture;
-        Ptr<IConfig const> m_prevConfig;
         AssertionInfo m_lastAssertionInfo;
-        std::vector<UnfinishedSections> m_unfinishedSections;
+        std::vector<SectionEndInfo> m_unfinishedSections;
+        std::vector<ITracker*> m_activeSections;
+        TrackerContext m_trackerContext;
     };
 
     IResultCapture& getResultCapture() {
@@ -5505,89 +5974,87 @@ namespace Catch {
 
 namespace Catch {
 
-    class Runner {
-
-    public:
-        Runner( Ptr<Config> const& config )
-        :   m_config( config )
-        {
-            openStream();
-            makeReporter();
+    Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
+        Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
+        if( !reporter ) {
+            std::ostringstream oss;
+            oss << "No reporter registered with name: '" << reporterName << "'";
+            throw std::domain_error( oss.str() );
         }
+        return reporter;
+    }
 
-        Totals runTests() {
-
-            RunContext context( m_config.get(), m_reporter );
+    Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
+        std::vector<std::string> reporters = config->getReporterNames();
+        if( reporters.empty() )
+            reporters.push_back( "console" );
 
-            Totals totals;
+        Ptr<IStreamingReporter> reporter;
+        for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
+                it != itEnd;
+                ++it )
+            reporter = addReporter( reporter, createReporter( *it, config ) );
+        return reporter;
+    }
+    Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
+        IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
+        for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
+                it != itEnd;
+                ++it )
+            reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
+        return reporters;
+    }
 
-            context.testGroupStarting( "all tests", 1, 1 ); // deprecated?
+    Totals runTests( Ptr<Config> const& config ) {
 
-            TestSpec testSpec = m_config->testSpec();
-            if( !testSpec.hasFilters() )
-                testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+        Ptr<IConfig const> iconfig = config.get();
 
-            std::vector<TestCase> testCases;
-            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, testCases );
+        Ptr<IStreamingReporter> reporter = makeReporter( config );
+        reporter = addListeners( iconfig, reporter );
 
-            int testsRunForGroup = 0;
-            for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
-                    it != itEnd;
-                    ++it ) {
-                testsRunForGroup++;
-                if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) {
+        RunContext context( iconfig, reporter );
 
-                    if( context.aborting() )
-                        break;
+        Totals totals;
 
-                    totals += context.runTest( *it );
-                    m_testsAlreadyRun.insert( *it );
-                }
-            }
-            std::vector<TestCase> skippedTestCases;
-            getRegistryHub().getTestCaseRegistry().getFilteredTests( testSpec, *m_config, skippedTestCases, true );
+        context.testGroupStarting( config->name(), 1, 1 );
 
-            for( std::vector<TestCase>::const_iterator it = skippedTestCases.begin(), itEnd = skippedTestCases.end();
-                    it != itEnd;
-                    ++it )
-                m_reporter->skipTest( *it );
+        TestSpec testSpec = config->testSpec();
+        if( !testSpec.hasFilters() )
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
 
-            context.testGroupEnded( "all tests", totals, 1, 1 );
-            return totals;
+        std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
+        for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
+                it != itEnd;
+                ++it ) {
+            if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
+                totals += context.runTest( *it );
+            else
+                reporter->skipTest( *it );
         }
 
-    private:
-        void openStream() {
-            // Open output file, if specified
-            if( !m_config->getFilename().empty() ) {
-                m_ofs.open( m_config->getFilename().c_str() );
-                if( m_ofs.fail() ) {
-                    std::ostringstream oss;
-                    oss << "Unable to open file: '" << m_config->getFilename() << "'";
-                    throw std::domain_error( oss.str() );
-                }
-                m_config->setStreamBuf( m_ofs.rdbuf() );
-            }
-        }
-        void makeReporter() {
-            std::string reporterName = m_config->getReporterName().empty()
-                ? "console"
-                : m_config->getReporterName();
+        context.testGroupEnded( iconfig->name(), totals, 1, 1 );
+        return totals;
+    }
 
-            m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() );
-            if( !m_reporter ) {
-                std::ostringstream oss;
-                oss << "No reporter registered with name: '" << reporterName << "'";
-                throw std::domain_error( oss.str() );
-            }
-        }
+    void applyFilenamesAsTags( IConfig const& config ) {
+        std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
+        for(std::size_t i = 0; i < tests.size(); ++i ) {
+            TestCase& test = const_cast<TestCase&>( tests[i] );
+            std::set<std::string> tags = test.tags;
 
-    private:
-        Ptr<Config> m_config;
-        std::ofstream m_ofs;
-        Ptr<IStreamingReporter> m_reporter;
-        std::set<TestCase> m_testsAlreadyRun;
-    };
+            std::string filename = test.lineInfo.file;
+            std::string::size_type lastSlash = filename.find_last_of( "\\/" );
+            if( lastSlash != std::string::npos )
+                filename = filename.substr( lastSlash+1 );
+
+            std::string::size_type lastDot = filename.find_last_of( "." );
+            if( lastDot != std::string::npos )
+                filename = filename.substr( 0, lastDot );
+
+            tags.insert( "#" + filename );
+            setTags( test, tags );
+        }
+    }
 
     class Session : NonCopyable {
         static bool alreadyInstantiated;
@@ -5616,7 +6083,7 @@ namespace Catch {
             Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
         }
 
-        int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+        int applyCommandLine( int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
             try {
                 m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
                 m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
@@ -5643,7 +6110,7 @@ namespace Catch {
             m_config.reset();
         }
 
-        int run( int argc, char* const argv[] ) {
+        int run( int argc, char const* const argv[] ) {
 
             int returnCode = applyCommandLine( argc, argv );
             if( returnCode == 0 )
@@ -5659,15 +6126,16 @@ namespace Catch {
             {
                 config(); // Force config to be constructed
 
-                std::srand( m_configData.rngSeed );
+                seedRng( *m_config );
 
-                Runner runner( m_config );
+                if( m_configData.filenamesAsTags )
+                    applyFilenamesAsTags( *m_config );
 
                 // Handle list request
                 if( Option<std::size_t> listed = list( config() ) )
                     return static_cast<int>( *listed );
 
-                return static_cast<int>( runner.runTests().assertions.failed );
+                return static_cast<int>( runTests( m_config ).assertions.failed );
             }
             catch( std::exception& ex ) {
                 Catch::cerr() << ex.what() << std::endl;
@@ -5689,7 +6157,6 @@ namespace Catch {
                 m_config = new Config( m_configData );
             return *m_config;
         }
-
     private:
         Clara::CommandLine<ConfigData> m_cli;
         std::vector<Clara::Parser::Token> m_unusedTokens;
@@ -5715,16 +6182,76 @@ namespace Catch {
 
 namespace Catch {
 
-    class TestRegistry : public ITestCaseRegistry {
-        struct LexSort {
-            bool operator() (TestCase i,TestCase j) const { return (i<j);}
-        };
-        struct RandomNumberGenerator {
-            int operator()( int n ) const { return std::rand() % n; }
-        };
+    struct LexSort {
+        bool operator() (TestCase i,TestCase j) const { return (i<j);}
+    };
+    struct RandomNumberGenerator {
+        int operator()( int n ) const { return std::rand() % n; }
+    };
+
+    inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+
+        std::vector<TestCase> sorted = unsortedTestCases;
+
+        switch( config.runOrder() ) {
+            case RunTests::InLexicographicalOrder:
+                std::sort( sorted.begin(), sorted.end(), LexSort() );
+                break;
+            case RunTests::InRandomOrder:
+                {
+                    seedRng( config );
+
+                    RandomNumberGenerator rng;
+                    std::random_shuffle( sorted.begin(), sorted.end(), rng );
+                }
+                break;
+            case RunTests::InDeclarationOrder:
+                // already in declaration order
+                break;
+        }
+        return sorted;
+    }
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+    }
+
+    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+        std::set<TestCase> seenFunctions;
+        for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
+            it != itEnd;
+            ++it ) {
+            std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
+            if( !prev.second ){
+                Catch::cerr()
+                << Colour( Colour::Red )
+                << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+                << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
+                << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+                exit(1);
+            }
+        }
+    }
+
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+        std::vector<TestCase> filtered;
+        filtered.reserve( testCases.size() );
+        for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+                it != itEnd;
+                ++it )
+            if( matchTest( *it, testSpec, config ) )
+                filtered.push_back( *it );
+        return filtered;
+    }
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+    }
 
+    class TestRegistry : public ITestCaseRegistry {
     public:
-        TestRegistry() : m_unnamedCount( 0 ) {}
+        TestRegistry()
+        :   m_currentSortOrder( RunTests::InDeclarationOrder ),
+            m_unnamedCount( 0 )
+        {}
         virtual ~TestRegistry();
 
         virtual void registerTest( TestCase const& testCase ) {
@@ -5734,69 +6261,29 @@ namespace Catch {
                 oss << "Anonymous test case " << ++m_unnamedCount;
                 return registerTest( testCase.withName( oss.str() ) );
             }
-
-            if( m_functions.find( testCase ) == m_functions.end() ) {
-                m_functions.insert( testCase );
-                m_functionsInOrder.push_back( testCase );
-                if( !testCase.isHidden() )
-                    m_nonHiddenFunctions.push_back( testCase );
-            }
-            else {
-                TestCase const& prev = *m_functions.find( testCase );
-                {
-                    Colour colourGuard( Colour::Red );
-                    Catch::cerr()   << "error: TEST_CASE( \"" << name << "\" ) already defined.\n"
-                                << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n"
-                                << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl;
-                }
-                exit(1);
-            }
+            m_functions.push_back( testCase );
         }
 
         virtual std::vector<TestCase> const& getAllTests() const {
-            return m_functionsInOrder;
+            return m_functions;
         }
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
+            if( m_sortedFunctions.empty() )
+                enforceNoDuplicateTestCases( m_functions );
 
-        virtual std::vector<TestCase> const& getAllNonHiddenTests() const {
-            return m_nonHiddenFunctions;
-        }
-
-        virtual void getFilteredTests( TestSpec const& testSpec, IConfig const& config, std::vector<TestCase>& matchingTestCases, bool negated = false ) const {
-
-            for( std::vector<TestCase>::const_iterator  it = m_functionsInOrder.begin(),
-                                                        itEnd = m_functionsInOrder.end();
-                    it != itEnd;
-                    ++it ) {
-                bool includeTest = testSpec.matches( *it ) && ( config.allowThrows() || !it->throws() );
-                if( includeTest != negated )
-                    matchingTestCases.push_back( *it );
+            if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+                m_sortedFunctions = sortTests( config, m_functions );
+                m_currentSortOrder = config.runOrder();
             }
-            sortTests( config, matchingTestCases );
+            return m_sortedFunctions;
         }
 
     private:
-
-        static void sortTests( IConfig const& config, std::vector<TestCase>& matchingTestCases ) {
-
-            switch( config.runOrder() ) {
-                case RunTests::InLexicographicalOrder:
-                    std::sort( matchingTestCases.begin(), matchingTestCases.end(), LexSort() );
-                    break;
-                case RunTests::InRandomOrder:
-                {
-                    RandomNumberGenerator rng;
-                    std::random_shuffle( matchingTestCases.begin(), matchingTestCases.end(), rng );
-                }
-                    break;
-                case RunTests::InDeclarationOrder:
-                    // already in declaration order
-                    break;
-            }
-        }
-        std::set<TestCase> m_functions;
-        std::vector<TestCase> m_functionsInOrder;
-        std::vector<TestCase> m_nonHiddenFunctions;
+        std::vector<TestCase> m_functions;
+        mutable RunTests::InWhatOrder m_currentSortOrder;
+        mutable std::vector<TestCase> m_sortedFunctions;
         size_t m_unnamedCount;
+        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
     };
 
     ///////////////////////////////////////////////////////////////////////////
@@ -5829,29 +6316,38 @@ namespace Catch {
         return className;
     }
 
-    ///////////////////////////////////////////////////////////////////////////
+    void registerTestCase
+        (   ITestCase* testCase,
+            char const* classOrQualifiedMethodName,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
 
-    AutoReg::AutoReg(   TestFunction function,
-                        SourceLineInfo const& lineInfo,
-                        NameAndDesc const& nameAndDesc ) {
+        getMutableRegistryHub().registerTest
+            ( makeTestCase
+                (   testCase,
+                    extractClassName( classOrQualifiedMethodName ),
+                    nameAndDesc.name,
+                    nameAndDesc.description,
+                    lineInfo ) );
+    }
+    void registerTestCaseFunction
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
         registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
     }
 
-    AutoReg::~AutoReg() {}
-
-    void AutoReg::registerTestCase( ITestCase* testCase,
-                                    char const* classOrQualifiedMethodName,
-                                    NameAndDesc const& nameAndDesc,
-                                    SourceLineInfo const& lineInfo ) {
+    ///////////////////////////////////////////////////////////////////////////
 
-        getMutableRegistryHub().registerTest
-            ( makeTestCase( testCase,
-                            extractClassName( classOrQualifiedMethodName ),
-                            nameAndDesc.name,
-                            nameAndDesc.description,
-                            lineInfo ) );
+    AutoReg::AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
+        registerTestCaseFunction( function, lineInfo, nameAndDesc );
     }
 
+    AutoReg::~AutoReg() {}
+
 } // end namespace Catch
 
 // #included from: catch_reporter_registry.hpp
@@ -5865,27 +6361,32 @@ namespace Catch {
 
     public:
 
-        virtual ~ReporterRegistry() {
-            deleteAllValues( m_factories );
-        }
+        virtual ~ReporterRegistry() CATCH_OVERRIDE {}
 
-        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const {
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
             FactoryMap::const_iterator it =  m_factories.find( name );
             if( it == m_factories.end() )
-                return NULL;
+                return CATCH_NULL;
             return it->second->create( ReporterConfig( config ) );
         }
 
-        void registerReporter( std::string const& name, IReporterFactory* factory ) {
+        void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
             m_factories.insert( std::make_pair( name, factory ) );
         }
+        void registerListener( Ptr<IReporterFactory> const& factory ) {
+            m_listeners.push_back( factory );
+        }
 
-        FactoryMap const& getFactories() const {
+        virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
             return m_factories;
         }
+        virtual Listeners const& getListeners() const CATCH_OVERRIDE {
+            return m_listeners;
+        }
 
     private:
         FactoryMap m_factories;
+        Listeners m_listeners;
     };
 }
 
@@ -5913,13 +6414,13 @@ namespace Catch {
 #ifdef __OBJC__
                 // In Objective-C try objective-c exceptions first
                 @try {
-                    throw;
+                    return tryTranslators();
                 }
                 @catch (NSException *exception) {
                     return Catch::toString( [exception description] );
                 }
 #else
-                throw;
+                return tryTranslators();
 #endif
             }
             catch( TestFailureException& ) {
@@ -5935,20 +6436,15 @@ namespace Catch {
                 return msg;
             }
             catch(...) {
-                return tryTranslators( m_translators.begin() );
+                return "Unknown exception";
             }
         }
 
-        std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const {
-            if( it == m_translators.end() )
-                return "Unknown exception";
-
-            try {
-                return (*it)->translate();
-            }
-            catch(...) {
-                return tryTranslators( it+1 );
-            }
+        std::string tryTranslators() const {
+            if( m_translators.empty() )
+                throw;
+            else
+                return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
         }
 
     private:
@@ -5968,24 +6464,27 @@ namespace Catch {
         public: // IRegistryHub
             RegistryHub() {
             }
-            virtual IReporterRegistry const& getReporterRegistry() const {
+            virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
                 return m_reporterRegistry;
             }
-            virtual ITestCaseRegistry const& getTestCaseRegistry() const {
+            virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
                 return m_testCaseRegistry;
             }
-            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() {
+            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
                 return m_exceptionTranslatorRegistry;
             }
 
         public: // IMutableRegistryHub
-            virtual void registerReporter( std::string const& name, IReporterFactory* factory ) {
+            virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
                 m_reporterRegistry.registerReporter( name, factory );
             }
-            virtual void registerTest( TestCase const& testInfo ) {
+            virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+                m_reporterRegistry.registerListener( factory );
+            }
+            virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
                 m_testCaseRegistry.registerTest( testInfo );
             }
-            virtual void registerTranslator( const IExceptionTranslator* translator ) {
+            virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
                 m_exceptionTranslatorRegistry.registerTranslator( translator );
             }
 
@@ -5997,7 +6496,7 @@ namespace Catch {
 
         // Single, global, instance
         inline RegistryHub*& getTheRegistryHub() {
-            static RegistryHub* theRegistryHub = NULL;
+            static RegistryHub* theRegistryHub = CATCH_NULL;
             if( !theRegistryHub )
                 theRegistryHub = new RegistryHub();
             return theRegistryHub;
@@ -6012,7 +6511,7 @@ namespace Catch {
     }
     void cleanUp() {
         delete getTheRegistryHub();
-        getTheRegistryHub() = NULL;
+        getTheRegistryHub() = CATCH_NULL;
         cleanUpContext();
     }
     std::string translateActiveException() {
@@ -6042,24 +6541,11 @@ namespace Catch {
 
 } // end namespace Catch
 
-// #included from: catch_context_impl.hpp
-#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
-
-// #included from: catch_stream.hpp
-#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
-
-// #included from: catch_streambuf.h
-#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
-
-#include <streambuf>
-
-namespace Catch {
-
-    class StreamBufBase : public std::streambuf {
-    public:
-        virtual ~StreamBufBase() CATCH_NOEXCEPT;
-    };
-}
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
 
 #include <stdexcept>
 #include <cstdio>
@@ -6105,6 +6591,19 @@ namespace Catch {
 
     ///////////////////////////////////////////////////////////////////////////
 
+    FileStream::FileStream( std::string const& filename ) {
+        m_ofs.open( filename.c_str() );
+        if( m_ofs.fail() ) {
+            std::ostringstream oss;
+            oss << "Unable to open file: '" << filename << "'";
+            throw std::domain_error( oss.str() );
+        }
+    }
+
+    std::ostream& FileStream::stream() const {
+        return m_ofs;
+    }
+
     struct OutputDebugWriter {
 
         void operator()( std::string const&str ) {
@@ -6112,23 +6611,26 @@ namespace Catch {
         }
     };
 
-    Stream::Stream()
-    : streamBuf( NULL ), isOwned( false )
+    DebugOutStream::DebugOutStream()
+    :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+        m_os( m_streamBuf.get() )
     {}
 
-    Stream::Stream( std::streambuf* _streamBuf, bool _isOwned )
-    : streamBuf( _streamBuf ), isOwned( _isOwned )
+    std::ostream& DebugOutStream::stream() const {
+        return m_os;
+    }
+
+    // Store the streambuf from cout up-front because
+    // cout may get redirected when running tests
+    CoutStream::CoutStream()
+    :   m_os( Catch::cout().rdbuf() )
     {}
 
-    void Stream::release() {
-        if( isOwned ) {
-            delete streamBuf;
-            streamBuf = NULL;
-            isOwned = false;
-        }
+    std::ostream& CoutStream::stream() const {
+        return m_os;
     }
 
-#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement this functions
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
     std::ostream& cout() {
         return std::cout;
     }
@@ -6142,7 +6644,7 @@ namespace Catch {
 
     class Context : public IMutableContext {
 
-        Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {}
+        Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
         Context( Context const& );
         void operator=( Context const& );
 
@@ -6188,7 +6690,7 @@ namespace Catch {
                 m_generatorsByTestName.find( testName );
             return it != m_generatorsByTestName.end()
                 ? it->second
-                : NULL;
+                : CATCH_NULL;
         }
 
         IGeneratorsForTest& getGeneratorsForCurrentTest() {
@@ -6209,7 +6711,7 @@ namespace Catch {
     };
 
     namespace {
-        Context* currentContext = NULL;
+        Context* currentContext = CATCH_NULL;
     }
     IMutableContext& getCurrentMutableContext() {
         if( !currentContext )
@@ -6220,17 +6722,9 @@ namespace Catch {
         return getCurrentMutableContext();
     }
 
-    Stream createStream( std::string const& streamName ) {
-        if( streamName == "stdout" ) return Stream( Catch::cout().rdbuf(), false );
-        if( streamName == "stderr" ) return Stream( Catch::cerr().rdbuf(), false );
-        if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true );
-
-        throw std::domain_error( "Unknown stream: " + streamName );
-    }
-
     void cleanUpContext() {
         delete currentContext;
-        currentContext = NULL;
+        currentContext = CATCH_NULL;
     }
 }
 
@@ -6286,12 +6780,13 @@ namespace {
         {
             CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
             GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
-            originalAttributes = csbiInfo.wAttributes;
+            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
         }
 
         virtual void use( Colour::Code _colourCode ) {
             switch( _colourCode ) {
-                case Colour::None:      return setTextAttribute( originalAttributes );
+                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
                 case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
                 case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
                 case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
@@ -6311,10 +6806,11 @@ namespace {
 
     private:
         void setTextAttribute( WORD _textAttribute ) {
-            SetConsoleTextAttribute( stdoutHandle, _textAttribute );
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
         }
         HANDLE stdoutHandle;
-        WORD originalAttributes;
+        WORD originalForegroundAttributes;
+        WORD originalBackgroundAttributes;
     };
 
     IColourImpl* platformColourInstance() {
@@ -6640,6 +7136,21 @@ namespace Catch {
         return TestCase( _testCase, info );
     }
 
+    void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
+    {
+        testCaseInfo.tags = tags;
+        testCaseInfo.lcaseTags.clear();
+
+        std::ostringstream oss;
+        for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
+            oss << "[" << *it << "]";
+            std::string lcaseTag = toLower( *it );
+            testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+            testCaseInfo.lcaseTags.insert( lcaseTag );
+        }
+        testCaseInfo.tagsAsString = oss.str();
+    }
+
     TestCaseInfo::TestCaseInfo( std::string const& _name,
                                 std::string const& _className,
                                 std::string const& _description,
@@ -6648,18 +7159,10 @@ namespace Catch {
     :   name( _name ),
         className( _className ),
         description( _description ),
-        tags( _tags ),
         lineInfo( _lineInfo ),
         properties( None )
     {
-        std::ostringstream oss;
-        for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) {
-            oss << "[" << *it << "]";
-            std::string lcaseTag = toLower( *it );
-            properties = static_cast<SpecialProperties>( properties | parseSpecialTag( lcaseTag ) );
-            lcaseTags.insert( lcaseTag );
-        }
-        tagsAsString = oss.str();
+        setTags( *this, _tags );
     }
 
     TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
@@ -6767,7 +7270,7 @@ namespace Catch {
         return os;
     }
 
-    Version libraryVersion( 1, 2, 1, "", 0 );
+    Version libraryVersion( 1, 3, 3, "", 0 );
 
 }
 
@@ -6960,7 +7463,7 @@ namespace Catch {
 #else
         uint64_t getCurrentTicks() {
             timeval t;
-            gettimeofday(&t,NULL);
+            gettimeofday(&t,CATCH_NULL);
             return static_cast<uint64_t>( t.tv_sec ) * 1000000ull + static_cast<uint64_t>( t.tv_usec );
         }
 #endif
@@ -7059,6 +7562,14 @@ namespace Catch {
         return line < other.line || ( line == other.line  && file < other.file );
     }
 
+    void seedRng( IConfig const& config ) {
+        if( config.rngSeed() != 0 )
+            std::srand( config.rngSeed() );
+    }
+    unsigned int rngSeed() {
+        return getCurrentContext().getConfig()->rngSeed();
+    }
+
     std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
 #ifndef __GNUG__
         os << info.file << "(" << info.line << ")";
@@ -7098,8 +7609,13 @@ namespace Catch {
     }
 
     Section::~Section() {
-        if( m_sectionIncluded )
-            getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() );
+        if( m_sectionIncluded ) {
+            SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
+            if( std::uncaught_exception() )
+                getResultCapture().sectionEndedEarly( endInfo );
+            else
+                getResultCapture().sectionEnded( endInfo );
+        }
     }
 
     // This indicates whether the section should be executed or not
@@ -7151,7 +7667,7 @@ namespace Catch {
             // Call sysctl.
 
             size = sizeof(info);
-            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) {
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) {
                 Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
                 return false;
             }
@@ -7205,9 +7721,11 @@ namespace Catch {
 
 namespace Detail {
 
-    std::string unprintableString = "{?}";
+    const std::string unprintableString = "{?}";
 
     namespace {
+        const int hexThreshold = 255;
+
         struct Endianness {
             enum Arch { Big, Little };
 
@@ -7289,7 +7807,7 @@ std::string toString( wchar_t* const value )
 std::string toString( int value ) {
     std::ostringstream oss;
     oss << value;
-    if( value >= 255 )
+    if( value > Detail::hexThreshold )
         oss << " (0x" << std::hex << value << ")";
     return oss.str();
 }
@@ -7297,7 +7815,7 @@ std::string toString( int value ) {
 std::string toString( unsigned long value ) {
     std::ostringstream oss;
     oss << value;
-    if( value >= 255 )
+    if( value > Detail::hexThreshold )
         oss << " (0x" << std::hex << value << ")";
     return oss.str();
 }
@@ -7347,6 +7865,23 @@ std::string toString( unsigned char value ) {
     return toString( static_cast<char>( value ) );
 }
 
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ")";
+    return oss.str();
+}
+std::string toString( unsigned long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ")";
+    return oss.str();
+}
+#endif
+
 #ifdef CATCH_CONFIG_CPP11_NULLPTR
 std::string toString( std::nullptr_t ) {
     return "nullptr";
@@ -7376,11 +7911,17 @@ std::string toString( std::nullptr_t ) {
 
 namespace Catch {
 
+    std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) {
+        return secondArg.empty() || secondArg == "\"\""
+            ? capturedExpression
+            : capturedExpression + ", " + secondArg;
+    }
     ResultBuilder::ResultBuilder(   char const* macroName,
                                     SourceLineInfo const& lineInfo,
                                     char const* capturedExpression,
-                                    ResultDisposition::Flags resultDisposition )
-    :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition ),
+                                    ResultDisposition::Flags resultDisposition,
+                                    char const* secondArg )
+    :   m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ),
         m_shouldDebugBreak( false ),
         m_shouldThrow( false )
     {}
@@ -7421,9 +7962,35 @@ namespace Catch {
         setResultType( resultType );
         captureExpression();
     }
+    void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
+        if( expectedMessage.empty() )
+            captureExpectedException( Matchers::Impl::Generic::AllOf<std::string>() );
+        else
+            captureExpectedException( Matchers::Equals( expectedMessage ) );
+    }
+
+    void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher<std::string> const& matcher ) {
+
+        assert( m_exprComponents.testFalse == false );
+        AssertionResultData data = m_data;
+        data.resultType = ResultWas::Ok;
+        data.reconstructedExpression = m_assertionInfo.capturedExpression;
+
+        std::string actualMessage = Catch::translateActiveException();
+        if( !matcher.match( actualMessage ) ) {
+            data.resultType = ResultWas::ExpressionFailed;
+            data.reconstructedExpression = actualMessage;
+        }
+        AssertionResult result( m_assertionInfo, data );
+        handleResult( result );
+    }
 
     void ResultBuilder::captureExpression() {
         AssertionResult result = build();
+        handleResult( result );
+    }
+    void ResultBuilder::handleResult( AssertionResult const& result )
+    {
         getResultCapture().assertionEnded( result );
 
         if( !result.isOk() ) {
@@ -7576,6 +8143,137 @@ namespace Catch {
 
 } // end namespace Catch
 
+// #included from: ../reporters/catch_reporter_multi.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+namespace Catch {
+
+class MultipleReporters : public SharedImpl<IStreamingReporter> {
+    typedef std::vector<Ptr<IStreamingReporter> > Reporters;
+    Reporters m_reporters;
+
+public:
+    void add( Ptr<IStreamingReporter> const& reporter ) {
+        m_reporters.push_back( reporter );
+    }
+
+public: // IStreamingReporter
+
+    virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+        return m_reporters[0]->getPreferences();
+    }
+
+    virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->noMatchingTestCases( spec );
+    }
+
+    virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunStarting( testRunInfo );
+    }
+
+    virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupStarting( groupInfo );
+    }
+
+    virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseStarting( testInfo );
+    }
+
+    virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionStarting( sectionInfo );
+    }
+
+    virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->assertionStarting( assertionInfo );
+    }
+
+    // The return value indicates if the messages buffer should be cleared:
+    virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+        bool clearBuffer = false;
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            clearBuffer |= (*it)->assertionEnded( assertionStats );
+        return clearBuffer;
+    }
+
+    virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionEnded( sectionStats );
+    }
+
+    virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseEnded( testCaseStats );
+    }
+
+    virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupEnded( testGroupStats );
+    }
+
+    virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunEnded( testRunStats );
+    }
+
+    virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->skipTest( testInfo );
+    }
+};
+
+Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
+    Ptr<IStreamingReporter> resultingReporter;
+
+    if( existingReporter ) {
+        MultipleReporters* multi = dynamic_cast<MultipleReporters*>( existingReporter.get() );
+        if( !multi ) {
+            multi = new MultipleReporters;
+            resultingReporter = Ptr<IStreamingReporter>( multi );
+            if( existingReporter )
+                multi->add( existingReporter );
+        }
+        else
+            resultingReporter = existingReporter;
+        multi->add( additionalReporter );
+    }
+    else
+        resultingReporter = additionalReporter;
+
+    return resultingReporter;
+}
+
+} // end namespace Catch
+
 // #included from: ../reporters/catch_reporter_xml.hpp
 #define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
 
@@ -7591,47 +8289,53 @@ namespace Catch {
         StreamingReporterBase( ReporterConfig const& _config )
         :   m_config( _config.fullConfig() ),
             stream( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
+
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
 
-        virtual ~StreamingReporterBase();
+        virtual ~StreamingReporterBase() CATCH_OVERRIDE;
 
-        virtual void noMatchingTestCases( std::string const& ) {}
+        virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
 
-        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) {
+        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
             currentTestRunInfo = _testRunInfo;
         }
-        virtual void testGroupStarting( GroupInfo const& _groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
             currentGroupInfo = _groupInfo;
         }
 
-        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) {
+        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
             currentTestCaseInfo = _testInfo;
         }
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
             m_sectionStack.push_back( _sectionInfo );
         }
 
-        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) {
+        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
             m_sectionStack.pop_back();
         }
-        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) {
+        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
             currentTestCaseInfo.reset();
         }
-        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) {
+        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
             currentGroupInfo.reset();
         }
-        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) {
+        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
             currentTestCaseInfo.reset();
             currentGroupInfo.reset();
             currentTestRunInfo.reset();
         }
 
-        virtual void skipTest( TestCaseInfo const& ) {
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
             // Don't do anything with this by default.
             // It can optionally be overridden in the derived class.
         }
 
-        Ptr<IConfig> m_config;
+        Ptr<IConfig const> m_config;
         std::ostream& stream;
 
         LazyStat<TestRunInfo> currentTestRunInfo;
@@ -7639,6 +8343,7 @@ namespace Catch {
         LazyStat<TestCaseInfo> currentTestCaseInfo;
 
         std::vector<SectionInfo> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
     };
 
     struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
@@ -7689,15 +8394,21 @@ namespace Catch {
         CumulativeReporterBase( ReporterConfig const& _config )
         :   m_config( _config.fullConfig() ),
             stream( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
         ~CumulativeReporterBase();
 
-        virtual void testRunStarting( TestRunInfo const& ) {}
-        virtual void testGroupStarting( GroupInfo const& ) {}
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
+
+        virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
+        virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
 
-        virtual void testCaseStarting( TestCaseInfo const& ) {}
+        virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
 
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
             SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
             Ptr<SectionNode> node;
             if( m_sectionStack.empty() ) {
@@ -7722,7 +8433,7 @@ namespace Catch {
             m_deepestSection = node;
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) {}
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
 
         virtual bool assertionEnded( AssertionStats const& assertionStats ) {
             assert( !m_sectionStack.empty() );
@@ -7730,13 +8441,13 @@ namespace Catch {
             sectionNode.assertions.push_back( assertionStats );
             return true;
         }
-        virtual void sectionEnded( SectionStats const& sectionStats ) {
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
             assert( !m_sectionStack.empty() );
             SectionNode& node = *m_sectionStack.back();
             node.stats = sectionStats;
             m_sectionStack.pop_back();
         }
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
             assert( m_sectionStack.size() == 0 );
             node->children.push_back( m_rootSection );
@@ -7747,12 +8458,12 @@ namespace Catch {
             m_deepestSection->stdOut = testCaseStats.stdOut;
             m_deepestSection->stdErr = testCaseStats.stdErr;
         }
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
             node->children.swap( m_testCases );
             m_testGroups.push_back( node );
         }
-        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
             Ptr<TestRunNode> node = new TestRunNode( testRunStats );
             node->children.swap( m_testGroups );
             m_testRuns.push_back( node );
@@ -7760,9 +8471,9 @@ namespace Catch {
         }
         virtual void testRunEndedCumulative() = 0;
 
-        virtual void skipTest( TestCaseInfo const& ) {}
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
 
-        Ptr<IConfig> m_config;
+        Ptr<IConfig const> m_config;
         std::ostream& stream;
         std::vector<AssertionStats> m_assertions;
         std::vector<std::vector<Ptr<SectionNode> > > m_sections;
@@ -7774,6 +8485,7 @@ namespace Catch {
         Ptr<SectionNode> m_rootSection;
         Ptr<SectionNode> m_deepestSection;
         std::vector<Ptr<SectionNode> > m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
 
     };
 
@@ -7787,6 +8499,17 @@ namespace Catch {
         return line;
     }
 
+    struct TestEventListenerBase : StreamingReporterBase {
+        TestEventListenerBase( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config )
+        {}
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+        virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
+            return false;
+        }
+    };
+
 } // end namespace Catch
 
 // #included from: ../internal/catch_reporter_registrars.hpp
@@ -7817,7 +8540,7 @@ namespace Catch {
     template<typename T>
     class ReporterRegistrar {
 
-        class ReporterFactory : public IReporterFactory {
+        class ReporterFactory : public SharedImpl<IReporterFactory> {
 
             // *** Please Note ***:
             // - If you end up here looking at a compiler error because it's trying to register
@@ -7845,22 +8568,102 @@ namespace Catch {
             getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
         }
     };
+
+    template<typename T>
+    class ListenerRegistrar {
+
+        class ListenerFactory : public SharedImpl<IReporterFactory> {
+
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new T( config );
+            }
+            virtual std::string getDescription() const {
+                return "";
+            }
+        };
+
+    public:
+
+        ListenerRegistrar() {
+            getMutableRegistryHub().registerListener( new ListenerFactory() );
+        }
+    };
 }
 
 #define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
     namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
 #define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
     namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
 
+#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
+    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
 // #included from: ../internal/catch_xmlwriter.hpp
 #define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
 
 #include <sstream>
 #include <string>
 #include <vector>
+#include <iomanip>
 
 namespace Catch {
 
+    class XmlEncode {
+    public:
+        enum ForWhat { ForTextNodes, ForAttributes };
+
+        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
+        :   m_str( str ),
+            m_forWhat( forWhat )
+        {}
+
+        void encodeTo( std::ostream& os ) const {
+
+            // Apostrophe escaping not necessary if we always use " to write attributes
+            // (see: http://www.w3.org/TR/xml/#syntax)
+
+            for( std::size_t i = 0; i < m_str.size(); ++ i ) {
+                char c = m_str[i];
+                switch( c ) {
+                    case '<':   os << "<"; break;
+                    case '&':   os << "&"; break;
+
+                    case '>':
+                        // See: http://www.w3.org/TR/xml/#syntax
+                        if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
+                            os << ">";
+                        else
+                            os << c;
+                        break;
+
+                    case '\"':
+                        if( m_forWhat == ForAttributes )
+                            os << """;
+                        else
+                            os << c;
+                        break;
+
+                    default:
+                        // Escape control chars - based on contribution by @espenalb in PR #465
+                        if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' )
+                            os << "&#x" << std::uppercase << std::hex << static_cast<int>( c );
+                        else
+                            os << c;
+                }
+            }
+        }
+
+        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+            xmlEncode.encodeTo( os );
+            return os;
+        }
+
+    private:
+        std::string m_str;
+        ForWhat m_forWhat;
+    };
+
     class XmlWriter {
     public:
 
@@ -7872,7 +8675,7 @@ namespace Catch {
 
             ScopedElement( ScopedElement const& other )
             :   m_writer( other.m_writer ){
-                other.m_writer = NULL;
+                other.m_writer = CATCH_NULL;
             }
 
             ~ScopedElement() {
@@ -7943,11 +8746,8 @@ namespace Catch {
         }
 
         XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
-            if( !name.empty() && !attribute.empty() ) {
-                stream() << " " << name << "=\"";
-                writeEncodedText( attribute );
-                stream() << "\"";
-            }
+            if( !name.empty() && !attribute.empty() )
+                stream() << " " << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << "\"";
             return *this;
         }
 
@@ -7958,9 +8758,9 @@ namespace Catch {
 
         template<typename T>
         XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
-            if( !name.empty() )
-                stream() << " " << name << "=\"" << attribute << "\"";
-            return *this;
+            std::ostringstream oss;
+            oss << attribute;
+            return writeAttribute( name, oss.str() );
         }
 
         XmlWriter& writeText( std::string const& text, bool indent = true ) {
@@ -7969,7 +8769,7 @@ namespace Catch {
                 ensureTagClosed();
                 if( tagWasOpen && indent )
                     stream() << m_indent;
-                writeEncodedText( text );
+                stream() << XmlEncode( text );
                 m_needsNewline = true;
             }
             return *this;
@@ -8014,30 +8814,6 @@ namespace Catch {
             }
         }
 
-        void writeEncodedText( std::string const& text ) {
-            static const char* charsToEncode = "<&\"";
-            std::string mtext = text;
-            std::string::size_type pos = mtext.find_first_of( charsToEncode );
-            while( pos != std::string::npos ) {
-                stream() << mtext.substr( 0, pos );
-
-                switch( mtext[pos] ) {
-                    case '<':
-                        stream() << "<";
-                        break;
-                    case '&':
-                        stream() << "&";
-                        break;
-                    case '\"':
-                        stream() << """;
-                        break;
-                }
-                mtext = mtext.substr( pos+1 );
-                pos = mtext.find_first_of( charsToEncode );
-            }
-            stream() << mtext;
-        }
-
         bool m_tagIsOpen;
         bool m_needsNewline;
         std::vector<std::string> m_tags;
@@ -8046,32 +8822,44 @@ namespace Catch {
     };
 
 }
+// #included from: catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#    ifdef __ICC // icpc defines the __clang__ macro
+#        pragma warning(pop)
+#    else
+#        pragma clang diagnostic pop
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic pop
+#endif
+
+
 namespace Catch {
     class XmlReporter : public StreamingReporterBase {
     public:
         XmlReporter( ReporterConfig const& _config )
         :   StreamingReporterBase( _config ),
             m_sectionDepth( 0 )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
 
-        virtual ~XmlReporter();
+        virtual ~XmlReporter() CATCH_OVERRIDE;
 
         static std::string getDescription() {
             return "Reports test results as an XML document";
         }
 
     public: // StreamingReporterBase
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = true;
-            return prefs;
-        }
 
-        virtual void noMatchingTestCases( std::string const& s ) {
+        virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
             StreamingReporterBase::noMatchingTestCases( s );
         }
 
-        virtual void testRunStarting( TestRunInfo const& testInfo ) {
+        virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testRunStarting( testInfo );
             m_xml.setStream( stream );
             m_xml.startElement( "Catch" );
@@ -8079,13 +8867,13 @@ namespace Catch {
                 m_xml.writeAttribute( "name", m_config->name() );
         }
 
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testGroupStarting( groupInfo );
             m_xml.startElement( "Group" )
                 .writeAttribute( "name", groupInfo.name );
         }
 
-        virtual void testCaseStarting( TestCaseInfo const& testInfo ) {
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseStarting(testInfo);
             m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) );
 
@@ -8093,7 +8881,7 @@ namespace Catch {
                 m_testCaseTimer.start();
         }
 
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::sectionStarting( sectionInfo );
             if( m_sectionDepth++ > 0 ) {
                 m_xml.startElement( "Section" )
@@ -8102,9 +8890,9 @@ namespace Catch {
             }
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) { }
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
             const AssertionResult& assertionResult = assertionStats.assertionResult;
 
             // Print any info messages in <Info> tags.
@@ -8175,7 +8963,7 @@ namespace Catch {
             return true;
         }
 
-        virtual void sectionEnded( SectionStats const& sectionStats ) {
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
             StreamingReporterBase::sectionEnded( sectionStats );
             if( --m_sectionDepth > 0 ) {
                 XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
@@ -8190,7 +8978,7 @@ namespace Catch {
             }
         }
 
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseEnded( testCaseStats );
             XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
             e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
@@ -8201,7 +8989,7 @@ namespace Catch {
             m_xml.endElement();
         }
 
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testGroupEnded( testGroupStats );
             // TODO: Check testGroupStats.aborting and act accordingly.
             m_xml.scopedElement( "OverallResults" )
@@ -8211,7 +8999,7 @@ namespace Catch {
             m_xml.endElement();
         }
 
-        virtual void testRunEnded( TestRunStats const& testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testRunEnded( testRunStats );
             m_xml.scopedElement( "OverallResults" )
                 .writeAttribute( "successes", testRunStats.totals.assertions.passed )
@@ -8242,28 +9030,24 @@ namespace Catch {
         JunitReporter( ReporterConfig const& _config )
         :   CumulativeReporterBase( _config ),
             xml( _config.stream() )
-        {}
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
 
-        ~JunitReporter();
+        virtual ~JunitReporter() CATCH_OVERRIDE;
 
         static std::string getDescription() {
             return "Reports test results in an XML format that looks like Ant's junitreport target";
         }
 
-        virtual void noMatchingTestCases( std::string const& /*spec*/ ) {}
-
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = true;
-            return prefs;
-        }
+        virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {}
 
-        virtual void testRunStarting( TestRunInfo const& runInfo ) {
+        virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
             CumulativeReporterBase::testRunStarting( runInfo );
             xml.startElement( "testsuites" );
         }
 
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) {
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
             suiteTimer.start();
             stdOutForSuite.str("");
             stdErrForSuite.str("");
@@ -8271,25 +9055,25 @@ namespace Catch {
             CumulativeReporterBase::testGroupStarting( groupInfo );
         }
 
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
             if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException )
                 unexpectedExceptions++;
             return CumulativeReporterBase::assertionEnded( assertionStats );
         }
 
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
             stdOutForSuite << testCaseStats.stdOut;
             stdErrForSuite << testCaseStats.stdErr;
             CumulativeReporterBase::testCaseEnded( testCaseStats );
         }
 
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
             double suiteTime = suiteTimer.getElapsedSeconds();
             CumulativeReporterBase::testGroupEnded( testGroupStats );
             writeGroup( *m_testGroups.back(), suiteTime );
         }
 
-        virtual void testRunEndedCumulative() {
+        virtual void testRunEndedCumulative() CATCH_OVERRIDE {
             xml.endElement();
         }
 
@@ -8454,24 +9238,19 @@ namespace Catch {
             m_headerPrinted( false )
         {}
 
-        virtual ~ConsoleReporter();
+        virtual ~ConsoleReporter() CATCH_OVERRIDE;
         static std::string getDescription() {
             return "Reports test results as plain lines of text";
         }
-        virtual ReporterPreferences getPreferences() const {
-            ReporterPreferences prefs;
-            prefs.shouldRedirectStdOut = false;
-            return prefs;
-        }
 
-        virtual void noMatchingTestCases( std::string const& spec ) {
+        virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
             stream << "No test cases matched '" << spec << "'" << std::endl;
         }
 
-        virtual void assertionStarting( AssertionInfo const& ) {
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
         }
 
-        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
             AssertionResult const& result = _assertionStats.assertionResult;
 
             bool printInfoMessages = true;
@@ -8491,11 +9270,11 @@ namespace Catch {
             return true;
         }
 
-        virtual void sectionStarting( SectionInfo const& _sectionInfo ) {
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
             m_headerPrinted = false;
             StreamingReporterBase::sectionStarting( _sectionInfo );
         }
-        virtual void sectionEnded( SectionStats const& _sectionStats ) {
+        virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
             if( _sectionStats.missingAssertions ) {
                 lazyPrint();
                 Colour colour( Colour::ResultError );
@@ -8517,11 +9296,11 @@ namespace Catch {
             StreamingReporterBase::sectionEnded( _sectionStats );
         }
 
-        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) {
+        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
             StreamingReporterBase::testCaseEnded( _testCaseStats );
             m_headerPrinted = false;
         }
-        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) {
+        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
             if( currentGroupInfo.used ) {
                 printSummaryDivider();
                 stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
@@ -8530,7 +9309,7 @@ namespace Catch {
             }
             StreamingReporterBase::testGroupEnded( _testGroupStats );
         }
-        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE {
             printTotalsDivider( _testRunStats.totals );
             printTotals( _testRunStats.totals );
             stream << std::endl;
@@ -9169,8 +9948,14 @@ namespace Catch {
 } // end namespace Catch
 
 namespace Catch {
+    // These are all here to avoid warnings about not having any out of line
+    // virtual methods
     NonCopyable::~NonCopyable() {}
     IShared::~IShared() {}
+    IStream::~IStream() CATCH_NOEXCEPT {}
+    FileStream::~FileStream() CATCH_NOEXCEPT {}
+    CoutStream::~CoutStream() CATCH_NOEXCEPT {}
+    DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
     StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
     IContext::~IContext() {}
     IResultCapture::~IResultCapture() {}
@@ -9204,6 +9989,7 @@ namespace Catch {
     FreeFunctionTestCase::~FreeFunctionTestCase() {}
     IGeneratorInfo::~IGeneratorInfo() {}
     IGeneratorsForTest::~IGeneratorsForTest() {}
+    WildcardPattern::~WildcardPattern() {}
     TestSpec::Pattern::~Pattern() {}
     TestSpec::NamePattern::~NamePattern() {}
     TestSpec::TagPattern::~TagPattern() {}
@@ -9215,6 +10001,13 @@ namespace Catch {
     Matchers::Impl::StdString::EndsWith::~EndsWith() {}
 
     void Config::dummy() {}
+
+    namespace TestCaseTracking {
+        ITracker::~ITracker() {}
+        TrackerBase::~TrackerBase() {}
+        SectionTracker::~SectionTracker() {}
+        IndexTracker::~IndexTracker() {}
+    }
 }
 
 #ifdef __clang__
@@ -9230,7 +10023,7 @@ namespace Catch {
 #ifndef __OBJC__
 
 // Standard C/C++ main entry point
-int main (int argc, char * const argv[]) {
+int main (int argc, char * argv[]) {
     return Catch::Session().run( argc, argv );
 }
 
@@ -9268,8 +10061,9 @@ int main (int argc, char * const argv[]) {
 #define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
 #define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
 
-#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" )
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" )
 #define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" )
 #define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" )
 
 #define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" )
@@ -9280,6 +10074,7 @@ int main (int argc, char * const argv[]) {
 
 #define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" )
 #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" )
 #define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
 
 #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
@@ -9295,6 +10090,7 @@ int main (int argc, char * const argv[]) {
     #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
     #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
     #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
     #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
     #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ )
     #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ )
@@ -9302,6 +10098,7 @@ int main (int argc, char * const argv[]) {
     #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
     #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
     #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
     #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
     #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg )
     #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg )
@@ -9321,11 +10118,11 @@ int main (int argc, char * const argv[]) {
 #define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
 #define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
 #endif
-#define CATCH_GIVEN( desc )    CATCH_SECTION( "Given: " desc, "" )
-#define CATCH_WHEN( desc )     CATCH_SECTION( " When: " desc, "" )
-#define CATCH_AND_WHEN( desc ) CATCH_SECTION( "  And: " desc, "" )
-#define CATCH_THEN( desc )     CATCH_SECTION( " Then: " desc, "" )
-#define CATCH_AND_THEN( desc ) CATCH_SECTION( "  And: " desc, "" )
+#define CATCH_GIVEN( desc )    CATCH_SECTION( std::string( "Given: ") + desc, "" )
+#define CATCH_WHEN( desc )     CATCH_SECTION( std::string( " When: ") + desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
+#define CATCH_THEN( desc )     CATCH_SECTION( std::string( " Then: ") + desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
 
 // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
 #else
@@ -9333,8 +10130,9 @@ int main (int argc, char * const argv[]) {
 #define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
 #define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
 
-#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "REQUIRE_THROWS" )
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" )
 #define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" )
 #define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" )
 
 #define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" )
@@ -9343,8 +10141,9 @@ int main (int argc, char * const argv[]) {
 #define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" )
 #define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" )
 
-#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" )
+#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" )
 #define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" )
 #define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
 
 #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
@@ -9360,6 +10159,7 @@ int main (int argc, char * const argv[]) {
     #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
     #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
     #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
     #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
     #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ )
     #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ )
@@ -9367,6 +10167,7 @@ int main (int argc, char * const argv[]) {
     #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
     #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
     #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
     #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
     #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg )
     #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg )
@@ -9390,27 +10191,13 @@ int main (int argc, char * const argv[]) {
 #define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
 #define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
 #endif
-#define GIVEN( desc )    SECTION( "   Given: " desc, "" )
-#define WHEN( desc )     SECTION( "    When: " desc, "" )
-#define AND_WHEN( desc ) SECTION( "And when: " desc, "" )
-#define THEN( desc )     SECTION( "    Then: " desc, "" )
-#define AND_THEN( desc ) SECTION( "     And: " desc, "" )
+#define GIVEN( desc )    SECTION( std::string("   Given: ") + desc, "" )
+#define WHEN( desc )     SECTION( std::string("    When: ") + desc, "" )
+#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
+#define THEN( desc )     SECTION( std::string("    Then: ") + desc, "" )
+#define AND_THEN( desc ) SECTION( std::string("     And: ") + desc, "" )
 
 using Catch::Detail::Approx;
 
-// #included from: internal/catch_reenable_warnings.h
-
-#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
-
-#ifdef __clang__
-#    ifdef __ICC // icpc defines the __clang__ macro
-#        pragma warning(pop)
-#    else
-#        pragma clang diagnostic pop
-#    endif
-#elif defined __GNUC__
-#    pragma GCC diagnostic pop
-#endif
-
 #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
 
diff --git a/test/include/scalar_access.hpp b/test/include/scalar_access.hpp
new file mode 100644
index 0000000..f435a87
--- /dev/null
+++ b/test/include/scalar_access.hpp
@@ -0,0 +1,111 @@
+
+#define PBF_TYPE_NAME PROTOZERO_TEST_STRING(PBF_TYPE)
+#define GET_TYPE PROTOZERO_TEST_CONCAT(get_, PBF_TYPE)
+#define ADD_TYPE PROTOZERO_TEST_CONCAT(add_, PBF_TYPE)
+
+TEST_CASE("read field: " PBF_TYPE_NAME) {
+
+    SECTION("zero") {
+        const std::string buffer = load_data(PBF_TYPE_NAME "/data-zero");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.GET_TYPE() == 0);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("positive") {
+        const std::string buffer = load_data(PBF_TYPE_NAME "/data-pos");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.GET_TYPE() == 1);
+        REQUIRE(!item.next());
+    }
+
+    SECTION("max") {
+        const std::string buffer = load_data(PBF_TYPE_NAME "/data-max");
+
+        protozero::pbf_reader item(buffer);
+
+        REQUIRE(item.next());
+        REQUIRE(item.GET_TYPE() == std::numeric_limits<cpp_type>::max());
+        REQUIRE(!item.next());
+    }
+
+#if PBF_TYPE_IS_SIGNED
+    SECTION("negative") {
+        if (std::is_signed<cpp_type>::value) {
+            const std::string buffer = load_data(PBF_TYPE_NAME "/data-neg");
+
+            protozero::pbf_reader item(buffer);
+
+            REQUIRE(item.next());
+            REQUIRE(item.GET_TYPE() == -1);
+            REQUIRE(!item.next());
+        }
+    }
+
+    SECTION("min") {
+        if (std::is_signed<cpp_type>::value) {
+            const std::string buffer = load_data(PBF_TYPE_NAME "/data-min");
+
+            protozero::pbf_reader item(buffer);
+
+            REQUIRE(item.next());
+            REQUIRE(item.GET_TYPE() == std::numeric_limits<cpp_type>::min());
+            REQUIRE(!item.next());
+        }
+    }
+#endif
+
+    SECTION("end_of_buffer") {
+        const std::string buffer = load_data(PBF_TYPE_NAME "/data-max");
+
+        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
+            protozero::pbf_reader item(buffer.data(), i);
+            REQUIRE(item.next());
+            REQUIRE_THROWS_AS(item.GET_TYPE(), protozero::end_of_buffer_exception);
+        }
+    }
+
+}
+
+TEST_CASE("write field: " PBF_TYPE_NAME) {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("zero") {
+        pw.ADD_TYPE(1, 0);
+        REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-zero"));
+    }
+
+    SECTION("positive") {
+        pw.ADD_TYPE(1, 1);
+        REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-pos"));
+    }
+
+    SECTION("max") {
+        pw.ADD_TYPE(1, std::numeric_limits<cpp_type>::max());
+        REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-max"));
+    }
+
+#if PBF_TYPE_IS_SIGNED
+    SECTION("negative") {
+        pw.ADD_TYPE(1, -1);
+        REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-neg"));
+    }
+
+    SECTION("min") {
+        if (std::is_signed<cpp_type>::value) {
+            pw.ADD_TYPE(1, std::numeric_limits<cpp_type>::min());
+            REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-min"));
+        }
+    }
+#endif
+
+}
+
diff --git a/test/include/test.hpp b/test/include/test.hpp
index 057a946..e88903a 100644
--- a/test/include/test.hpp
+++ b/test/include/test.hpp
@@ -18,3 +18,9 @@ struct assert_error : public std::runtime_error {
 
 extern std::string load_data(const std::string& filename);
 
+#define PROTOZERO_TEST_CONCAT2(x, y) x##y
+#define PROTOZERO_TEST_CONCAT(x, y) PROTOZERO_TEST_CONCAT2(x, y)
+
+#define PROTOZERO_TEST_STRING2(s) #s
+#define PROTOZERO_TEST_STRING(s) PROTOZERO_TEST_STRING2(s)
+
diff --git a/test/t/fixed32/test_cases.cpp b/test/t/alignment/test_cases.cpp
similarity index 51%
copy from test/t/fixed32/test_cases.cpp
copy to test/t/alignment/test_cases.cpp
index 5892412..2d6a212 100644
--- a/test/t/fixed32/test_cases.cpp
+++ b/test/t/alignment/test_cases.cpp
@@ -1,13 +1,13 @@
 
 #include <test.hpp>
 
-TEST_CASE("read fixed32 field") {
+// Run these tests twice, the second time we basically move the data
+// one byte down in the buffer. It doesn't matter how the data or buffer
+// is aligned before that, in at least one of these cases the int32 will
+// not be aligned properly. So we test that even in that case the int32
+// will be extracted properly.
 
-    // Run these tests twice, the second time we basically move the data
-    // one byte down in the buffer. It doesn't matter how the data or buffer
-    // is aligned before that, in at least one of these cases the int32 will
-    // not be aligned properly. So we test that even in that case the int32
-    // will be extracted properly.
+TEST_CASE("check alignment issues for fixed32 field") {
 
     for (std::string::size_type n = 0; n < 2; ++n) {
 
@@ -24,26 +24,26 @@ TEST_CASE("read fixed32 field") {
             REQUIRE(!item.next());
         }
 
-        SECTION("max-uint") {
-            abuffer.append(load_data("fixed32/data-max-uint"));
+        SECTION("positive") {
+            abuffer.append(load_data("fixed32/data-pos"));
             protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
 
             REQUIRE(item.next());
-            REQUIRE(item.get_fixed32() == std::numeric_limits<uint32_t>::max());
+            REQUIRE(item.get_fixed32() == 1UL);
             REQUIRE(!item.next());
         }
 
-        SECTION("min-uint") {
-            abuffer.append(load_data("fixed32/data-min-uint"));
+        SECTION("max") {
+            abuffer.append(load_data("fixed32/data-max"));
             protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
 
             REQUIRE(item.next());
-            REQUIRE(item.get_fixed32() == std::numeric_limits<uint32_t>::min());
+            REQUIRE(item.get_fixed32() == std::numeric_limits<uint32_t>::max());
             REQUIRE(!item.next());
         }
 
         SECTION("end_of_buffer") {
-            abuffer.append(load_data("fixed32/data-min-uint"));
+            abuffer.append(load_data("fixed32/data-pos"));
 
             for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
                 protozero::pbf_reader item(abuffer.data() + n, i);
@@ -52,14 +52,6 @@ TEST_CASE("read fixed32 field") {
             }
         }
 
-        SECTION("check assert on varint/string access") {
-            abuffer.append(load_data("fixed32/data-zero"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_string(), assert_error);
-        }
-
         SECTION("assert detecting tag==0") {
             abuffer.append(load_data("fixed32/data-zero"));
             protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
@@ -86,24 +78,51 @@ TEST_CASE("read fixed32 field") {
 
 }
 
-TEST_CASE("write fixed32 field") {
+TEST_CASE("check alignment issues for fixed64 field") {
 
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
+    for (std::string::size_type n = 0; n < 2; ++n) {
 
-    SECTION("zero") {
-        pw.add_fixed32(1, 0);
-        REQUIRE(buffer == load_data("fixed32/data-zero"));
-    }
+        std::string abuffer;
+        abuffer.reserve(1000);
+        abuffer.append(n, '\0');
 
-    SECTION("max-uint") {
-        pw.add_fixed32(1, std::numeric_limits<uint32_t>::max());
-        REQUIRE(buffer == load_data("fixed32/data-max-uint"));
-    }
+        SECTION("zero") {
+            abuffer.append(load_data("fixed64/data-zero"));
+            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
+
+            REQUIRE(item.next());
+            REQUIRE(item.get_fixed64() == 0ULL);
+            REQUIRE(!item.next());
+        }
+
+        SECTION("positive") {
+            abuffer.append(load_data("fixed64/data-pos"));
+            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
+
+            REQUIRE(item.next());
+            REQUIRE(item.get_fixed64() == 1ULL);
+            REQUIRE(!item.next());
+        }
+
+        SECTION("max") {
+            abuffer.append(load_data("fixed64/data-max"));
+            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
+
+            REQUIRE(item.next());
+            REQUIRE(item.get_fixed64() == std::numeric_limits<uint64_t>::max());
+            REQUIRE(!item.next());
+        }
+
+        SECTION("end_of_buffer") {
+            abuffer.append(load_data("fixed64/data-pos"));
+
+            for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
+                protozero::pbf_reader item(abuffer.data() + n, i);
+                REQUIRE(item.next());
+                REQUIRE_THROWS_AS(item.get_fixed64(), protozero::end_of_buffer_exception);
+            }
+        }
 
-    SECTION("min-uint") {
-        pw.add_fixed32(1, std::numeric_limits<uint32_t>::min());
-        REQUIRE(buffer == load_data("fixed32/data-min-uint"));
     }
 
 }
diff --git a/test/t/fixed32/data-max-uint.pbf b/test/t/fixed32/data-max.pbf
similarity index 100%
copy from test/t/fixed32/data-max-uint.pbf
copy to test/t/fixed32/data-max.pbf
diff --git a/test/t/fixed32/data-min-uint.pbf b/test/t/fixed32/data-min-uint.pbf
deleted file mode 100644
index 1cf2c40..0000000
Binary files a/test/t/fixed32/data-min-uint.pbf and /dev/null differ
diff --git a/test/t/fixed32/data-pos.pbf b/test/t/fixed32/data-pos.pbf
new file mode 100644
index 0000000..e63fa08
Binary files /dev/null and b/test/t/fixed32/data-pos.pbf differ
diff --git a/test/t/fixed32/test_cases.cpp b/test/t/fixed32/test_cases.cpp
index 5892412..23cbf6c 100644
--- a/test/t/fixed32/test_cases.cpp
+++ b/test/t/fixed32/test_cases.cpp
@@ -1,110 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read fixed32 field") {
+#define PBF_TYPE fixed32
+#define PBF_TYPE_IS_SIGNED 0
+using cpp_type = uint32_t;
 
-    // Run these tests twice, the second time we basically move the data
-    // one byte down in the buffer. It doesn't matter how the data or buffer
-    // is aligned before that, in at least one of these cases the int32 will
-    // not be aligned properly. So we test that even in that case the int32
-    // will be extracted properly.
-
-    for (std::string::size_type n = 0; n < 2; ++n) {
-
-        std::string abuffer;
-        abuffer.reserve(1000);
-        abuffer.append(n, '\0');
-
-        SECTION("zero") {
-            abuffer.append(load_data("fixed32/data-zero"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE(item.next());
-            REQUIRE(item.get_fixed32() == 0UL);
-            REQUIRE(!item.next());
-        }
-
-        SECTION("max-uint") {
-            abuffer.append(load_data("fixed32/data-max-uint"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE(item.next());
-            REQUIRE(item.get_fixed32() == std::numeric_limits<uint32_t>::max());
-            REQUIRE(!item.next());
-        }
-
-        SECTION("min-uint") {
-            abuffer.append(load_data("fixed32/data-min-uint"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE(item.next());
-            REQUIRE(item.get_fixed32() == std::numeric_limits<uint32_t>::min());
-            REQUIRE(!item.next());
-        }
-
-        SECTION("end_of_buffer") {
-            abuffer.append(load_data("fixed32/data-min-uint"));
-
-            for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
-                protozero::pbf_reader item(abuffer.data() + n, i);
-                REQUIRE(item.next());
-                REQUIRE_THROWS_AS(item.get_fixed32(), protozero::end_of_buffer_exception);
-            }
-        }
-
-        SECTION("check assert on varint/string access") {
-            abuffer.append(load_data("fixed32/data-zero"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_string(), assert_error);
-        }
-
-        SECTION("assert detecting tag==0") {
-            abuffer.append(load_data("fixed32/data-zero"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
-            REQUIRE(item.next());
-            REQUIRE(item.get_fixed32() == 0UL);
-            REQUIRE_THROWS(item.get_fixed32());
-            REQUIRE(!item.next());
-        }
-
-        SECTION("skip") {
-            abuffer.append(load_data("fixed32/data-zero"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE_THROWS_AS(item.skip(), assert_error);
-            REQUIRE(item.next());
-            item.skip();
-            REQUIRE_THROWS(item.skip());
-            REQUIRE(!item.next());
-        }
-
-    }
-
-}
-
-TEST_CASE("write fixed32 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_fixed32(1, 0);
-        REQUIRE(buffer == load_data("fixed32/data-zero"));
-    }
-
-    SECTION("max-uint") {
-        pw.add_fixed32(1, std::numeric_limits<uint32_t>::max());
-        REQUIRE(buffer == load_data("fixed32/data-max-uint"));
-    }
-
-    SECTION("min-uint") {
-        pw.add_fixed32(1, std::numeric_limits<uint32_t>::min());
-        REQUIRE(buffer == load_data("fixed32/data-min-uint"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/fixed32/testcase.cpp b/test/t/fixed32/testcase.cpp
index a4b58da..8394e75 100644
--- a/test/t/fixed32/testcase.cpp
+++ b/test/t/fixed32/testcase.cpp
@@ -8,10 +8,10 @@ int main(int c, char *argv[]) {
     msg.set_i(0);
     write_to_file(msg, "data-zero.pbf");
 
-    msg.set_i(std::numeric_limits<uint32_t>::max());
-    write_to_file(msg, "data-max-uint.pbf");
+    msg.set_i(1);
+    write_to_file(msg, "data-pos.pbf");
 
-    msg.set_i(std::numeric_limits<uint32_t>::min());
-    write_to_file(msg, "data-min-uint.pbf");
+    msg.set_i(std::numeric_limits<uint32_t>::max());
+    write_to_file(msg, "data-max.pbf");
 }
 
diff --git a/test/t/fixed32/writer_test_cases.cpp b/test/t/fixed32/writer_test_cases.cpp
index 55ff948..a76ca3c 100644
--- a/test/t/fixed32/writer_test_cases.cpp
+++ b/test/t/fixed32/writer_test_cases.cpp
@@ -18,7 +18,7 @@ TEST_CASE("write fixed32 field and check with libprotobuf") {
         REQUIRE(msg.i() == 0);
     }
 
-    SECTION("max-uint") {
+    SECTION("max") {
         pw.add_fixed32(1, std::numeric_limits<uint32_t>::max());
 
         msg.ParseFromString(buffer);
@@ -26,7 +26,7 @@ TEST_CASE("write fixed32 field and check with libprotobuf") {
         REQUIRE(msg.i() == std::numeric_limits<uint32_t>::max());
     }
 
-    SECTION("min-uint") {
+    SECTION("min") {
         pw.add_fixed32(1, std::numeric_limits<uint32_t>::min());
 
         msg.ParseFromString(buffer);
diff --git a/test/t/fixed64/data-max-uint.pbf b/test/t/fixed64/data-max.pbf
similarity index 100%
copy from test/t/fixed64/data-max-uint.pbf
copy to test/t/fixed64/data-max.pbf
diff --git a/test/t/fixed64/data-min-uint.pbf b/test/t/fixed64/data-min-uint.pbf
deleted file mode 100644
index 5f47e98..0000000
Binary files a/test/t/fixed64/data-min-uint.pbf and /dev/null differ
diff --git a/test/t/fixed64/data-pos.pbf b/test/t/fixed64/data-pos.pbf
new file mode 100644
index 0000000..83f1583
Binary files /dev/null and b/test/t/fixed64/data-pos.pbf differ
diff --git a/test/t/fixed64/test_cases.cpp b/test/t/fixed64/test_cases.cpp
index 8923507..9b613ba 100644
--- a/test/t/fixed64/test_cases.cpp
+++ b/test/t/fixed64/test_cases.cpp
@@ -1,80 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read fixed64 field") {
+#define PBF_TYPE fixed64
+#define PBF_TYPE_IS_SIGNED 0
+using cpp_type = uint64_t;
 
-    // Run these tests twice, the second time we basically move the data
-    // one byte down in the buffer. It doesn't matter how the data or buffer
-    // is aligned before that, in at least one of these cases the int64 will
-    // not be aligned properly. So we test that even in that case the int64
-    // will be extracted properly.
-
-    for (std::string::size_type n = 0; n < 2; ++n) {
-
-        std::string abuffer;
-        abuffer.reserve(1000);
-        abuffer.append(n, '\0');
-
-        SECTION("zero") {
-            abuffer.append(load_data("fixed64/data-zero"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE(item.next());
-            REQUIRE(item.get_fixed64() == 0ULL);
-            REQUIRE(!item.next());
-        }
-
-        SECTION("max-uint") {
-            abuffer.append(load_data("fixed64/data-max-uint"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE(item.next());
-            REQUIRE(item.get_fixed64() == std::numeric_limits<uint64_t>::max());
-            REQUIRE(!item.next());
-        }
-
-        SECTION("min-uint") {
-            abuffer.append(load_data("fixed64/data-min-uint"));
-            protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
-
-            REQUIRE(item.next());
-            REQUIRE(item.get_fixed64() == std::numeric_limits<uint64_t>::min());
-            REQUIRE(!item.next());
-        }
-
-        SECTION("end_of_buffer") {
-            abuffer.append(load_data("fixed64/data-min-uint"));
-
-            for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
-                protozero::pbf_reader item(abuffer.data() + n, i);
-                REQUIRE(item.next());
-                REQUIRE_THROWS_AS(item.get_fixed64(), protozero::end_of_buffer_exception);
-            }
-        }
-
-    }
-
-}
-
-TEST_CASE("write fixed64 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_fixed64(1, 0ULL);
-        REQUIRE(buffer == load_data("fixed64/data-zero"));
-    }
-
-    SECTION("max-uint") {
-        pw.add_fixed64(1, std::numeric_limits<uint64_t>::max());
-        REQUIRE(buffer == load_data("fixed64/data-max-uint"));
-    }
-
-    SECTION("min-uint") {
-        pw.add_fixed64(1, std::numeric_limits<uint64_t>::min());
-        REQUIRE(buffer == load_data("fixed64/data-min-uint"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/fixed64/testcase.cpp b/test/t/fixed64/testcase.cpp
index 09a4d79..eb1eea1 100644
--- a/test/t/fixed64/testcase.cpp
+++ b/test/t/fixed64/testcase.cpp
@@ -8,10 +8,10 @@ int main(int c, char *argv[]) {
     msg.set_i(0);
     write_to_file(msg, "data-zero.pbf");
 
-    msg.set_i(std::numeric_limits<uint64_t>::max());
-    write_to_file(msg, "data-max-uint.pbf");
+    msg.set_i(1);
+    write_to_file(msg, "data-pos.pbf");
 
-    msg.set_i(std::numeric_limits<uint64_t>::min());
-    write_to_file(msg, "data-min-uint.pbf");
+    msg.set_i(std::numeric_limits<uint64_t>::max());
+    write_to_file(msg, "data-max.pbf");
 }
 
diff --git a/test/t/float/test_cases.cpp b/test/t/float/test_cases.cpp
index 96fd84c..718db85 100644
--- a/test/t/float/test_cases.cpp
+++ b/test/t/float/test_cases.cpp
@@ -20,7 +20,7 @@ TEST_CASE("read float field") {
             protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
 
             REQUIRE(item.next());
-            REQUIRE(item.get_float() == Approx(0.0f));
+            REQUIRE(double(item.get_float()) == Approx(0.0));
             REQUIRE(!item.next());
         }
 
@@ -29,7 +29,7 @@ TEST_CASE("read float field") {
             protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
 
             REQUIRE(item.next());
-            REQUIRE(item.get_float() == Approx(5.34f));
+            REQUIRE(double(item.get_float()) == Approx(5.34));
             REQUIRE(!item.next());
         }
 
@@ -38,7 +38,7 @@ TEST_CASE("read float field") {
             protozero::pbf_reader item(abuffer.data() + n, abuffer.size() - n);
 
             REQUIRE(item.next());
-            REQUIRE(item.get_float() == Approx(-1.71f));
+            REQUIRE(double(item.get_float()) == Approx(-1.71));
             REQUIRE(!item.next());
         }
 
diff --git a/test/t/int32/test_cases.cpp b/test/t/int32/test_cases.cpp
index 04a83b3..bd9492f 100644
--- a/test/t/int32/test_cases.cpp
+++ b/test/t/int32/test_cases.cpp
@@ -1,119 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read int32 field") {
+#define PBF_TYPE int32
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int32_t;
 
-    SECTION("zero") {
-        const std::string buffer = load_data("int32/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int32() == 0L);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("positive") {
-        const std::string buffer = load_data("int32/data-pos");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int32() == 1L);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("negative") {
-        const std::string buffer = load_data("int32/data-neg");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int32() == -1L);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("max") {
-        const std::string buffer = load_data("int32/data-max");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int32() == std::numeric_limits<int32_t>::max());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("min64") {
-        const std::string buffer = load_data("int32/data-min");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(static_cast<int32_t>(item.get_int64()) == std::numeric_limits<int32_t>::min());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("min") {
-        const std::string buffer = load_data("int32/data-min");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int32() == std::numeric_limits<int32_t>::min());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("end_of_buffer") {
-        const std::string buffer = load_data("int32/data-min");
-
-        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
-            protozero::pbf_reader item(buffer.data(), i);
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_int32(), protozero::end_of_buffer_exception);
-        }
-    }
-
-    SECTION("check assert on string/fixed int access") {
-        const std::string buffer = load_data("int32/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
-        REQUIRE_THROWS_AS(item.get_string(), assert_error);
-    }
-
-}
-
-TEST_CASE("write int32 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_int32(1, 0L);
-        REQUIRE(buffer == load_data("int32/data-zero"));
-    }
-
-    SECTION("positive") {
-        pw.add_int32(1, 1L);
-        REQUIRE(buffer == load_data("int32/data-pos"));
-    }
-
-    SECTION("negative") {
-        pw.add_int32(1, -1L);
-        REQUIRE(buffer == load_data("int32/data-neg"));
-    }
-
-    SECTION("max") {
-        pw.add_int32(1, std::numeric_limits<int32_t>::max());
-        REQUIRE(buffer == load_data("int32/data-max"));
-    }
-
-    SECTION("min") {
-        pw.add_int32(1, std::numeric_limits<int32_t>::min());
-        REQUIRE(buffer == load_data("int32/data-min"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/int64/data-overflow.pbf b/test/t/int64/data-overflow.pbf
deleted file mode 100644
index bfb65e2..0000000
--- a/test/t/int64/data-overflow.pbf
+++ /dev/null
@@ -1 +0,0 @@
-����������
\ No newline at end of file
diff --git a/test/t/int64/test_cases.cpp b/test/t/int64/test_cases.cpp
index 0ee4060..8977103 100644
--- a/test/t/int64/test_cases.cpp
+++ b/test/t/int64/test_cases.cpp
@@ -1,108 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read int64 field") {
+#define PBF_TYPE int64
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int64_t;
 
-    SECTION("zero") {
-        const std::string buffer = load_data("int64/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int64() == 0LL);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("positive") {
-        const std::string buffer = load_data("int64/data-pos");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int64() == 1LL);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("negative") {
-        const std::string buffer = load_data("int64/data-neg");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int64() == -1LL);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("max") {
-        const std::string buffer = load_data("int64/data-max");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int64() == std::numeric_limits<int64_t>::max());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("min") {
-        const std::string buffer = load_data("int64/data-min");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_int64() == std::numeric_limits<int64_t>::min());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("end_of_buffer") {
-        const std::string buffer = load_data("int64/data-min");
-
-        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
-            protozero::pbf_reader item(buffer.data(), i);
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_int64(), protozero::end_of_buffer_exception);
-        }
-    }
-
-    SECTION("varint overflow") {
-        const std::string buffer = load_data("int64/data-overflow");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE_THROWS_AS(item.get_int64(), protozero::varint_too_long_exception);
-    }
-
-}
-
-TEST_CASE("write int64 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_int64(1, 0LL);
-        REQUIRE(buffer == load_data("int64/data-zero"));
-    }
-
-    SECTION("positive") {
-        pw.add_int64(1, 1LL);
-        REQUIRE(buffer == load_data("int64/data-pos"));
-    }
-
-    SECTION("negative") {
-        pw.add_int64(1, -1LL);
-        REQUIRE(buffer == load_data("int64/data-neg"));
-    }
-
-    SECTION("max") {
-        pw.add_int64(1, std::numeric_limits<int64_t>::max());
-        REQUIRE(buffer == load_data("int64/data-max"));
-    }
-
-    SECTION("min") {
-        pw.add_int64(1, std::numeric_limits<int64_t>::min());
-        REQUIRE(buffer == load_data("int64/data-min"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/int64/testcase.cpp b/test/t/int64/testcase.cpp
index c8add03..fd57a23 100644
--- a/test/t/int64/testcase.cpp
+++ b/test/t/int64/testcase.cpp
@@ -19,15 +19,5 @@ int main(int c, char *argv[]) {
 
     msg.set_i(std::numeric_limits<int64_t>::min());
     std::string data = write_to_file(msg, "data-min.pbf");
-
-    // this is a bit of binary trickery:
-    // we add a 0x80 byte one before the end of the data
-    // this will lead to a varint thats 11 bytes long, which is not allowed
-    data.insert(10, 1, 0x80);
-    {
-        std::ofstream d("data-overflow.pbf");
-        assert(d.is_open());
-        d << data;
-    }
 }
 
diff --git a/test/t/message/test_cases.cpp b/test/t/message/test_cases.cpp
index 2298806..0ebf28f 100644
--- a/test/t/message/test_cases.cpp
+++ b/test/t/message/test_cases.cpp
@@ -69,6 +69,52 @@ TEST_CASE("write message field") {
 
 }
 
+TEST_CASE("write message field into non-empty buffer") {
+
+    std::string buffer_test{"some data already in here"};
+    protozero::pbf_writer pbf_test(buffer_test);
+
+    SECTION("string") {
+        std::string buffer_submessage;
+        protozero::pbf_writer pbf_submessage(buffer_submessage);
+        pbf_submessage.add_string(1, "foobar");
+
+        pbf_test.add_message(1, buffer_submessage);
+    }
+
+    SECTION("string with subwriter") {
+        protozero::pbf_writer pbf_submessage(pbf_test, 1);
+        pbf_submessage.add_string(1, "foobar");
+    }
+
+    REQUIRE(buffer_test == std::string{"some data already in here"} + load_data("message/data-message"));
+
+}
+
+TEST_CASE("write message field reserving memory beforehand") {
+
+    std::string buffer_test;
+    protozero::pbf_writer pbf_test(buffer_test);
+    pbf_test.reserve(100);
+    REQUIRE(buffer_test.capacity() >= 100);
+
+    SECTION("string") {
+        std::string buffer_submessage;
+        protozero::pbf_writer pbf_submessage(buffer_submessage);
+        pbf_submessage.add_string(1, "foobar");
+
+        pbf_test.add_message(1, buffer_submessage);
+    }
+
+    SECTION("string with subwriter") {
+        protozero::pbf_writer pbf_submessage(pbf_test, 1);
+        pbf_submessage.add_string(1, "foobar");
+    }
+
+    REQUIRE(buffer_test == load_data("message/data-message"));
+
+}
+
 TEST_CASE("write optional message field") {
 
     std::string buffer_opt;
diff --git a/test/t/repeated_packed_bool/test_cases.cpp b/test/t/repeated_packed_bool/test_cases.cpp
index 574d538..98bf88b 100644
--- a/test/t/repeated_packed_bool/test_cases.cpp
+++ b/test/t/repeated_packed_bool/test_cases.cpp
@@ -83,3 +83,39 @@ TEST_CASE("write repeated packed bool field") {
 
 }
 
+TEST_CASE("write repeated packed bool field using packed_field_bool") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty - should do rollback") {
+        {
+            protozero::packed_field_bool field{pw, 1};
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_bool/data-empty"));
+    }
+
+    SECTION("one") {
+        {
+            protozero::packed_field_bool field{pw, 1};
+            field.add_element(true);
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_bool/data-one"));
+    }
+
+    SECTION("many") {
+        {
+            protozero::packed_field_bool field{pw, 1};
+            field.add_element(true);
+            field.add_element(true);
+            field.add_element(false);
+            field.add_element(true);
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_bool/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_fixed32/test_cases.cpp b/test/t/repeated_packed_fixed32/test_cases.cpp
index 4a02eec..e8fd3a9 100644
--- a/test/t/repeated_packed_fixed32/test_cases.cpp
+++ b/test/t/repeated_packed_fixed32/test_cases.cpp
@@ -92,6 +92,78 @@ TEST_CASE("write repeated packed fixed32 field") {
 
 }
 
+TEST_CASE("write repeated packed fixed32 field using packed_field_fixed32") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty - should do rollback") {
+        {
+            protozero::packed_field_fixed32 field{pw, 1};
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed32/data-empty"));
+    }
+
+    SECTION("one") {
+        {
+            protozero::packed_field_fixed32 field{pw, 1};
+            field.add_element(17UL);
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed32/data-one"));
+    }
+
+    SECTION("one with predefined size") {
+        {
+            protozero::packed_field_fixed32 field{pw, 1, 1};
+            field.add_element(17UL);
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed32/data-one"));
+    }
+
+    SECTION("many") {
+        {
+            protozero::packed_field_fixed32 field{pw, 1};
+            field.add_element(17UL);
+            field.add_element(0UL);
+            field.add_element(1UL);
+            field.add_element(std::numeric_limits<uint32_t>::max());
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed32/data-many"));
+    }
+
+    SECTION("many with predefined size") {
+        {
+            protozero::packed_field_fixed32 field{pw, 1, 4};
+            field.add_element(17UL);
+            field.add_element(0UL);
+            field.add_element(1UL);
+            field.add_element(std::numeric_limits<uint32_t>::max());
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_fixed32/data-many"));
+    }
+
+    SECTION("failure when not closing properly after zero elements") {
+        protozero::packed_field_fixed32 field{pw, 1};
+        REQUIRE_THROWS_AS({
+            pw.add_fixed32(2, 1234); // dummy values
+        }, assert_error);
+    }
+
+    SECTION("failure when not closing properly after one element") {
+        protozero::packed_field_fixed32 field{pw, 1};
+        field.add_element(17UL);
+        REQUIRE_THROWS_AS({
+            pw.add_fixed32(2, 1234); // dummy values
+        }, assert_error);
+    }
+
+}
+
 TEST_CASE("write from different types of iterators") {
 
     std::string buffer;
diff --git a/test/t/repeated_packed_int64/test_cases.cpp b/test/t/repeated_packed_int64/test_cases.cpp
index 9afda2a..2013a22 100644
--- a/test/t/repeated_packed_int64/test_cases.cpp
+++ b/test/t/repeated_packed_int64/test_cases.cpp
@@ -83,3 +83,41 @@ TEST_CASE("write repeated packed int64 field") {
 
 }
 
+TEST_CASE("write repeated packed int64 field using packed_field_int64") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty - should do rollback") {
+        {
+            protozero::packed_field_int64 field{pw, 1};
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_int64/data-empty"));
+    }
+
+    SECTION("one") {
+        {
+            protozero::packed_field_int64 field{pw, 1};
+            field.add_element(17LL);
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_int64/data-one"));
+    }
+
+    SECTION("many") {
+        {
+            protozero::packed_field_int64 field{pw, 1};
+            field.add_element(17LL);
+            field.add_element( 0LL);
+            field.add_element( 1LL);
+            field.add_element(-1LL);
+            field.add_element(std::numeric_limits<int64_t>::max());
+            field.add_element(std::numeric_limits<int64_t>::min());
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_int64/data-many"));
+    }
+
+}
+
diff --git a/test/t/repeated_packed_sint64/test_cases.cpp b/test/t/repeated_packed_sint64/test_cases.cpp
index 7fa24a0..0e06ec2 100644
--- a/test/t/repeated_packed_sint64/test_cases.cpp
+++ b/test/t/repeated_packed_sint64/test_cases.cpp
@@ -83,3 +83,41 @@ TEST_CASE("write repeated packed sint64 field") {
 
 }
 
+TEST_CASE("write repeated packed sint64 field using packed_field_sint64") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    SECTION("empty - should do rollback") {
+        {
+            protozero::packed_field_sint64 field{pw, 1};
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_sint64/data-empty"));
+    }
+
+    SECTION("one") {
+        {
+            protozero::packed_field_sint64 field{pw, 1};
+            field.add_element(17L);
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_sint64/data-one"));
+    }
+
+    SECTION("many") {
+        {
+            protozero::packed_field_sint64 field{pw, 1};
+            field.add_element(17L);
+            field.add_element( 0L);
+            field.add_element( 1L);
+            field.add_element(-1L);
+            field.add_element(std::numeric_limits<int64_t>::max());
+            field.add_element(std::numeric_limits<int64_t>::min());
+        }
+
+        REQUIRE(buffer == load_data("repeated_packed_sint64/data-many"));
+    }
+
+}
+
diff --git a/test/t/rollback/test_cases.cpp b/test/t/rollback/test_cases.cpp
new file mode 100644
index 0000000..1223006
--- /dev/null
+++ b/test/t/rollback/test_cases.cpp
@@ -0,0 +1,214 @@
+
+#include <test.hpp>
+
+TEST_CASE("rollback when using packed_field functions") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    pw.add_fixed64(2, 111);
+    pw.add_string(3, "foo");
+
+    SECTION("empty - should do rollback") {
+        {
+            protozero::packed_field_sint64 field{pw, 1};
+        }
+
+        pw.add_int32(4, 123);
+
+        protozero::pbf_reader msg(buffer);
+
+        msg.next();
+        REQUIRE(msg.tag() == 2);
+        REQUIRE(msg.get_fixed64() == 111);
+
+        msg.next();
+        REQUIRE(msg.tag() == 3);
+        REQUIRE(msg.get_string() == "foo");
+
+        msg.next();
+        REQUIRE(msg.tag() == 4);
+        REQUIRE(msg.get_int32() == 123);
+    }
+
+    SECTION("one") {
+        {
+            protozero::packed_field_sint64 field{pw, 1};
+            field.add_element(17L);
+        }
+
+        pw.add_int32(4, 123);
+
+        protozero::pbf_reader msg(buffer);
+
+        msg.next();
+        REQUIRE(msg.tag() == 2);
+        REQUIRE(msg.get_fixed64() == 111);
+
+        msg.next();
+        REQUIRE(msg.tag() == 3);
+        REQUIRE(msg.get_string() == "foo");
+
+        msg.next();
+        REQUIRE(msg.tag() == 1);
+        auto it_pair = msg.get_packed_sint64();
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17L);
+        REQUIRE(it == it_pair.second);
+
+        msg.next();
+        REQUIRE(msg.tag() == 4);
+        REQUIRE(msg.get_int32() == 123);
+    }
+
+    SECTION("many") {
+        {
+            protozero::packed_field_sint64 field{pw, 1};
+            field.add_element(17L);
+            field.add_element( 0L);
+            field.add_element( 1L);
+            field.add_element(-1L);
+            field.add_element(std::numeric_limits<int64_t>::max());
+            field.add_element(std::numeric_limits<int64_t>::min());
+        }
+
+        pw.add_int32(4, 123);
+
+        protozero::pbf_reader msg(buffer);
+
+        msg.next();
+        REQUIRE(msg.tag() == 2);
+        REQUIRE(msg.get_fixed64() == 111);
+
+        msg.next();
+        REQUIRE(msg.tag() == 3);
+        REQUIRE(msg.get_string() == "foo");
+
+        msg.next();
+        REQUIRE(msg.tag() == 1);
+        auto it_pair = msg.get_packed_sint64();
+        auto it = it_pair.first;
+        REQUIRE(*it++ == 17L);
+        REQUIRE(*it++ ==  0L);
+        REQUIRE(*it++ ==  1L);
+        REQUIRE(*it++ == -1L);
+        REQUIRE(*it++ == std::numeric_limits<int64_t>::max());
+        REQUIRE(*it++ == std::numeric_limits<int64_t>::min());
+        REQUIRE(it == it_pair.second);
+
+        msg.next();
+        REQUIRE(msg.tag() == 4);
+        REQUIRE(msg.get_int32() == 123);
+    }
+
+    SECTION("manual rollback") {
+        {
+            protozero::packed_field_sint64 field{pw, 1};
+            field.add_element(17L);
+            field.add_element( 0L);
+            field.add_element( 1L);
+            field.rollback();
+        }
+
+        pw.add_int32(4, 123);
+
+        protozero::pbf_reader msg(buffer);
+
+        msg.next();
+        REQUIRE(msg.tag() == 2);
+        REQUIRE(msg.get_fixed64() == 111);
+
+        msg.next();
+        REQUIRE(msg.tag() == 3);
+        REQUIRE(msg.get_string() == "foo");
+
+        msg.next();
+        REQUIRE(msg.tag() == 4);
+        REQUIRE(msg.get_int32() == 123);
+    }
+
+    SECTION("manual rollback") {
+        {
+            protozero::packed_field_sint64 field{pw, 1};
+            field.add_element(1L);
+            field.rollback();
+            REQUIRE_THROWS_AS({
+                field.add_element(1L);
+            }, assert_error);
+        }
+    }
+}
+
+TEST_CASE("rollback when using submessages") {
+
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    pw.add_fixed64(2, 111);
+    pw.add_string(3, "foo");
+
+    {
+        protozero::pbf_writer pws(pw, 1);
+        pws.add_string(1, "foobar");
+        pws.rollback();
+    }
+
+    pw.add_int32(4, 123);
+
+    protozero::pbf_reader msg(buffer);
+
+    msg.next();
+    REQUIRE(msg.tag() == 2);
+    REQUIRE(msg.get_fixed64() == 111);
+
+    msg.next();
+    REQUIRE(msg.tag() == 3);
+    REQUIRE(msg.get_string() == "foo");
+
+    msg.next();
+    REQUIRE(msg.tag() == 4);
+    REQUIRE(msg.get_int32() == 123);
+
+}
+
+TEST_CASE("rollback on parent message is never allowed") {
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+    REQUIRE_THROWS_AS({
+        pw.rollback();
+    }, assert_error);
+}
+
+TEST_CASE("rollback on parent message is not allowed even if there is a submessage") {
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    pw.add_fixed64(2, 111);
+    pw.add_string(3, "foo");
+
+    {
+        protozero::pbf_writer pws(pw, 1);
+        pws.add_string(1, "foobar");
+        REQUIRE_THROWS_AS({
+            pw.rollback();
+        }, assert_error);
+    }
+}
+
+TEST_CASE("rollback on message is not allowed if there is a nested submessage") {
+    std::string buffer;
+    protozero::pbf_writer pw(buffer);
+
+    pw.add_fixed64(2, 111);
+    pw.add_string(3, "foo");
+
+    {
+        protozero::pbf_writer pws(pw, 1);
+        pws.add_string(1, "foobar");
+        protozero::pbf_writer pws2(pws, 1);
+        REQUIRE_THROWS_AS({
+            pws.rollback();
+        }, assert_error);
+    }
+}
+
diff --git a/test/t/sfixed32/data-max-int.pbf b/test/t/sfixed32/data-max.pbf
similarity index 100%
rename from test/t/sfixed32/data-max-int.pbf
rename to test/t/sfixed32/data-max.pbf
diff --git a/test/t/sfixed32/data-min-int.pbf b/test/t/sfixed32/data-min.pbf
similarity index 100%
rename from test/t/sfixed32/data-min-int.pbf
rename to test/t/sfixed32/data-min.pbf
diff --git a/test/t/fixed32/data-max-uint.pbf b/test/t/sfixed32/data-neg.pbf
similarity index 100%
rename from test/t/fixed32/data-max-uint.pbf
rename to test/t/sfixed32/data-neg.pbf
diff --git a/test/t/sfixed32/data-pos.pbf b/test/t/sfixed32/data-pos.pbf
new file mode 100644
index 0000000..e63fa08
Binary files /dev/null and b/test/t/sfixed32/data-pos.pbf differ
diff --git a/test/t/sfixed32/test_cases.cpp b/test/t/sfixed32/test_cases.cpp
index e2471f6..8dadcaa 100644
--- a/test/t/sfixed32/test_cases.cpp
+++ b/test/t/sfixed32/test_cases.cpp
@@ -1,69 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read sfixed32 field") {
+#define PBF_TYPE sfixed32
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int32_t;
 
-    SECTION("zero") {
-        const std::string buffer = load_data("sfixed32/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sfixed32() == 0L);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("max-int") {
-        const std::string buffer = load_data("sfixed32/data-max-int");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sfixed32() == std::numeric_limits<int32_t>::max());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("min-int") {
-        const std::string buffer = load_data("sfixed32/data-min-int");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sfixed32() == std::numeric_limits<int32_t>::min());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("end_of_buffer") {
-        const std::string buffer = load_data("sfixed32/data-min-int");
-
-        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
-            protozero::pbf_reader item(buffer.data(), i);
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_sfixed32(), protozero::end_of_buffer_exception);
-        }
-    }
-
-}
-
-TEST_CASE("write sfixed32 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_sfixed32(1, 0);
-        REQUIRE(buffer == load_data("sfixed32/data-zero"));
-    }
-
-    SECTION("max-uint") {
-        pw.add_sfixed32(1, std::numeric_limits<int32_t>::max());
-        REQUIRE(buffer == load_data("sfixed32/data-max-int"));
-    }
-
-    SECTION("min-uint") {
-        pw.add_sfixed32(1, std::numeric_limits<int32_t>::min());
-        REQUIRE(buffer == load_data("sfixed32/data-min-int"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/sfixed32/testcase.cpp b/test/t/sfixed32/testcase.cpp
index a50bd40..03be3e0 100644
--- a/test/t/sfixed32/testcase.cpp
+++ b/test/t/sfixed32/testcase.cpp
@@ -8,10 +8,16 @@ int main(int c, char *argv[]) {
     msg.set_i(0);
     write_to_file(msg, "data-zero.pbf");
 
+    msg.set_i(1);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_i(-1);
+    write_to_file(msg, "data-neg.pbf");
+
     msg.set_i(std::numeric_limits<int32_t>::max());
-    write_to_file(msg, "data-max-int.pbf");
+    write_to_file(msg, "data-max.pbf");
 
     msg.set_i(std::numeric_limits<int32_t>::min());
-    write_to_file(msg, "data-min-int.pbf");
+    write_to_file(msg, "data-min.pbf");
 }
 
diff --git a/test/t/sfixed64/data-max-int.pbf b/test/t/sfixed64/data-max.pbf
similarity index 100%
rename from test/t/sfixed64/data-max-int.pbf
rename to test/t/sfixed64/data-max.pbf
diff --git a/test/t/sfixed64/data-min-int.pbf b/test/t/sfixed64/data-min.pbf
similarity index 100%
rename from test/t/sfixed64/data-min-int.pbf
rename to test/t/sfixed64/data-min.pbf
diff --git a/test/t/fixed64/data-max-uint.pbf b/test/t/sfixed64/data-neg.pbf
similarity index 100%
rename from test/t/fixed64/data-max-uint.pbf
rename to test/t/sfixed64/data-neg.pbf
diff --git a/test/t/sfixed64/data-pos.pbf b/test/t/sfixed64/data-pos.pbf
new file mode 100644
index 0000000..83f1583
Binary files /dev/null and b/test/t/sfixed64/data-pos.pbf differ
diff --git a/test/t/sfixed64/test_cases.cpp b/test/t/sfixed64/test_cases.cpp
index cecf273..6e63e5b 100644
--- a/test/t/sfixed64/test_cases.cpp
+++ b/test/t/sfixed64/test_cases.cpp
@@ -1,69 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read sfixed64 field") {
+#define PBF_TYPE sfixed64
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int64_t;
 
-    SECTION("zero") {
-        const std::string buffer = load_data("sfixed64/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sfixed64() == 0LL);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("max-int") {
-        const std::string buffer = load_data("sfixed64/data-max-int");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sfixed64() == std::numeric_limits<int64_t>::max());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("min-int") {
-        const std::string buffer = load_data("sfixed64/data-min-int");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sfixed64() == std::numeric_limits<int64_t>::min());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("end_of_buffer") {
-        const std::string buffer = load_data("sfixed64/data-min-int");
-
-        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
-            protozero::pbf_reader item(buffer.data(), i);
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_sfixed64(), protozero::end_of_buffer_exception);
-        }
-    }
-
-}
-
-TEST_CASE("write sfixed64 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_sfixed64(1, 0);
-        REQUIRE(buffer == load_data("sfixed64/data-zero"));
-    }
-
-    SECTION("max-uint") {
-        pw.add_sfixed64(1, std::numeric_limits<int64_t>::max());
-        REQUIRE(buffer == load_data("sfixed64/data-max-int"));
-    }
-
-    SECTION("min-uint") {
-        pw.add_sfixed64(1, std::numeric_limits<int64_t>::min());
-        REQUIRE(buffer == load_data("sfixed64/data-min-int"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/sfixed64/testcase.cpp b/test/t/sfixed64/testcase.cpp
index 3ce45be..b2b50d8 100644
--- a/test/t/sfixed64/testcase.cpp
+++ b/test/t/sfixed64/testcase.cpp
@@ -8,10 +8,16 @@ int main(int c, char *argv[]) {
     msg.set_i(0);
     write_to_file(msg, "data-zero.pbf");
 
+    msg.set_i(1);
+    write_to_file(msg, "data-pos.pbf");
+
+    msg.set_i(-1);
+    write_to_file(msg, "data-neg.pbf");
+
     msg.set_i(std::numeric_limits<int64_t>::max());
-    write_to_file(msg, "data-max-int.pbf");
+    write_to_file(msg, "data-max.pbf");
 
     msg.set_i(std::numeric_limits<int64_t>::min());
-    write_to_file(msg, "data-min-int.pbf");
+    write_to_file(msg, "data-min.pbf");
 }
 
diff --git a/test/t/sint32/test_cases.cpp b/test/t/sint32/test_cases.cpp
index 68bd9ef..4a0d4fa 100644
--- a/test/t/sint32/test_cases.cpp
+++ b/test/t/sint32/test_cases.cpp
@@ -1,99 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read sint32 field") {
+#define PBF_TYPE sint32
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int32_t;
 
-    SECTION("zero") {
-        const std::string buffer = load_data("sint32/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint32() == 0L);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("positive") {
-        const std::string buffer = load_data("sint32/data-pos");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint32() == 1L);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("negative") {
-        const std::string buffer = load_data("sint32/data-neg");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint32() == -1L);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("max") {
-        const std::string buffer = load_data("sint32/data-max");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint32() == std::numeric_limits<int32_t>::max());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("min") {
-        const std::string buffer = load_data("sint32/data-min");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint32() == std::numeric_limits<int32_t>::min());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("end_of_buffer") {
-        const std::string buffer = load_data("sint32/data-min");
-
-        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
-            protozero::pbf_reader item(buffer.data(), i);
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_sint32(), protozero::end_of_buffer_exception);
-        }
-    }
-
-}
-
-TEST_CASE("write sint32 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_sint32(1, 0L);
-        REQUIRE(buffer == load_data("sint32/data-zero"));
-    }
-
-    SECTION("positive") {
-        pw.add_sint32(1, 1L);
-        REQUIRE(buffer == load_data("sint32/data-pos"));
-    }
-
-    SECTION("negative") {
-        pw.add_sint32(1, -1L);
-        REQUIRE(buffer == load_data("sint32/data-neg"));
-    }
-
-    SECTION("max") {
-        pw.add_sint32(1, std::numeric_limits<int32_t>::max());
-        REQUIRE(buffer == load_data("sint32/data-max"));
-    }
-
-    SECTION("min") {
-        pw.add_sint32(1, std::numeric_limits<int32_t>::min());
-        REQUIRE(buffer == load_data("sint32/data-min"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/sint64/test_cases.cpp b/test/t/sint64/test_cases.cpp
index 2dec5b1..850172f 100644
--- a/test/t/sint64/test_cases.cpp
+++ b/test/t/sint64/test_cases.cpp
@@ -1,99 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read sint64 field") {
+#define PBF_TYPE sint64
+#define PBF_TYPE_IS_SIGNED 1
+using cpp_type = int64_t;
 
-    SECTION("zero") {
-        const std::string buffer = load_data("sint64/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint64() == 0LL);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("positive") {
-        const std::string buffer = load_data("sint64/data-pos");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint64() == 1LL);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("negative") {
-        const std::string buffer = load_data("sint64/data-neg");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint64() == -1LL);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("max") {
-        const std::string buffer = load_data("sint64/data-max");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint64() == std::numeric_limits<int64_t>::max());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("min") {
-        const std::string buffer = load_data("sint64/data-min");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_sint64() == std::numeric_limits<int64_t>::min());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("end_of_buffer") {
-        const std::string buffer = load_data("sint64/data-min");
-
-        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
-            protozero::pbf_reader item(buffer.data(), i);
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_sint64(), protozero::end_of_buffer_exception);
-        }
-    }
-
-}
-
-TEST_CASE("write sint64 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_sint64(1, 0L);
-        REQUIRE(buffer == load_data("sint64/data-zero"));
-    }
-
-    SECTION("positive") {
-        pw.add_sint64(1, 1L);
-        REQUIRE(buffer == load_data("sint64/data-pos"));
-    }
-
-    SECTION("negative") {
-        pw.add_sint64(1, -1L);
-        REQUIRE(buffer == load_data("sint64/data-neg"));
-    }
-
-    SECTION("max") {
-        pw.add_sint64(1, std::numeric_limits<int64_t>::max());
-        REQUIRE(buffer == load_data("sint64/data-max"));
-    }
-
-    SECTION("min") {
-        pw.add_sint64(1, std::numeric_limits<int64_t>::min());
-        REQUIRE(buffer == load_data("sint64/data-min"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/skip/test_cases.cpp b/test/t/skip/test_cases.cpp
index fb4ee34..f426565 100644
--- a/test/t/skip/test_cases.cpp
+++ b/test/t/skip/test_cases.cpp
@@ -26,11 +26,11 @@ TEST_CASE("skip") {
             "double/data-zero",
             "enum/data-black",
             "enum/data-blue",
-            "fixed32/data-max-uint",
-            "fixed32/data-min-uint",
+            "fixed32/data-max",
+            "fixed32/data-pos",
             "fixed32/data-zero",
-            "fixed64/data-max-uint",
-            "fixed64/data-min-uint",
+            "fixed64/data-max",
+            "fixed64/data-pos",
             "fixed64/data-zero",
             "float/data-neg",
             "float/data-pos",
@@ -67,11 +67,11 @@ TEST_CASE("skip") {
             "repeated_packed_uint32/data-one",
             "repeated_packed_uint64/data-many",
             "repeated_packed_uint64/data-one",
-            "sfixed32/data-max-int",
-            "sfixed32/data-min-int",
+            "sfixed32/data-max",
+            "sfixed32/data-min",
             "sfixed32/data-zero",
-            "sfixed64/data-max-int",
-            "sfixed64/data-min-int",
+            "sfixed64/data-max",
+            "sfixed64/data-min",
             "sfixed64/data-zero",
             "sint32/data-max",
             "sint32/data-min",
diff --git a/test/t/string/test_cases.cpp b/test/t/string/test_cases.cpp
index 4609581..c81d293 100644
--- a/test/t/string/test_cases.cpp
+++ b/test/t/string/test_cases.cpp
@@ -43,15 +43,6 @@ TEST_CASE("read string field") {
         }
     }
 
-    SECTION("check assert on fixed int access") {
-        const std::string buffer = load_data("string/data-string");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
-    }
-
 }
 
 TEST_CASE("write string field") {
diff --git a/test/t/uint32/test_cases.cpp b/test/t/uint32/test_cases.cpp
index dcd525f..0036a47 100644
--- a/test/t/uint32/test_cases.cpp
+++ b/test/t/uint32/test_cases.cpp
@@ -1,69 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read uint32 field") {
+#define PBF_TYPE uint32
+#define PBF_TYPE_IS_SIGNED 0
+using cpp_type = uint32_t;
 
-    SECTION("zero") {
-        const std::string buffer = load_data("uint32/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_uint32() == 0ul);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("positive") {
-        const std::string buffer = load_data("uint32/data-pos");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_uint32() == 1ul);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("max") {
-        const std::string buffer = load_data("uint32/data-max");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_uint32() == std::numeric_limits<uint32_t>::max());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("end_of_buffer") {
-        const std::string buffer = load_data("uint32/data-max");
-
-        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
-            protozero::pbf_reader item(buffer.data(), i);
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_uint32(), protozero::end_of_buffer_exception);
-        }
-    }
-
-}
-
-TEST_CASE("write uint32 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_uint32(1, 0UL);
-        REQUIRE(buffer == load_data("uint32/data-zero"));
-    }
-
-    SECTION("positive") {
-        pw.add_uint32(1, 1UL);
-        REQUIRE(buffer == load_data("uint32/data-pos"));
-    }
-
-    SECTION("max") {
-        pw.add_uint32(1, std::numeric_limits<uint32_t>::max());
-        REQUIRE(buffer == load_data("uint32/data-max"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/uint64/test_cases.cpp b/test/t/uint64/test_cases.cpp
index 95a9168..b9ee585 100644
--- a/test/t/uint64/test_cases.cpp
+++ b/test/t/uint64/test_cases.cpp
@@ -1,69 +1,9 @@
 
 #include <test.hpp>
 
-TEST_CASE("read uint64 field") {
+#define PBF_TYPE uint64
+#define PBF_TYPE_IS_SIGNED 0
+using cpp_type = uint64_t;
 
-    SECTION("zero") {
-        const std::string buffer = load_data("uint64/data-zero");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_uint64() == 0ul);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("positive") {
-        const std::string buffer = load_data("uint64/data-pos");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_uint64() == 1ul);
-        REQUIRE(!item.next());
-    }
-
-    SECTION("max") {
-        const std::string buffer = load_data("uint64/data-max");
-
-        protozero::pbf_reader item(buffer);
-
-        REQUIRE(item.next());
-        REQUIRE(item.get_uint64() == std::numeric_limits<uint64_t>::max());
-        REQUIRE(!item.next());
-    }
-
-    SECTION("end_of_buffer") {
-        const std::string buffer = load_data("uint64/data-max");
-
-        for (std::string::size_type i = 1; i < buffer.size(); ++i) {
-            protozero::pbf_reader item(buffer.data(), i);
-            REQUIRE(item.next());
-            REQUIRE_THROWS_AS(item.get_uint64(), protozero::end_of_buffer_exception);
-        }
-    }
-
-}
-
-TEST_CASE("write uint64 field") {
-
-    std::string buffer;
-    protozero::pbf_writer pw(buffer);
-
-    SECTION("zero") {
-        pw.add_uint64(1, 0UL);
-        REQUIRE(buffer == load_data("uint64/data-zero"));
-    }
-
-    SECTION("positive") {
-        pw.add_uint64(1, 1UL);
-        REQUIRE(buffer == load_data("uint64/data-pos"));
-    }
-
-    SECTION("max") {
-        pw.add_uint64(1, std::numeric_limits<uint64_t>::max());
-        REQUIRE(buffer == load_data("uint64/data-max"));
-    }
-
-}
+#include <scalar_access.hpp>
 
diff --git a/test/t/wrong_type_access/test_cases.cpp b/test/t/wrong_type_access/test_cases.cpp
new file mode 100644
index 0000000..4ebefe2
--- /dev/null
+++ b/test/t/wrong_type_access/test_cases.cpp
@@ -0,0 +1,55 @@
+
+#include <test.hpp>
+
+// protobuf wire type 0
+TEST_CASE("check assert on non-varint access to varint") {
+    const std::string buffer = load_data("int32/data-zero");
+
+    protozero::pbf_reader item(buffer);
+
+    REQUIRE(item.next());
+    REQUIRE(item.get_int32() == 0);
+    REQUIRE_THROWS_AS(item.get_fixed64(), assert_error);
+    REQUIRE_THROWS_AS(item.get_string(), assert_error);
+    REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
+}
+
+// protobuf wire type 1
+TEST_CASE("check assert on non-fixed access to fixed64") {
+    const std::string buffer = load_data("fixed64/data-zero");
+
+    protozero::pbf_reader item(buffer);
+
+    REQUIRE(item.next());
+    REQUIRE_THROWS_AS(item.get_int32(), assert_error);
+    REQUIRE(item.get_fixed64() == 0);
+    REQUIRE_THROWS_AS(item.get_string(), assert_error);
+    REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
+}
+
+// protobuf wire type 2
+TEST_CASE("check assert on non-string access to string") {
+    const std::string buffer = load_data("string/data-string");
+
+    protozero::pbf_reader item(buffer);
+
+    REQUIRE(item.next());
+    REQUIRE_THROWS_AS(item.get_int32(), assert_error);
+    REQUIRE_THROWS_AS(item.get_fixed64(), assert_error);
+    REQUIRE(item.get_string() == "foobar");
+    REQUIRE_THROWS_AS(item.get_fixed32(), assert_error);
+}
+
+// protobuf wire type 5
+TEST_CASE("check assert on non-fixed access to fixed32") {
+    const std::string buffer = load_data("fixed32/data-zero");
+
+    protozero::pbf_reader item(buffer);
+
+    REQUIRE(item.next());
+    REQUIRE_THROWS_AS(item.get_int32(), assert_error);
+    REQUIRE_THROWS_AS(item.get_fixed64(), assert_error);
+    REQUIRE_THROWS_AS(item.get_string(), assert_error);
+    REQUIRE(item.get_fixed32() == 0);
+}
+
diff --git a/tutorial.md b/tutorial.md
index d5273ee..c139739 100644
--- a/tutorial.md
+++ b/tutorial.md
@@ -27,7 +27,7 @@ Keep the `protozero` directory and include the files in the form
 
     #include <protozero/FILENAME.hpp>
 
-You always need `byteswap.hpp`, `config.hpp`, `pbf_types.hpp`, `varint.hpp`,
+You always need `byteswap.hpp`, `config.hpp`, `types.hpp`, `varint.hpp`,
 and `exception.hpp`. For reading you need `pbf_reader.hpp` and probably
 `pbf_message.hpp`, for writing you need `pbf_writer.hpp` and probably
 `pbf_builder.hpp`. You only need `version.hpp` if you want access to the macros
@@ -387,7 +387,7 @@ that looks somewhat like this:
     #include <protozero/pbf_writer.hpp>
 
     std::string data;
-    osmium::util::pbf_writer pbf_example(data);
+    protozero::pbf_writer pbf_example(data);
 
     pbf_example.add_uint32(1, 27);       // uint32_t x
     pbf_example.add_fixed64(17, 1);      // fixed64 r
@@ -395,9 +395,11 @@ that looks somewhat like this:
     pbf_example.add_fixed64(17, 3);
     pbf_example.add_string(2, "foobar"); // string s
 
-First you create an empty string which will be used as buffer to assemble the
+First you need a string which will be used as buffer to assemble the
 protobuf-formatted message. The `pbf_writer` object contains a reference to
 this string buffer and through it you add data to that buffer piece by piece.
+The buffer doesn't have to be empty, the `pbf_writer` will simply append its
+data to whatever is there already.
 
 
 ### Handling Scalar Fields
@@ -422,11 +424,64 @@ the `.proto` file are not available.
 Repeated packed fields can easily be set from a pair of iterators:
 
     std::string data;
-    osmium::util::pbf_writer pw(data);
+    protozero::pbf_writer pw(data);
 
     std::vector<int> v = { 1, 4, 9, 16, 25, 36 };
     pw.add_packed_int32(1, std::begin(v), std::end(v));
 
+If you don't have an iterator you can use the alternative form:
+
+    std::string data;
+    protozero::pbf_writer pw(data);
+    {
+        protozero::packed_field_int32 field{pw, 1};
+        field.add_element(1);
+        field.add_element(10);
+        field.add_element(100);
+    }
+
+Of course you can add as many elements as you want. If you add no elements
+at all, this code will still work, protozero detects this special case and
+pretends you never even initialized this field.
+
+The nested scope is important in this case, because the destructor of the
+`field` object will make sure the length stored inside the field is set to
+the right value. You must close that scope before adding other fields to the
+`pw` pbf writer.
+
+If you know how many elements you will add to the field and your field contains
+fixed length elements, you can tell Protozero and it can optimize this case:
+
+    std::string data;
+    protozero::pbf_writer pw(data);
+    {
+        protozero::packed_field_fixed32 field{pw, 1, 2}; // exactly two elements
+        field.add_element(42);
+        field.add_element(13);
+    }
+
+In this case you have to supply exactly as many elements as you promised,
+otherwise you will get a broken protobuf message.
+
+This works for `packed_field_fixed32`, `packed_field_sfixed32`,
+`packed_field_fixed64`, `packed_field_sfixed64`, `packed_field_float`, and
+`packed_field_double`.
+
+You can abandon writing of the packed field if this becomes necessary by
+calling `rollback()`:
+
+    std::string data;
+    protozero::pbf_writer pw(data);
+    {
+        protozero::packed_field_int32 field{pw, 1};
+        field.add_element(42);
+        // some error occurs, you don't want to have this field at all
+        field.rollback();
+    }
+
+The result is the same as if the lines inside the nested brackets had never
+been called. Do not try to call `add_element()` after a rollback.
+
 
 ### Handling Sub-Messages
 
@@ -434,7 +489,7 @@ Nested sub-messages can be handled by first creating the submessage and then
 adding to the parent message:
 
     std::string buffer_sub;
-    osmium::util::pbf_writer pbf_sub(buffer_sub);
+    protozero::pbf_writer pbf_sub(buffer_sub);
 
     // add fields to sub-message
     pbf_sub.add_...(...);
@@ -443,15 +498,15 @@ adding to the parent message:
     // sub-message is finished here
 
     std::string buffer_parent;
-    osmium::util::pbf_writer pbf_parent(buffer_parent);
-    pbf_parent.add_message(buffer_sub);
+    protozero::pbf_writer pbf_parent(buffer_parent);
+    pbf_parent.add_message(1, buffer_sub);
 
 This is easy to do but it has the drawback of needing a separate `std::string`
-buffer. If this concerns you (and why would use use protozero and not the
-Google protobuf library if it doesn't) there is another way:
+buffer. If this concerns you (and why would you use protozero and not the
+Google protobuf library if it doesn't?) there is another way:
 
-    std::string buffer;
-    osmium::util::pbf_writer pbf_parent(buffer);
+    std::string data;
+    protozero::pbf_writer pbf_parent(data);
 
     // optionally add fields to parent here
     pbf_parent.add_...(...);
@@ -460,7 +515,7 @@ Google protobuf library if it doesn't) there is another way:
     {
         // create new pbf_writer with parent and the tag (field number)
         // as parameters
-        osmium::util::pbf_writer pbf_sub(pbf_parent, 1);
+        protozero::pbf_writer pbf_sub(pbf_parent, 1);
 
         // add fields to sub here...
         pbf_sub.add_...(...);
@@ -479,6 +534,32 @@ into it. It then adds the contents of the submessage to the buffer. When the
 written in the reserved space. If less space was needed for the length field
 than was available, the rest of the buffer is moved over a few bytes.
 
+You can abandon writing of submessage if this becomes necessary by
+calling `rollback()`:
+
+    std::string data;
+    protozero::pbf_writer pbf_parent(data);
+
+    // open a new scope
+    {
+        // create new pbf_writer with parent and the tag (field number)
+        // as parameters
+        protozero::pbf_writer pbf_sub(pbf_parent, 1);
+
+        // add fields to sub here...
+        pbf_sub.add_...(...);
+
+        // some problem occurs and you want to abandon the submessage:
+        pbf_sub.rollback();
+    }
+
+    // optionally add more fields to parent here
+    pbf_parent.add_...(...);
+
+The result is the same as if the lines inside the nested brackets had never
+been called. Do not try to call any of the `add_*` functions on the submessage
+after a rollback.
+
 ## Writing Protobuf-Encoded Messages Using `pbf_builder`
 
 Just like the `pbf_message` template class wraps the `pbf_reader` class, there
@@ -489,6 +570,20 @@ integers.
 
 See the `test/t/complex` test case for a complete example using this interface.
 
+## Reserving memory
+
+If you know beforehand how large a message will become or can take an educated
+guess, you can call the usual `std::string::reserve()` on the underlying string
+before you give it to an `pbf_writer` or `pbf_builder` object.
+
+Or you can (at any time) call `reserve()` on the `pbf_writer` or `pbf_builder`.
+This will reserve the given amount of bytes in addition to whatever is already
+in that message.
+
+In the general case it is not easy to figure out how much memory you will need
+because of the varint packing of integers. But sometimes you can make at least
+a rough estimate. Still, you should probably only use this facility if you have
+benchmarks proving that it actually makes your program faster.
 
 ## Using the Low-Level Varint and Zigzag Encoding and Decoding Functions
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/protozero.git



More information about the Pkg-grass-devel mailing list