Skip to content

Optimize and add ability to configure error printing function #11

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

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Changes from 3 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
50 changes: 41 additions & 9 deletions include/cjdb/contracts.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,46 @@
#ifndef CJDB_CONTRACTS_HPP
#define CJDB_CONTRACTS_HPP

#include <cstdio>
#include <string_view>
#include <type_traits>

#ifdef _MSC_VER
#define CJDB_PRETTY_FUNCTION __FUNCSIG__
#define CJDB_FORCE_INLINE __forceinline
#else
#define CJDB_PRETTY_FUNCTION __PRETTY_FUNCTION__
#define CJDB_FORCE_INLINE [[gnu::always_inline]] inline
#endif // _MSC_VER

#ifndef CJDB_PRINT_ERROR
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this guard protecting against?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea is that the user could optionally define their own CJDB_PRINT_ERROR function.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cjdb::contracts_detail is a detail namespace, so a user can't do this under the proposed model. If you want to make this configurable, please put it in namespace cjdb. I think the macro should probably be opt-out, not opt-in? Otherwise folks won't get any output in their debug builds, which would be surprising.

Copy link
Contributor Author

@rayhamel rayhamel May 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea is that the user could define or choose an appropriate function under whichever namespace, then define the function-like macro CJDB_PRINT_ERROR to call that function. The cjdb::contracts_detail::print_error function is only created when CJDB_PRINT_ERROR is not already defined.

There may of course be better ways of making this configurable.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please have a think about a more C++-oriented way to configure this? You don't need to implement it just yet: come back here and post the idea, but if we can avoid a macro, that'd be great.

Copy link
Contributor Author

@rayhamel rayhamel Jun 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the implementation to use a function pointer cjdb::print_error which the user can set to something else. I still do have two macro configuration options CJDB_USE_IOSTREAM (as you know, #include <iostream> introduces some overhead, so should be opt-in) and CJDB_SKIP_STDIO, in which case there's also no dependency on cstdio and the user must provide their own definition for cjdb::print_error.

#ifdef CJDB_USE_STDIO
#include <cstdio>

namespace cjdb::contracts_detail {
struct print_error_fn {
CJDB_FORCE_INLINE void operator()(std::string_view const message) const noexcept
{
std::fwrite(message.data(), sizeof(char), message.size(), stderr);
}
};
inline constexpr auto print_error = print_error_fn{};
} // namespace cjdb::contracts_detail
#else
#include <iostream>

namespace cjdb::contracts_detail {
struct print_error_fn {
CJDB_FORCE_INLINE void operator()(std::string_view const message) const noexcept
try {
std::cerr << message;
} catch(...) {}
};
inline constexpr auto print_error = print_error_fn{};
} // namespace cjdb::contracts_detail
#endif // CJDB_USE_STDIO
#define CJDB_PRINT_ERROR(MESSAGE) ::cjdb::contracts_detail::print_error(MESSAGE)
#endif // CJDB_PRINT_ERROR

// clang-tidy doesn't yet support this
//
// #ifndef __cpp_lib_is_constant_evaluated
Expand All @@ -18,12 +54,6 @@
#define CJDB_ASSERT(...) CJDB_CONTRACT_IMPL("assertion", __VA_ARGS__)
#define CJDB_ENSURES(...) CJDB_CONTRACT_IMPL("post-condition", __VA_ARGS__)

#ifdef _MSC_VER
#define CJDB_PRETTY_FUNCTION __FUNCSIG__
#else
#define CJDB_PRETTY_FUNCTION __PRETTY_FUNCTION__
#endif // _MSC_VER

namespace cjdb::contracts_detail {
#ifdef NDEBUG
inline constexpr auto is_debug = false;
Expand All @@ -39,7 +69,9 @@ namespace cjdb::contracts_detail {
if (not result) {
if (not std::is_constant_evaluated()) {
if constexpr (is_debug) {
std::fprintf(stderr, "%s in `%s`\n", message.data(), function.data());
CJDB_PRINT_ERROR(message);
CJDB_PRINT_ERROR(function);
CJDB_PRINT_ERROR("`\n");
}
}
#ifdef _MSC_VER
Expand Down Expand Up @@ -69,7 +101,7 @@ namespace cjdb::contracts_detail {

#define CJDB_CONTRACT_IMPL(CJDB_KIND, ...) \
::cjdb::contracts_detail::contract_impl(::cjdb::contracts_detail::matches_bool(__VA_ARGS__), \
__FILE__ ":" CJDB_TO_STRING(__LINE__) ": " CJDB_KIND " `" #__VA_ARGS__ "` failed", \
__FILE__ ":" CJDB_TO_STRING(__LINE__) ": " CJDB_KIND " `" #__VA_ARGS__ "` failed in `", \
CJDB_PRETTY_FUNCTION)


Expand Down