Commit a7513594 authored by Felix Morgner's avatar Felix Morgner
Browse files

week08: add materials

parent a1012983
File added
# Compile-Time Computation
# Multi-Threading and Mutexes
If you have not completed it yet, finish the testat exercise first!
In this exercise...
Goals:
In these exercises you will...
* ... you get familiar with the thread API of C++
* ... observe the effect of compile-time evaluation on the resulting executable binary.
* ... implement your own literal type
* ... implement compile-time member functions
# 1. Basic Threading
## Compile-Time Hashing
***Note:*** On Linux you might need to add the `pthread` library to link the projects correctly: `Project Properties -> C++ General -> Paths & Symbols -> Libraries -> Add -> "pthread"`
**Context:** You have to write an application that requires a password for accessing higher privilege level for special configurations. This password shall be hard-coded into the binary.
## a) Thread IDs
Create a simple application that starts three threads:
It is probably not a smart idea to store passwords in a program in plain text. As it can be easily extracted from the binary's read only data section. An alternative might be to hash the password first and compare it against the hashed password provided by the user. Nevertheless, it is inconvenient to place password hashes directly into your source code. However, that is not necessary if you have a hashing function that can be called at compile-time.
* One thread shall be created with a functor
* One thread shall be created with a free function
* One thread shall be created with a lambda
We have prepared an example project that uses a constexpr function for hashing an input string: [here](week08/exercise_templates/w08_template_01_CompileTimeHashing)
Each thread prints its ID (`std::this_thread::get_id()`) on `std::cout`.
Your task:
Since it is discouraged to use `std::cout` directly in your functor, function and lambda, each should have a `std::ostream & out` parameter. If you want to pass `std::cout` through the `std::thread` constructor, you have to wrap the argument in an `std::ref` on the call-site.
* Import the project in Cevelop or build it on console (`make`). It is a Makefile project that will compile two versions of a program. One will compute the hash of a password at runtime (`runtime.cpp`) the other will compute the hash of the password at compile-time (`compile_time.cpp`). The make file will also dump (with `objdump`) and scan the contents of the resulting binaries for the expected hash and the password.
* Check the output to see which implementation contains the password in plain text and which only contains the hash value.
* When checking the content you can use the `objdump` command, which is also included in the `Makefile`. Depending on your system and the binary format you will have to check different sections. Umcomment either of the `ODMPSECTION` variables in the `Makefile` to enable showing the corresponding section. Only one section can be displayed:
```make
#ODMPSECTION = -j .rodata
#ODMPSECTION = -j .rdata
ODMPSECTION = -j .data
```
## b) Measure Speedup
In the template you have an implementation of a program that counts the prime numbers in a given range (`countPrimes` function). Parallelize the computation using multiple threads in `countPrimesParallel`.
**Credits:** We have not implemented the compile-time hash functions ourselves, but use an existing implementation of [Chocobo1](https://github.com/Chocobo1/Hash), that is published under GPL3.0.
***Note:*** Be aware, that each thread must only access his entry of the `results` array and that the `main`thread must only access those results after the threads have been joined.
## Literal Vector Type
Implement an N-dimensional `Vector` literal type. Don't worry, we don't want you to implement a STL-like container here. We rather want you to implement a simple type in the mathematical sense of vector, that can be used at compile-time. We have prepared a project template containing test cases [here](week08/exercise_templates/w08_template_02_VectorLiteralType).
The CUTE tests also contain `static_assert`s to show the compile-time capabilities of your `Vector`, at least for the cases where this is feasible. The CUTE `ASSERT`s in the test are for convenience reasons. Because a `static_assert` will not tell you what the actual and the expected values effectively were on failure, it is amenable to still have the CUTE framework for checking the values at run-time to see the comparison. You just need to comment the `static_assert` out.
**Note:** Since several test cases rely on class template argument deduction, Cevelop will mark the code as incorrect in many places, even though it might be correct! For this exercise rely on the compiler output instead.
Implement the following features:
* Allow creation of a vector with explicit class template arguments: `constexpr Vector<double, 1> v{1.0}`.
* Use an `std::array` for storing the `Vector`'s values.
* You need to implement a constructor that is a variadic template to accommodate the arbitrary number of constructor arguments.
* Implement the subscript operator to access the individual components: `v[0]`
* Check that accessing an invalid index with the subscript operator throws an exception. Can you do this at compile-time?
* Overload the subscript operator that it distinguishes between `const` and non-`const` access. Can both be used in `constexpr` contexts?
* Implement the `==` and `!=` operators for comparing `Vector`s.
* Implement an output operator (`<<`) for printing `Vector`'s on an `std::ostream`. Can you implement that as `constexpr` function?
* Implement a `length()` member function that returns the length of a `Vector`.
* Implement a `scalarProduct()` function for calculating the scalar product of a `Vector`.
* **Difficult:** Implement class template argument deduction for `Vector`:
* Implement a deduction guide for that constructor.
* Can you restrict that it is not possible to supply different argument types for the construction? You ca use the predefined `are_all_same` predicate. It takes multiple type arguments and has the value `true` if all types are the same. You can apply it in an `std::enable_if_t` default template argument for the constructor.
* Is it possible to check that the compilation fails when initializing a `Vector` with different types through a succeeding test?
## Optional: Range Constructor
You can add a range-constructor to your `BoundedBuffer` and add a deduction guide for it.
Signature of the range-constructor:
```cpp
template <typename Iter>
BoundedBuffer(Iter begin, Iter end);
```
\ No newline at end of file
What speedup can you achieve?
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>w08_solution_02_VectorLiteralType</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>ch.hsr.ifs.cute.ui.cutenature</nature>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
</projectDescription>
#include "cute.h"
#include "ide_listener.h"
#include "xml_listener.h"
#include "cute_runner.h"
#include "vector.h"
#include <sstream>
void testCreateVectorWithSingleElement() {
constexpr Vector<double, 1> v{1.0};
static_assert(v[0] == 1.0);
ASSERT_EQUAL(1.0, v[0]);
}
void testCreateVectorWithTwoElements() {
constexpr Vector<double, 2> v{1.0, 2.0};
static_assert(v[0] == 1.0);
static_assert(v[1] == 2.0);
ASSERT_EQUAL(1.0, v[0]);
ASSERT_EQUAL(2.0, v[1]);
}
void testCreateVectorWithoutElements() {
constexpr Vector<double, 0> v{};
ASSERT(true);
}
void testAccessOutOfBoundsThrows() {
constexpr Vector<double, 1> v{1.0};
// static_assert(v[1] == 0.0);
ASSERT_THROWS(v[1], std::out_of_range);
}
void testDeduceTemplateArguments() {
constexpr Vector v{1.0, 2.0};
static_assert(v[0] == 1.0);
static_assert(v[1] == 2.0);
ASSERT_EQUAL(1.0, v[0]);
ASSERT_EQUAL(2.0, v[1]);
}
constexpr auto create() {
Vector v{1.0, 2.0};
v[0] = 3.0;
return v;
}
void testAllowModificationOfNonConstElement() {
constexpr auto v = create();
static_assert(v[0] == 3.0);
static_assert(v[1] == 2.0);
ASSERT_EQUAL(3.0, v[0]);
ASSERT_EQUAL(2.0, v[1]);
}
void testCalculateLengthOfZeroVector() {
constexpr Vector v{0.0, 0.0, 0.0, 0.0};
constexpr auto length = v.length();
static_assert(length == 0.0);
ASSERT_EQUAL(0.0, length);
}
void testCalculateLength() {
constexpr Vector v{1.0, 2.0, 3.0};
constexpr auto length = v.length();
static_assert(length == std::sqrt(14.0));
ASSERT_EQUAL(std::sqrt(14.0), length);
}
void testCalculateLengthOfUnitVector() {
constexpr Vector v{1.0 / std::sqrt(14.0), 2.0 / std::sqrt(14.0), 3.0 / std::sqrt(14.0)};
constexpr auto length = v.length();
static_assert(length == 1.0);
ASSERT_EQUAL(1.0, length);
}
void testEqualityComparison() {
constexpr Vector v{1.0, 2.5, 3.2};
static_assert(v == v);
ASSERT_EQUAL(v, v);
}
void testUnequalityComparision() {
constexpr Vector lhs{1.0, 2.5, 3.3};
constexpr Vector rhs{1.0, 2.5, 3.1};
static_assert(lhs != rhs);
ASSERT_NOT_EQUAL_TO(lhs, rhs);
}
void testOutputOperator() {
constexpr Vector v{1.1, 2.5, 3.3};
std::ostringstream out { };
out << v;
ASSERT_EQUAL("(1.1, 2.5, 3.3)", out.str());
}
void testScalarProduct() {
constexpr Vector lhs{1.0, 2.0, 3.0, 4.0};
constexpr Vector rhs{4.2, 1.1, 0.0, 9.5};
constexpr double expected = 44.4;
static_assert(lhs.scalarProduct(rhs) == expected);
ASSERT_EQUAL(expected, lhs.scalarProduct(rhs));
}
void testDifferentTypes() {
constexpr Vector fail{1., 1.f};
}
bool runAllTests(int argc, char const *argv[]) {
cute::suite s { };
s.push_back(CUTE(testCreateVectorWithSingleElement));
s.push_back(CUTE(testCreateVectorWithTwoElements));
s.push_back(CUTE(testCreateVectorWithoutElements));
s.push_back(CUTE(testAccessOutOfBoundsThrows));
s.push_back(CUTE(testDeduceTemplateArguments));
s.push_back(CUTE(testAllowModificationOfNonConstElement));
s.push_back(CUTE(testCalculateLengthOfZeroVector));
s.push_back(CUTE(testCalculateLength));
s.push_back(CUTE(testCalculateLengthOfUnitVector));
s.push_back(CUTE(testEqualityComparison));
s.push_back(CUTE(testUnequalityComparision));
s.push_back(CUTE(testOutputOperator));
s.push_back(CUTE(testScalarProduct));
cute::xml_file_opener xmlfile(argc, argv);
cute::xml_listener<cute::ide_listener<>> lis(xmlfile.out);
auto runner = cute::makeRunner(lis, argc, argv);
bool success = runner(s, "AllTests");
return success;
}
int main(int argc, char const *argv[]) {
return runAllTests(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE;
}
#ifndef VECTOR_H_
#define VECTOR_H_
#include <array>
#include <cmath>
#include <cstddef>
#include <type_traits>
#include <utility>
#include <ostream>
template<typename T, typename ...U>
constexpr bool are_all_same = std::conjunction_v<std::is_same<T, std::remove_cv_t<std::remove_reference_t<U>>>...>;
template<typename T, std::size_t N>
class Vector {
std::array<T, N> values { };
public:
template<typename ...U, typename = std::enable_if_t<are_all_same<T, U...>>>
explicit constexpr Vector(U &&... u) :
values { std::forward<U>(u)... } {
}
constexpr T const & operator[](size_t index) const {
return values.at(index);
}
constexpr T & operator[](size_t index) {
return values.at(index);
}
constexpr T length() const {
T sum{};
for (auto index = 0u; index < N; index++) {
sum += values[index] * values[index];
}
return std::sqrt(sum);
}
constexpr T scalarProduct(Vector const & other) const {
T sum{};
for (auto index = 0u; index < N; index++) {
sum += values[index] * other[index];
}
return sum;
}
constexpr bool operator==(Vector const & other) const {
for (size_t index = 0u; index < N; index++) {
if ((*this)[index] != other[index]) {
return false;
}
}
return true;
}
constexpr bool operator!=(Vector const & other) const {
return !(*this == other);
}
};
template <typename T, std::size_t N>
std::ostream & operator<<(std::ostream & out, Vector<T, N> const & v) {
out << '(';
for (size_t index = 0u; index < N; index++) {
if (index > 0u) {
out << ", ";
}
out << v[index];
}
out << ')';
return out;
}
template <typename Head, typename...U>
Vector(Head, U ...) -> Vector<Head, sizeof...(U) + 1>;
#endif /* VECTOR_H_ */
/*********************************************************************************
* This file is part of CUTE.
*
* Copyright (c) 2006-2018 Peter Sommerlad, IFS
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*********************************************************************************/
#ifndef CUTE_H_
#define CUTE_H_
// all CUTE includes for writing tests and suites
#include "cute_base.h"
#include "cute_equals.h"
#include "cute_relops.h"
#include "cute_data_driven.h"
#include "cute_throws.h"
#include "cute_suite.h"
#include "cute_repeated_test.h"
#include "cute_suite_test.h"
#include "cute_test_incarnate.h"
#include "cute_test.h"
#include "cute_testmember.h"
#include "cute_version.h"
#endif /*CUTE_H_*/
/*********************************************************************************
* This file is part of CUTE.
*
* Copyright (c) 2007-2018 Peter Sommerlad, IFS
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*********************************************************************************/
#ifndef CUTE_BASE_H_
#define CUTE_BASE_H_
#include <string>
#include "cute_to_string.h"
#include "cute_determine_version.h"
namespace cute {
struct test_failure {
std::string reason;
std::string filename;
int lineno;
test_failure(std::string const &r,char const *f, int line)
:reason(r),filename(f),lineno(line)
{ }
char const * what() const { return reason.c_str(); }
};
}
#if defined(USE_STD11) && !defined(_MSC_VER)
#define CUTE_FUNCNAME_PREFIX std::string(__func__)+": "
#else
#if defined( _MSC_VER ) || defined(__GNUG__)
//#if defined(USE_STD11)
//#define CUTE_FUNCNAME_PREFIX
// workaround doesn't work namespace { char const __FUNCTION__ []="lambda";}
// MSC can not use lambdas outside of function bodies for tests.
//#endif
// use -D CUTE_FUNCNAME_PREFIX if you want to use non-local lambdas with test macros
#if !defined(CUTE_FUNCNAME_PREFIX)
#define CUTE_FUNCNAME_PREFIX std::string(__FUNCTION__)+": "
#endif
#else // could provide #defines for other compiler-specific extensions... need to experiment, i.e., MS uses __FUNCTION__
#define CUTE_FUNCNAME_PREFIX std::string("")
#endif
#endif
#define ASSERTM(msg,cond) do { if (!(cond)) throw cute::test_failure(CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg),__FILE__,__LINE__);} while(false)
#define ASSERT(cond) ASSERTM(#cond,cond)
#define FAIL() ASSERTM("FAIL()",false)
#define FAILM(msg) ASSERTM(msg,false)
#endif /*CUTE_BASE_H_*/
/*********************************************************************************
* This file is part of CUTE.
*
* Copyright (c) 2007-2018 Peter Sommerlad, IFS
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*********************************************************************************/
#ifndef CUTE_COUNTING_LISTENER_H_
#define CUTE_COUNTING_LISTENER_H_
#include "cute_listener.h"
namespace cute{
template <typename Listener=null_listener>
struct counting_listener:Listener{
counting_listener()
:Listener()
,numberOfTests(0),successfulTests(0),failedTests(0),errors(0),numberOfSuites(0),numberOfTestsInSuites(0){}
counting_listener(Listener const &s)
:Listener(s)
,numberOfTests(0),successfulTests(0),failedTests(0),errors(0),numberOfSuites(0),numberOfTestsInSuites(0){}
void begin(suite const &s,char const *info, size_t n_of_tests){
++numberOfSuites;
numberOfTestsInSuites+=n_of_tests;
Listener::begin(s,info, n_of_tests);
}
void start(test const &t){
++numberOfTests;
Listener::start(t);
}
void success(test const &t,char const *msg){
++successfulTests;
Listener::success(t,msg);
}
void failure(test const &t,test_failure const &e){
++failedTests;
Listener::failure(t,e);
}
void error(test const &t,char const *what){
++errors;
Listener::error(t,what);
}
size_t numberOfTests;
size_t successfulTests;
size_t failedTests;
size_t errors;
size_t numberOfSuites;
size_t numberOfTestsInSuites;
};
}
#endif /*CUTE_COUNTING_LISTENER_H_*/
/*********************************************************************************
* This file is part of CUTE.
*
* Copyright (c) 2013-2018 Peter Sommerlad, IFS
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*********************************************************************************/
#ifndef CUTE_DATA_DRIVEN_H_
#define CUTE_DATA_DRIVEN_H_
#include "cute_base.h"
#include "cute_equals.h"
#include "cute_relops.h"
#define DDTM(msg) (cute::test_failure(cute::cute_to_string::backslashQuoteTabNewline(msg),__FILE__,__LINE__))
#define DDT() DDTM("")
#define ASSERT_DDTM(cond,msg,failure) do{ \
if (!(cond)) \
throw cute::test_failure(CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg)+(failure).reason,\
(failure).filename.c_str(),(failure).lineno);\
} while (false)
#define ASSERT_DDT(cond,failure) ASSERT_DDTM(cond,#cond,(failure))
#define ASSERT_EQUAL_DDTM(msg,expected,actual,failure) cute::assert_equal((expected),(actual),\
CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg)\
+(failure).reason,(failure).filename.c_str(),(failure).lineno)
#define ASSERT_EQUAL_DDT(expected,actual,failure) ASSERT_EQUAL_DDTM(#expected " == " #actual, (expected),(actual),(failure))
#define ASSERT_EQUAL_DELTA_DDTM(msg,expected,actual,delta,failure) cute::assert_equal_delta((expected),(actual),(delta),\
CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg)\
+(failure).reason,(failure).filename.c_str(),(failure).lineno)
#define ASSERT_EQUAL_DELTA_DDT(expected,actual,delta,failure) ASSERT_EQUAL_DELTA_DDTM(#expected " == " #actual " with error " #delta ,(expected),(actual),(delta),(failure))
#define ASSERT_LESS_DDTM(msg,left,right,failure) cute::assert_relop<std::less>((left),(right),\
CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg)\
+(failure).reason,(failure).filename.c_str(),(failure).lineno)
#define ASSERT_LESS_DDT(left,right,failure) ASSERT_LESS_DDTM(#left " < " #right, (left),(right),(failure))
#define ASSERT_LESS_EQUAL_DDTM(msg,left,right,failure) cute::assert_relop<std::less_equal>((left),(right),\
CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg)\
+(failure).reason,(failure).filename.c_str(),(failure).lineno)
#define ASSERT_LESS_EQUAL_DDT(left,right,failure) ASSERT_LESS_EQUAL_DDTM(#left " <= " #right, (left),(right),(failure))
#define ASSERT_GREATER_DDTM(msg,left,right,failure) cute::assert_relop<std::greater>((left),(right),\
CUTE_FUNCNAME_PREFIX+cute::cute_to_string::backslashQuoteTabNewline(msg)\
+(failure).reason,(failure).filename.c_str(),(failure).lineno)
#define ASSERT_GREATER_DDT(left,right,failure) ASSERT_GREATER_DDTM(#left " > " #right, (left),(right),(failure))
#define ASSERT_GREATER_EQUAL_DDTM(msg,left,right,failure) cute::assert_relop<std::greater_equal>((left),(right),\