Skip to content

Commit

Permalink
Merge pull request #172 from snitch-org/cschreib/reporter-state
Browse files Browse the repository at this point in the history
Remove shared reporter state (+add inplace_any and type_id)
  • Loading branch information
cschreib authored May 27, 2024
2 parents f324947 + add06dc commit d663212
Show file tree
Hide file tree
Showing 18 changed files with 388 additions and 48 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ set(SNITCH_MAX_UNIQUE_TAGS 1024 CACHE STRING "Maximum number of unique
set(SNITCH_MAX_COMMAND_LINE_ARGS 1024 CACHE STRING "Maximum number of command line arguments to a test application.")
set(SNITCH_MAX_REGISTERED_REPORTERS 8 CACHE STRING "Maximum number of registered reporter that can be selected from the command line.")
set(SNITCH_MAX_PATH_LENGTH 1024 CACHE STRING "Maximum length of a file path when writing output to file.")
set(SNITCH_MAX_REPORTER_SIZE_BYTES 128 CACHE STRING "Maximum size (in bytes) of a reporter object.")

# Feature toggles.
set(SNITCH_DEFINE_MAIN ON CACHE BOOL "Define main() in snitch -- disable to provide your own main() function.")
Expand Down Expand Up @@ -60,6 +61,7 @@ configure_file("${PROJECT_SOURCE_DIR}/include/snitch/snitch_config.hpp.config"

set(SNITCH_INCLUDES
${PROJECT_SOURCE_DIR}/include/snitch/snitch.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_any.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_append.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_capture.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_cli.hpp
Expand Down Expand Up @@ -89,6 +91,7 @@ set(SNITCH_INCLUDES
${PROJECT_SOURCE_DIR}/include/snitch/snitch_string.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_string_utility.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_test_data.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_type_id.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_type_name.hpp
${PROJECT_SOURCE_DIR}/include/snitch/snitch_vector.hpp
${PROJECT_BINARY_DIR}/snitch/snitch_config.hpp)
Expand Down Expand Up @@ -219,6 +222,7 @@ if (SNITCH_DO_TEST)
SNITCH_MAX_MESSAGE_LENGTH=129
SNITCH_MAX_TEST_NAME_LENGTH=130
SNITCH_MAX_CAPTURE_LENGTH=131
SNITCH_MAX_REPORTER_SIZE_BYTES=16
SNITCH_DEFINE_MAIN=0)
endfunction()

Expand Down
2 changes: 2 additions & 0 deletions include/snitch/snitch.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef SNITCH_HPP
#define SNITCH_HPP

#include "snitch/snitch_any.hpp"
#include "snitch/snitch_append.hpp"
#include "snitch/snitch_capture.hpp"
#include "snitch/snitch_cli.hpp"
Expand Down Expand Up @@ -31,6 +32,7 @@
#include "snitch/snitch_string.hpp"
#include "snitch/snitch_string_utility.hpp"
#include "snitch/snitch_test_data.hpp"
#include "snitch/snitch_type_id.hpp"
#include "snitch/snitch_type_name.hpp"
#include "snitch/snitch_vector.hpp"

Expand Down
108 changes: 108 additions & 0 deletions include/snitch/snitch_any.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#ifndef SNITCH_ANY_HPP
#define SNITCH_ANY_HPP

#include "snitch/snitch_config.hpp"
#include "snitch/snitch_error_handling.hpp"
#include "snitch/snitch_function.hpp"
#include "snitch/snitch_type_id.hpp"

#include <array>
#include <cstddef>
#include <utility>

namespace snitch {
namespace impl {
template<typename T>
void delete_object(char* storage) noexcept {
reinterpret_cast<T*>(storage)->~T();
}
} // namespace impl

template<std::size_t MaxSize>
class inplace_any {
std::array<char, MaxSize> storage = {};
function_ref<void(char*) noexcept> deleter = [](char*) noexcept {};
type_id_t id = type_id<void>();

void release() noexcept {
deleter = [](char*) noexcept {};
id = type_id<void>();
}

public:
constexpr inplace_any() = default;

inplace_any(const inplace_any&) = delete;

constexpr inplace_any(inplace_any&& other) noexcept :
storage(other.storage), deleter(other.deleter), id(other.id) {
other.release();
}

inplace_any& operator=(const inplace_any&) = delete;

constexpr inplace_any& operator=(inplace_any&& other) noexcept {
reset();
storage = other.storage;
deleter = other.deleter;
id = other.id;
other.release();
return *this;
}

template<typename T, typename... Args>
explicit inplace_any(std::in_place_type_t<T>, Args&&... args) {
emplace<T>(std::forward<Args>(args)...);
}

~inplace_any() {
reset();
}

bool has_value() const noexcept {
return id != type_id<void>();
}

type_id_t type() const noexcept {
return id;
}

template<typename T, typename... Args>
void emplace(Args&&... args) {
static_assert(
sizeof(T) <= MaxSize,
"This type is too large to fit in this inplace_any, increase storage size");

reset();
new (storage.data()) T(std::forward<Args>(args)...);
deleter = &impl::delete_object<T>;
id = type_id<T>();
}

// Requires: not empty and stored type == T.
template<typename T>
const T& get() const {
if (!has_value()) {
assertion_failed("inplace_any is empty");
}
if (type() != type_id<T>()) {
assertion_failed("inplace_any holds an object of a different type");
}

return *reinterpret_cast<const T*>(storage.data());
}

// Requires: not empty and stored type == T.
template<typename T>
T& get() {
return const_cast<T&>(const_cast<const inplace_any*>(this)->get<T>());
}

void reset() noexcept {
deleter(storage.data());
release();
}
};
} // namespace snitch

#endif
3 changes: 3 additions & 0 deletions include/snitch/snitch_config.hpp.config
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
#if !defined(SNITCH_MAX_PATH_LENGTH)
# define SNITCH_MAX_PATH_LENGTH ${SNITCH_MAX_PATH_LENGTH}
#endif
#if !defined(SNITCH_MAX_REPORTER_SIZE_BYTES)
# define SNITCH_MAX_REPORTER_SIZE_BYTES ${SNITCH_MAX_REPORTER_SIZE_BYTES}
#endif
#if !defined(SNITCH_DEFINE_MAIN)
#cmakedefine01 SNITCH_DEFINE_MAIN
#endif
Expand Down
28 changes: 3 additions & 25 deletions include/snitch/snitch_macros_reporter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,9 @@
static const std::string_view SNITCH_MACRO_CONCAT(reporter_id_, __COUNTER__) \
[[maybe_unused]] = snitch::tests.add_reporter(NAME, __VA_ARGS__)

#define SNITCH_REGISTER_REPORTER_IMPL(NAME, TYPE, COUNTER) \
static std::optional<TYPE> SNITCH_MACRO_CONCAT(reporter_, COUNTER); \
static void SNITCH_MACRO_CONCAT(reporter_init_, COUNTER)(snitch::registry & r) noexcept { \
SNITCH_MACRO_CONCAT(reporter_, COUNTER).emplace(r); \
} \
template<typename T> \
static bool SNITCH_MACRO_CONCAT(reporter_config_, COUNTER)( \
snitch::registry & r, std::string_view k, std::string_view v) noexcept { \
return SNITCH_MACRO_CONCAT(reporter_, COUNTER)->configure(r, k, v); \
} \
static void SNITCH_MACRO_CONCAT(reporter_report_, COUNTER)( \
const snitch::registry& r, const snitch::event::data& e) noexcept { \
SNITCH_MACRO_CONCAT(reporter_, COUNTER)->report(r, e); \
} \
static void SNITCH_MACRO_CONCAT(reporter_finish_, COUNTER)(snitch::registry&) noexcept { \
SNITCH_MACRO_CONCAT(reporter_, COUNTER).reset(); \
} \
static const std::string_view SNITCH_MACRO_CONCAT(reporter_id_, COUNTER) [[maybe_unused]] = \
snitch::tests.add_reporter( \
NAME, &SNITCH_MACRO_CONCAT(reporter_init_, COUNTER), \
&SNITCH_MACRO_CONCAT(reporter_config_, COUNTER) < TYPE >, \
&SNITCH_MACRO_CONCAT(reporter_report_, COUNTER), \
&SNITCH_MACRO_CONCAT(reporter_finish_, COUNTER))

#define SNITCH_REGISTER_REPORTER(NAME, TYPE) SNITCH_REGISTER_REPORTER_IMPL(NAME, TYPE, __COUNTER__)
#define SNITCH_REGISTER_REPORTER(NAME, TYPE) \
static const std::string_view SNITCH_MACRO_CONCAT(reporter_id_, __COUNTER__) \
[[maybe_unused]] = snitch::tests.add_reporter<TYPE>(NAME)

// clang-format off
#if SNITCH_WITH_SHORTHAND_MACROS
Expand Down
49 changes: 42 additions & 7 deletions include/snitch/snitch_registry.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef SNITCH_REGISTRY_HPP
#define SNITCH_REGISTRY_HPP

#include "snitch/snitch_any.hpp"
#include "snitch/snitch_append.hpp"
#include "snitch/snitch_cli.hpp"
#include "snitch/snitch_config.hpp"
Expand Down Expand Up @@ -34,6 +35,8 @@ constexpr std::size_t max_tag_length = SNITCH_MAX_TAG_LENGTH;
constexpr std::size_t max_unique_tags = SNITCH_MAX_UNIQUE_TAGS;
// Maximum number of registered reporters to select from the command line.
constexpr std::size_t max_registered_reporters = SNITCH_MAX_REGISTERED_REPORTERS;
// Maximum size of a reporter instance, in bytes.
constexpr std::size_t max_reporter_size_bytes = SNITCH_MAX_REPORTER_SIZE_BYTES;
} // namespace snitch

namespace snitch::impl {
Expand Down Expand Up @@ -102,6 +105,13 @@ struct registered_reporter {
finish_report_function finish = [](registry&) noexcept {};
};

template<typename T>
concept reporter_type =
requires(registry& reg) { T{reg}; } &&
requires(T& rep, registry& reg, std::string_view k, std::string_view v) {
{ rep.configure(reg, k, v) } -> convertible_to<bool>;
} && requires(T& rep, const registry& reg, const event::data& e) { rep.report(reg, e); };

class registry {
// Contains all registered test cases.
small_vector<impl::test_case, max_test_cases> test_list;
Expand All @@ -112,8 +122,27 @@ class registry {
// Used when writing output to file.
std::optional<impl::file_writer> file_writer;

// the default console reporter
snitch::reporter::console::reporter console_reporter;
// Type-erased storage for the current reporter instance.
inplace_any<max_reporter_size_bytes> reporter_storage;

template<typename T>
void initialize_reporter(registry&) noexcept {
this->reporter_storage.emplace<T>(*this);
}

template<typename T>
void report(const registry&, const event::data& e) noexcept {
this->reporter_storage.get<T>().report(*this, e);
}

template<typename T>
bool configure_reporter(registry&, std::string_view k, std::string_view v) noexcept {
return this->reporter_storage.get<T>().configure(*this, k, v);
}

SNITCH_EXPORT void destroy_reporter(registry&) noexcept;

SNITCH_EXPORT void report_default(const registry&, const event::data& e) noexcept;

public:
enum class verbosity { quiet, normal, high, full } verbose = verbosity::normal;
Expand All @@ -125,9 +154,8 @@ class registry {
using report_function = snitch::report_function;
using finish_report_function = snitch::finish_report_function;

print_function print_callback = &snitch::impl::stdout_print;
report_function report_callback = {
console_reporter, snitch::constant<&snitch::reporter::console::reporter::report>{}};
print_function print_callback = &snitch::impl::stdout_print;
report_function report_callback = {*this, constant<&registry::report_default>{}};
finish_report_function finish_callback = [](registry&) noexcept {};

// Internal API; do not use.
Expand Down Expand Up @@ -178,8 +206,15 @@ class registry {
const report_function& report,
const std::optional<finish_report_function>& finish);

// Internal API; do not use.
SNITCH_EXPORT std::string_view add_console_reporter();
// Requires: number of reporters + 1 <= max_registered_reporters.
template<reporter_type T>
std::string_view add_reporter(std::string_view name) {
return this->add_reporter(
name, initialize_report_function{*this, constant<&registry::initialize_reporter<T>>{}},
configure_report_function{*this, constant<&registry::configure_reporter<T>>{}},
report_function{*this, constant<&registry::report<T>>{}},
finish_report_function{*this, constant<&registry::destroy_reporter>{}});
}

// Internal API; do not use.
// Requires: number of tests + 1 <= max_test_cases, well-formed test ID.
Expand Down
2 changes: 1 addition & 1 deletion include/snitch/snitch_reporter_console.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ struct reporter {

reporter() = default;

SNITCH_EXPORT void init(registry& r) noexcept;
SNITCH_EXPORT explicit reporter(registry& r) noexcept;

SNITCH_EXPORT bool configure(registry&, std::string_view, std::string_view) noexcept;

Expand Down
29 changes: 29 additions & 0 deletions include/snitch/snitch_type_id.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#ifndef SNITCH_TYPE_ID_HPP
#define SNITCH_TYPE_ID_HPP

#include "snitch/snitch_config.hpp"

namespace snitch {
using type_id_t = const void*;
}

namespace snitch::impl {
template<typename T>
struct type_id {
constexpr static char value = 0;
};
} // namespace snitch::impl

namespace snitch {
template<typename T>
type_id_t type_id() noexcept {
return &impl::type_id<T>::value;
}

template<>
constexpr type_id_t type_id<void>() noexcept {
return nullptr;
}
} // namespace snitch

#endif
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ add_project_link_arguments(cpp_arguments, language : 'cpp')
include_dirs = include_directories('.', 'include')

headers = files('include/snitch/snitch.hpp',
'include/snitch/snitch_any.hpp',
'include/snitch/snitch_append.hpp',
'include/snitch/snitch_capture.hpp',
'include/snitch/snitch_cli.hpp',
Expand Down Expand Up @@ -58,6 +59,7 @@ headers = files('include/snitch/snitch.hpp',
'include/snitch/snitch_string_utility.hpp',
'include/snitch/snitch_test_data.hpp',
'include/snitch/snitch_type_name.hpp',
'include/snitch/snitch_type_id.hpp',
'include/snitch/snitch_vector.hpp')

sources = files('src/snitch_append.cpp',
Expand Down
1 change: 1 addition & 0 deletions meson_options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ option('max_unique_tags' ,type: 'integer' ,value: 1024, description: 'M
option('max_command_line_args' ,type: 'integer' ,value: 1024, description: 'Maximum number of command line arguments to a test application.')
option('max_registered_reporters' ,type: 'integer' ,value: 8 , description: 'Maximum number of registered reporter that can be selected from the command line.')
option('max_path_length' ,type: 'integer' ,value: 1024, description: 'Maximum length of a file path when writing output to file.')
option('max_reporter_size_bytes' ,type: 'integer' ,value: 128, description: 'Maximum size (in bytes) of a reporter object.')

# Feature toggles.
option('define_main' ,type: 'boolean' ,value: true, description: 'Define main() in snitch -- disable to provide your own main() function.')
Expand Down
1 change: 1 addition & 0 deletions snitch/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ conf_data = configuration_data({
'SNITCH_MAX_COMMAND_LINE_ARGS' : get_option('max_command_line_args'),
'SNITCH_MAX_REGISTERED_REPORTERS' : get_option('max_registered_reporters'),
'SNITCH_MAX_PATH_LENGTH' : get_option('max_path_length'),
'SNITCH_MAX_REPORTER_SIZE_BYTES' : get_option('max_reporter_size_bytes'),

'SNITCH_DEFINE_MAIN' : get_option('define_main').to_int(),
'SNITCH_WITH_EXCEPTIONS' : get_option('with_exceptions').to_int(),
Expand Down
22 changes: 12 additions & 10 deletions src/snitch_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,13 +336,18 @@ std::string_view registry::add_reporter(
return name;
}

std::string_view registry::add_console_reporter() {
using reporter_type = snitch::reporter::console::reporter;
return add_reporter("console",
initialize_report_function{console_reporter, snitch::constant<&reporter_type::init>{}},
configure_report_function{console_reporter, snitch::constant<&reporter_type::configure>{}},
{console_reporter, snitch::constant<&snitch::reporter::console::reporter::report>{}},
{});
void registry::destroy_reporter(registry&) noexcept {
reporter_storage.reset();
}

void registry::report_default(const registry&, const event::data& e) noexcept {
using default_reporter = reporter::console::reporter;

if (reporter_storage.type() != type_id<default_reporter>()) {
reporter_storage.emplace<default_reporter>(*this);
}

reporter_storage.get<default_reporter>().report(*this, e);
}

const char*
Expand Down Expand Up @@ -1028,6 +1033,3 @@ small_vector_span<const registered_reporter> registry::reporters() const noexcep

constinit registry tests;
} // namespace snitch

static const std::string_view console_reporter_id [[maybe_unused]] =
snitch::tests.add_console_reporter();
Loading

0 comments on commit d663212

Please sign in to comment.