Skip to content

Remove shared reporter state (+add inplace_any and type_id) #172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

Check warning on line 25 in include/snitch/snitch_type_id.hpp

View check run for this annotation

Codecov / codecov/patch

include/snitch/snitch_type_id.hpp#L24-L25

Added lines #L24 - L25 were not covered by tests
}
} // 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
Loading