1
0
mirror of https://github.com/catchorg/Catch2.git synced 2025-01-15 14:48:00 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
Joe Burzinski
9a8963133f Update single header generation script to warn about unused headers 2019-11-21 16:22:04 +01:00
Joe Burzinski
cfba9dce97 Fix wrong namespacing of benchmarking constructor helpers 2019-11-21 16:22:04 +01:00
4 changed files with 90 additions and 54 deletions

View File

@ -168,7 +168,7 @@ Note that it is not possible to simply use the same instance for different runs
and resetting it between each run since that would pollute the measurements with
the resetting code.
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
the same semantics as providing a callable to `meter.measure` with `int` argument:
```c++
@ -189,19 +189,17 @@ construct and destroy objects without dynamic allocation and in a way that lets
you measure construction and destruction separately.
```c++
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter)
{
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
meter.measure([&](int i) { storage[i].construct("thing"); });
})
};
BENCHMARK_ADVANCED("destroy", [](Catch::Benchmark::Chronometer meter)
{
BENCHMARK_ADVANCED("destroy")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
for(auto&& o : storage)
o.construct("thing");
meter.measure([&](int i) { storage[i].destruct(); });
})
};
```
`Catch::Benchmark::storage_for<T>` objects are just pieces of raw storage suitable for `T`

View File

@ -14,60 +14,62 @@
#include <type_traits>
namespace Catch {
namespace Detail {
template <typename T, bool Destruct>
struct ObjectStorage
{
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
ObjectStorage() : data() {}
ObjectStorage(const ObjectStorage& other)
namespace Benchmark {
namespace Detail {
template <typename T, bool Destruct>
struct ObjectStorage
{
new(&data) T(other.stored_object());
}
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
ObjectStorage(ObjectStorage&& other)
{
new(&data) T(std::move(other.stored_object()));
}
ObjectStorage() : data() {}
~ObjectStorage() { destruct_on_exit<T>(); }
ObjectStorage(const ObjectStorage& other)
{
new(&data) T(other.stored_object());
}
template <typename... Args>
void construct(Args&&... args)
{
new (&data) T(std::forward<Args>(args)...);
}
ObjectStorage(ObjectStorage&& other)
{
new(&data) T(std::move(other.stored_object()));
}
template <bool AllowManualDestruction = !Destruct>
typename std::enable_if<AllowManualDestruction>::type destruct()
{
stored_object().~T();
}
~ObjectStorage() { destruct_on_exit<T>(); }
private:
// If this is a constructor benchmark, destruct the underlying object
template <typename U>
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
// Otherwise, don't
template <typename U>
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
template <typename... Args>
void construct(Args&&... args)
{
new (&data) T(std::forward<Args>(args)...);
}
T& stored_object()
{
return *static_cast<T*>(static_cast<void*>(&data));
}
template <bool AllowManualDestruction = !Destruct>
typename std::enable_if<AllowManualDestruction>::type destruct()
{
stored_object().~T();
}
TStorage data;
};
private:
// If this is a constructor benchmark, destruct the underlying object
template <typename U>
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
// Otherwise, don't
template <typename U>
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
T& stored_object()
{
return *static_cast<T*>(static_cast<void*>(&data));
}
TStorage data;
};
}
template <typename T>
using storage_for = Detail::ObjectStorage<T, true>;
template <typename T>
using destructable_object = Detail::ObjectStorage<T, false>;
}
template <typename T>
using storage_for = Detail::ObjectStorage<T, true>;
template <typename T>
using destructable_object = Detail::ObjectStorage<T, false>;
}
#endif // TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED

View File

@ -126,5 +126,19 @@ TEST_CASE("Benchmark containers", "[!benchmark]") {
REQUIRE(v[i] == generated);
}
}
SECTION("construct and destroy example") {
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
meter.measure([&](int i) { storage[i].construct("thing"); });
};
BENCHMARK_ADVANCED("destroy")(Catch::Benchmark::Chronometer meter) {
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
for(auto&& o : storage)
o.construct("thing");
meter.measure([&](int i) { storage[i].destruct(); });
};
}
}
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING

View File

@ -23,6 +23,7 @@ def generate(v):
blankParser = re.compile( r'^\s*$')
seenHeaders = set([])
possibleHeaders = set([])
rootPath = os.path.join( catchPath, 'include/' )
outputPath = os.path.join( catchPath, 'single_include/catch2/catch.hpp' )
@ -52,8 +53,20 @@ def generate(v):
if globals['includeImpl'] or globals['implIfDefs'] == -1:
out.write( line )
def getDirsToSearch( ):
return [os.path.join( rootPath, s) for s in ['', 'internal', 'reporters', 'internal/benchmark', 'internal/benchmark/detail']]
def collectPossibleHeaders():
dirs = getDirsToSearch()
for dir in dirs:
hpps = glob(os.path.join(dir, '*.hpp'))
hs = glob(os.path.join(dir, '*.h'))
possibleHeaders.update( hpp.rpartition( os.sep )[2] for hpp in hpps )
possibleHeaders.update( h.rpartition( os.sep )[2] for h in hs )
def insertCpps():
dirs = [os.path.join( rootPath, s) for s in ['', 'internal', 'reporters', 'internal/benchmark', 'internal/benchmark/detail']]
dirs = getDirsToSearch()
cppFiles = []
for dir in dirs:
cppFiles += glob(os.path.join(dir, '*.cpp'))
@ -103,6 +116,13 @@ def generate(v):
write( line.rstrip() + "\n" )
write( u'// end {}\n'.format(filename) )
def warnUnparsedHeaders():
unparsedHeaders = possibleHeaders.difference( seenHeaders )
# These headers aren't packaged into the unified header, exclude them from any warning
whitelist = ['catch.hpp', 'catch_reporter_teamcity.hpp', 'catch_with_main.hpp', 'catch_reporter_automake.hpp', 'catch_reporter_tap.hpp', 'catch_reporter_sonarqube.hpp']
unparsedHeaders = unparsedHeaders.difference( whitelist )
if unparsedHeaders:
print( "WARNING: unparsed headers detected\n{0}\n".format( unparsedHeaders ) )
write( u"/*\n" )
write( u" * Catch v{0}\n".format( v.getVersionString() ) )
@ -117,11 +137,13 @@ def generate(v):
write( u"#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
write( u"#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n" )
collectPossibleHeaders()
parseFile( rootPath, 'catch.hpp' )
warnUnparsedHeaders()
write( u"#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED\n\n" )
out.close()
print ("Generated single include for Catch v{0}\n".format( v.getVersionString() ) )
print( "Generated single include for Catch v{0}\n".format( v.getVersionString() ) )
if __name__ == '__main__':