Skip to content
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
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -448,11 +448,13 @@ Hosted users who want standard-library interoperability can opt in:
target_compile_definitions(my_target PRIVATE PROTOCYTE_ENABLE_STD_STRING_VIEW=1)
```

When `PROTOCYTE_ENABLE_STD_STRING_VIEW` is defined, the runtime includes
`<string_view>` and both `::protocyte::Span<char>` / `Span<const char>` and
`::protocyte::String` are implicitly convertible to `std::string_view`. The
default accessor return type remains `Span<const char>`, so code that does not
enable the option keeps the smaller no-exception surface.
When `PROTOCYTE_ENABLE_STD_STRING_VIEW` is set to a nonzero value, the runtime
includes `<string_view>` and both `::protocyte::Span<char>` / `Span<const char>`
and `::protocyte::String` are implicitly convertible to `std::string_view`.
Generated immutable `string` field accessors also return `std::string_view` under
this opt-in, so hosted code can pass string fields directly to
standard-library APIs such as `std::format`. Code that does not enable the
option keeps the smaller no-exception `Span<const char>` accessor surface.

In a Windows kernel driver, one technically possible MSVC/STL-specific escape
hatch is to provide the STL's internal out-of-range throw helper yourself so
Expand Down
10 changes: 5 additions & 5 deletions smoke/generated/compat.protocyte.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ namespace protocyte_smoke::test::compat {
}
constexpr void clear_value() noexcept { value_ = {}; }

::protocyte::Span<const char> label() const noexcept { return label_.view(); }
::protocyte::StringView label() const noexcept { return label_.view(); }
typename Config::String &mutable_label() noexcept { return label_; }
template<class Value>::protocyte::Status set_label(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -652,7 +652,7 @@ namespace protocyte_smoke::test::compat {
}
constexpr void clear_f_double() noexcept { f_double_ = {}; }

::protocyte::Span<const char> f_string() const noexcept { return f_string_.view(); }
::protocyte::StringView f_string() const noexcept { return f_string_.view(); }
typename Config::String &mutable_f_string() noexcept { return f_string_; }
template<class Value>::protocyte::Status set_f_string(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -741,8 +741,8 @@ namespace protocyte_smoke::test::compat {
constexpr bool has_oneof_string() const noexcept {
return special_oneof_case_ == Special_oneofCase::oneof_string;
}
::protocyte::Span<const char> oneof_string() const noexcept {
return has_oneof_string() ? special_oneof.oneof_string.view() : ::protocyte::Span<const char> {};
::protocyte::StringView oneof_string() const noexcept {
return has_oneof_string() ? special_oneof.oneof_string.view() : ::protocyte::StringView {};
}
template<class Value>::protocyte::Status set_oneof_string(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -852,7 +852,7 @@ namespace protocyte_smoke::test::compat {
has_opt_int32_ = false;
}

::protocyte::Span<const char> opt_string() const noexcept { return opt_string_.view(); }
::protocyte::StringView opt_string() const noexcept { return opt_string_.view(); }
bool has_opt_string() const noexcept { return has_opt_string_; }
typename Config::String &mutable_opt_string() noexcept {
has_opt_string_ = true;
Expand Down
2 changes: 2 additions & 0 deletions smoke/generated/cross_package.protocyte.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

#include <protocyte/runtime/runtime.hpp>

#if !PROTOCYTE_ENABLE_STD_STRING_VIEW
#include <string_view>
#endif

namespace test::crosspkg {

Expand Down
22 changes: 12 additions & 10 deletions smoke/generated/example.protocyte.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

#include <protocyte/runtime/runtime.hpp>

#if !PROTOCYTE_ENABLE_STD_STRING_VIEW
#include <string_view>
#endif

namespace test::ultimate {

Expand Down Expand Up @@ -96,7 +98,7 @@ namespace test::ultimate {
return out;
}

::protocyte::Span<const char> description() const noexcept { return description_.view(); }
::protocyte::StringView description() const noexcept { return description_.view(); }
typename Config::String &mutable_description() noexcept { return description_; }
template<class Value>::protocyte::Status set_description(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -394,7 +396,7 @@ namespace test::ultimate {
return out;
}

::protocyte::Span<const char> name() const noexcept { return name_.view(); }
::protocyte::StringView name() const noexcept { return name_.view(); }
typename Config::String &mutable_name() noexcept { return name_; }
template<class Value>::protocyte::Status set_name(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -1118,7 +1120,7 @@ namespace test::ultimate {
deep_oneof_case_ = Deep_oneofCase::none;
}

::protocyte::Span<const char> extreme() const noexcept { return extreme_.view(); }
::protocyte::StringView extreme() const noexcept { return extreme_.view(); }
typename Config::String &mutable_extreme() noexcept { return extreme_; }
template<class Value>::protocyte::Status set_extreme(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -1168,8 +1170,8 @@ namespace test::ultimate {
}

constexpr bool has_text() const noexcept { return deep_oneof_case_ == Deep_oneofCase::text; }
::protocyte::Span<const char> text() const noexcept {
return has_text() ? deep_oneof.text.view() : ::protocyte::Span<const char> {};
::protocyte::StringView text() const noexcept {
return has_text() ? deep_oneof.text.view() : ::protocyte::StringView {};
}
template<class Value>::protocyte::Status set_text(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -2297,7 +2299,7 @@ namespace test::ultimate {
}
constexpr void clear_f_bool() noexcept { f_bool_ = {}; }

::protocyte::Span<const char> f_string() const noexcept { return f_string_.view(); }
::protocyte::StringView f_string() const noexcept { return f_string_.view(); }
typename Config::String &mutable_f_string() noexcept { return f_string_; }
template<class Value>::protocyte::Status set_f_string(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -2399,8 +2401,8 @@ namespace test::ultimate {
constexpr bool has_oneof_string() const noexcept {
return special_oneof_case_ == Special_oneofCase::oneof_string;
}
::protocyte::Span<const char> oneof_string() const noexcept {
return has_oneof_string() ? special_oneof.oneof_string.view() : ::protocyte::Span<const char> {};
::protocyte::StringView oneof_string() const noexcept {
return has_oneof_string() ? special_oneof.oneof_string.view() : ::protocyte::StringView {};
}
template<class Value>::protocyte::Status set_oneof_string(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down Expand Up @@ -2749,7 +2751,7 @@ namespace test::ultimate {
has_opt_int32_ = false;
}

::protocyte::Span<const char> opt_string() const noexcept { return opt_string_.view(); }
::protocyte::StringView opt_string() const noexcept { return opt_string_.view(); }
bool has_opt_string() const noexcept { return has_opt_string_; }
typename Config::String &mutable_opt_string() noexcept {
has_opt_string_ = true;
Expand Down Expand Up @@ -6063,7 +6065,7 @@ namespace test::ultimate {
return out;
}

::protocyte::Span<const char> tag() const noexcept { return tag_.view(); }
::protocyte::StringView tag() const noexcept { return tag_.view(); }
typename Config::String &mutable_tag() noexcept { return tag_; }
template<class Value>::protocyte::Status set_tag(const Value &value) noexcept
requires(::protocyte::ByteSpanSource<Value> && !::protocyte::TextSource<Value>)
Expand Down
14 changes: 10 additions & 4 deletions smoke/generated/protocyte/runtime/runtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include <new>
#include <type_traits>

#ifdef PROTOCYTE_ENABLE_STD_STRING_VIEW
#if PROTOCYTE_ENABLE_STD_STRING_VIEW
#include <string_view>
#endif

Expand Down Expand Up @@ -1269,11 +1269,11 @@ namespace protocyte {
constexpr usize size() const noexcept { return size_; }
constexpr usize size_bytes() const noexcept { return size_ * sizeof(T); }
constexpr bool empty() const noexcept { return size_ == 0u; }
#ifdef PROTOCYTE_ENABLE_STD_STRING_VIEW
#if PROTOCYTE_ENABLE_STD_STRING_VIEW
constexpr operator ::std::string_view() const noexcept
requires(::std::same_as<::std::remove_cv_t<T>, char>)
{
return data_ == nullptr ? ::std::string_view {} : ::std::string_view {data_, size_};
return ::std::string_view {data_, size_};
}
#endif
template<usize Count> constexpr Span<T, Count> first() const noexcept
Expand Down Expand Up @@ -1316,6 +1316,12 @@ namespace protocyte {
requires(!DataSizeSpanSource<Range &> && PointerSpanSource<Range &>)
Span(Range &) -> Span<::std::remove_pointer_t<SpanBeginPointer<Range &>>>;

#if PROTOCYTE_ENABLE_STD_STRING_VIEW
using StringView = ::std::string_view;
#else
using StringView = Span<const char>;
#endif

template<class T>
concept SpanSource = requires(T &value) { Span {value}; } || requires(const T &value) { Span {value}; };

Expand Down Expand Up @@ -2869,7 +2875,7 @@ namespace protocyte {
usize size() const noexcept { return bytes_.size(); }
usize length() const noexcept { return size(); }
bool empty() const noexcept { return bytes_.empty(); }
#ifdef PROTOCYTE_ENABLE_STD_STRING_VIEW
#if PROTOCYTE_ENABLE_STD_STRING_VIEW
operator ::std::string_view() const noexcept { return view(); }
#endif
void clear() noexcept { bytes_.clear(); }
Expand Down
17 changes: 17 additions & 0 deletions smoke/src/host_smoke.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#if __has_include(<format>)
#include <format>
#endif
#include <limits>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -214,6 +217,16 @@ namespace {
return protocyte::bytes_equal(lhs, rhs);
}

template<class R, protocyte::usize RExtent>
bool view_equal(std::string_view lhs, protocyte::Span<R, RExtent> rhs) noexcept {
return view_equal(protocyte::Span<const char> {lhs.data(), lhs.size()}, rhs);
}

template<class L, protocyte::usize LExtent>
bool view_equal(protocyte::Span<L, LExtent> lhs, std::string_view rhs) noexcept {
return view_equal(lhs, protocyte::Span<const char> {rhs.data(), rhs.size()});
}

template<size_t N> protocyte::Span<const protocyte::u8> view_of(const uint8_t (&data)[N]) noexcept {
return protocyte::Span<const protocyte::u8> {data, N};
}
Expand Down Expand Up @@ -2603,8 +2616,12 @@ TEST_CASE("byte setters accept contiguous byte containers", "[smoke][runtime][by
const auto string_view = protocyte::byte_span_of(string_payload);
REQUIRE(string_view);
CHECK(view_equal(message.f_string(), *string_view));
static_assert(std::is_same_v<decltype(message.f_string()), std::string_view>);
const std::string_view converted_span = message.f_string();
CHECK(converted_span == std::string_view {"hello"});
#if defined(__cpp_lib_format)
CHECK(std::format("{}", message.f_string()) == "hello");
#endif
const std::string_view converted_string = message.mutable_f_string();
CHECK(converted_string == converted_span);

Expand Down
28 changes: 28 additions & 0 deletions smoke/src/kernel_driver_smoke.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,34 @@ void __cdecl operator delete[](void *ptr) noexcept { ::operator delete(ptr); }
void __cdecl operator delete[](void *ptr, size_t) noexcept { ::operator delete(ptr); }
#endif

#if PROTOCYTE_ENABLE_STD_STRING_VIEW && defined(_DEBUG)
// MSVC's debug STL imports these CRT assertion hooks through __imp_* data symbols.
__declspec(noreturn) void protocyte_debug_crt_shim_bugcheck(const char *symbol_name) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL,
"Protocyte kernel smoke: MSVC STL debug CRT fallback called for %s.\n", symbol_name);
KeBugCheckEx(MANUALLY_INITIATED_CRASH, 0x50535643u, 0u, 0u, 0u);
for (;;) {}
}

extern "C" __declspec(noreturn) void __cdecl protocyte_invoke_watson_shim(const wchar_t *, const wchar_t *,
const wchar_t *, unsigned int, uintptr_t) {
protocyte_debug_crt_shim_bugcheck("_invoke_watson");
}

extern "C" int __cdecl protocyte_crt_dbg_report_shim(int, const char *, int, const char *, const char *, ...) {
protocyte_debug_crt_shim_bugcheck("_CrtDbgReport");
}

using protocyte_invoke_watson_fn = void(__cdecl *)(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int,
uintptr_t);
using protocyte_crt_dbg_report_fn = int(__cdecl *)(int, const char *, int, const char *, const char *, ...);

extern "C" {
protocyte_invoke_watson_fn __imp__invoke_watson = &protocyte_invoke_watson_shim;
protocyte_crt_dbg_report_fn __imp__CrtDbgReport = &protocyte_crt_dbg_report_shim;
}
#endif

namespace {

constexpr ULONG protocyte_pool_tag = 'TyCP';
Expand Down
37 changes: 27 additions & 10 deletions src/protocyte/cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def generate_header(file_model: FileModel, options: GeneratorOptions) -> str:
w.line(f"#include <{options.runtime_prefix}/runtime.hpp>")
extra_includes: list[str] = []
if _file_uses_string_view(file_model):
extra_includes.append("#include <string_view>")
extra_includes.extend(["#if !PROTOCYTE_ENABLE_STD_STRING_VIEW", "#include <string_view>", "#endif"])
for dependency in sorted(file_model.dependencies):
extra_includes.append(f'#include "{_include_path(dependency, options)}"')
if extra_includes:
Expand Down Expand Up @@ -813,11 +813,13 @@ def emit_setter_body() -> None:
return
if item.kind in {"string", "bytes"}:
typ = _field_type(item, options)
view_type = "::protocyte::Span<const char>" if item.kind == "string" else "::protocyte::Span<const ::protocyte::u8>"
w.line(f"{view_type} {item.cpp_name}() const noexcept {{ return {_member(item)}.view(); }}")
if item.proto3_optional:
w.line(f"bool has_{item.cpp_name}() const noexcept {{ return has_{item.cpp_name}_; }}")
w.line(f"{typ}& mutable_{item.cpp_name}() noexcept {{")
if item.kind == "string":
_emit_string_view_accessor(w, item.cpp_name, f"{_member(item)}.view()")
else:
w.line(f"::protocyte::Span<const ::protocyte::u8> {item.cpp_name}() const noexcept {{ return {_member(item)}.view(); }}")
if item.proto3_optional:
w.line(f"bool has_{item.cpp_name}() const noexcept {{ return has_{item.cpp_name}_; }}")
w.line(f"{typ}& mutable_{item.cpp_name}() noexcept {{")
w.push()
if item.proto3_optional:
w.line(f"has_{item.cpp_name}_ = true;")
Expand Down Expand Up @@ -879,10 +881,17 @@ def _emit_oneof_accessors(w: CppWriter, item: FieldModel, options: GeneratorOpti
f"constexpr bool has_{item.cpp_name}() const noexcept {{ return {case_member} == {case_type}::{item.cpp_name}; }}"
)
if item.kind in {"string", "bytes"}:
view_type = "::protocyte::Span<const char>" if item.kind == "string" else "::protocyte::Span<const ::protocyte::u8>"
w.line(
f"{view_type} {item.cpp_name}() const noexcept {{ return has_{item.cpp_name}() ? {_member(item)}.view() : {view_type}{{}}; }}"
)
if item.kind == "string":
_emit_string_view_accessor(
w,
item.cpp_name,
f"has_{item.cpp_name}() ? {_member(item)}.view() : ::protocyte::StringView{{}}",
)
else:
view_type = "::protocyte::Span<const ::protocyte::u8>"
w.line(
f"{view_type} {item.cpp_name}() const noexcept {{ return has_{item.cpp_name}() ? {_member(item)}.view() : {view_type}{{}}; }}"
)
def emit_setter_body() -> None:
if item.kind == "bytes" and item.array_enabled:
w.line(
Expand Down Expand Up @@ -1961,6 +1970,14 @@ def _runtime_scalar_type(cpp_type: str) -> str:
return _RUNTIME_SCALAR_TYPES.get(cpp_type, cpp_type)


def _emit_string_view_accessor(
w: CppWriter,
name: str,
expr: str,
) -> None:
w.line(f"::protocyte::StringView {name}() const noexcept {{ return {expr}; }}")


def _file_uses_string_view(file_model: FileModel) -> bool:
if any(constant.kind == CONSTANT_KIND_STRING for constant in file_model.constants):
return True
Expand Down
14 changes: 10 additions & 4 deletions src/protocyte/runtime/runtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include <new>
#include <type_traits>

#ifdef PROTOCYTE_ENABLE_STD_STRING_VIEW
#if PROTOCYTE_ENABLE_STD_STRING_VIEW
#include <string_view>
#endif

Expand Down Expand Up @@ -1269,11 +1269,11 @@ namespace protocyte {
constexpr usize size() const noexcept { return size_; }
constexpr usize size_bytes() const noexcept { return size_ * sizeof(T); }
constexpr bool empty() const noexcept { return size_ == 0u; }
#ifdef PROTOCYTE_ENABLE_STD_STRING_VIEW
#if PROTOCYTE_ENABLE_STD_STRING_VIEW
constexpr operator ::std::string_view() const noexcept
requires(::std::same_as<::std::remove_cv_t<T>, char>)
{
return data_ == nullptr ? ::std::string_view {} : ::std::string_view {data_, size_};
return ::std::string_view {data_, size_};
}
#endif
template<usize Count> constexpr Span<T, Count> first() const noexcept
Expand Down Expand Up @@ -1316,6 +1316,12 @@ namespace protocyte {
requires(!DataSizeSpanSource<Range &> && PointerSpanSource<Range &>)
Span(Range &) -> Span<::std::remove_pointer_t<SpanBeginPointer<Range &>>>;

#if PROTOCYTE_ENABLE_STD_STRING_VIEW
using StringView = ::std::string_view;
#else
using StringView = Span<const char>;
#endif

template<class T>
concept SpanSource = requires(T &value) { Span {value}; } || requires(const T &value) { Span {value}; };

Expand Down Expand Up @@ -2869,7 +2875,7 @@ namespace protocyte {
usize size() const noexcept { return bytes_.size(); }
usize length() const noexcept { return size(); }
bool empty() const noexcept { return bytes_.empty(); }
#ifdef PROTOCYTE_ENABLE_STD_STRING_VIEW
#if PROTOCYTE_ENABLE_STD_STRING_VIEW
operator ::std::string_view() const noexcept { return view(); }
#endif
void clear() noexcept { bytes_.clear(); }
Expand Down
Loading
Loading