Commit 34c678f8 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

exception_shared_string

Summary:
A refcounted-string type suitable for exception types.

Exception types should ordinarily be nothrow-copy-constructible, which excludes holding `std::string` fields, and should ordinarily be as small as possible, which excludes holding `std::shared_ptr<std::string>` fields.

Reviewed By: luciang

Differential Revision: D33010058

fbshipit-source-id: ae71f00d2053c9c52b9a980d028dce8397704cda
parent fa92f656
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#include <atomic> #include <atomic>
#include <cassert> #include <cassert>
#include <cstring>
#include <folly/lang/New.h>
// Accesses std::type_info and std::exception_ptr internals. Since these vary // Accesses std::type_info and std::exception_ptr internals. Since these vary
// by platform and library, import or copy the structure and function // by platform and library, import or copy the structure and function
...@@ -414,4 +417,48 @@ void* exception_ptr_get_object( ...@@ -414,4 +417,48 @@ void* exception_ptr_get_object(
#endif // defined(_WIN32) #endif // defined(_WIN32)
struct exception_shared_string::state {
// refcount ops use relaxed order since the string is immutable: side-effects
// need not be made visible to the destructor since there are none
static constexpr auto relaxed = std::memory_order_relaxed;
std::atomic<std::size_t> refs{0u};
std::size_t const size{0u};
static constexpr std::size_t object_size(std::size_t const len) noexcept {
return sizeof(state) + len + 1u;
}
static state* make(char const* const str, std::size_t const len) {
assert(len == std::strlen(str));
auto addr = operator_new(object_size(len), align_val_t{alignof(state)});
return new (addr) state(str, len);
}
state(char const* const str, std::size_t const len) noexcept : size{len} {
std::memcpy(static_cast<void*>(this + 1u), str, len + 1u);
}
char const* what() const noexcept {
return static_cast<char const*>(static_cast<void const*>(this + 1u));
}
void copy() noexcept { refs.fetch_add(1u, relaxed); }
void ruin() noexcept {
if (!refs.load(relaxed) || !refs.fetch_sub(1u, relaxed)) {
operator_delete(this, object_size(size), align_val_t{alignof(state)});
}
}
};
exception_shared_string::exception_shared_string(char const* const str)
: exception_shared_string{str, std::strlen(str)} {}
exception_shared_string::exception_shared_string(
char const* const str, std::size_t const len)
: state_{state::make(str, len)} {}
exception_shared_string::exception_shared_string(
exception_shared_string const& that) noexcept
: state_{(that.state_->copy(), that.state_)} {}
exception_shared_string::~exception_shared_string() {
state_->ruin();
}
char const* exception_shared_string::what() const noexcept {
return state_->what();
}
} // namespace folly } // namespace folly
...@@ -338,4 +338,34 @@ T* exception_ptr_get_object(std::exception_ptr const& ptr) noexcept { ...@@ -338,4 +338,34 @@ T* exception_ptr_get_object(std::exception_ptr const& ptr) noexcept {
return static_cast<T*>(object); return static_cast<T*>(object);
} }
// exception_shared_string
//
// An immutable refcounted string, with the same layout as a pointer, suitable
// for use in an exception. Exceptions are intended to cheaply nothrow-copy-
// constructible and mostly do not need to optimize moves, and this affects how
// exception messages are best stored.
class exception_shared_string {
private:
static void test_params_(char const*, std::size_t);
struct state;
state* const state_;
public:
explicit exception_shared_string(char const*);
exception_shared_string(char const*, std::size_t);
template <
typename String,
typename = decltype(test_params_(
FOLLY_DECLVAL(String const&).data(),
FOLLY_DECLVAL(String const&).size()))>
explicit exception_shared_string(String const& str)
: exception_shared_string{str.data(), str.size()} {}
exception_shared_string(exception_shared_string const&) noexcept;
~exception_shared_string();
void operator=(exception_shared_string const&) = delete;
char const* what() const noexcept;
};
} // namespace folly } // namespace folly
...@@ -212,3 +212,15 @@ TEST_F(ExceptionTest, exception_ptr_vmi) { ...@@ -212,3 +212,15 @@ TEST_F(ExceptionTest, exception_ptr_vmi) {
EXPECT_EQ(1, folly::exception_ptr_get_object<A1>(ptr)->value); EXPECT_EQ(1, folly::exception_ptr_get_object<A1>(ptr)->value);
EXPECT_EQ(44, *(C*)(folly::exception_ptr_get_object(ptr))); EXPECT_EQ(44, *(C*)(folly::exception_ptr_get_object(ptr)));
} }
TEST_F(ExceptionTest, exception_shared_string) {
constexpr auto c = "hello, world!";
auto s0 = folly::exception_shared_string(c);
auto s1 = s0;
auto s2 = s1;
EXPECT_STREQ(c, s2.what());
EXPECT_STREQ(c, folly::exception_shared_string(std::string_view(c)).what());
EXPECT_STREQ(c, folly::exception_shared_string(std::string(c)).what());
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment