1
0
mirror of https://github.com/catchorg/Catch2.git synced 2025-04-28 19:53:52 +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

@ -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__':