Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
10 changes: 4 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: mymindstorm/setup-emsdk@v14
- name: Install Rust nightly
- name: Install Rust stable
run: |
rustup toolchain install nightly
rustup default nightly
rustup toolchain install stable
rustup target add wasm32-wasip1
rustup component add rustfmt
- name: Install Clang 19
Expand All @@ -42,10 +41,9 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: mymindstorm/setup-emsdk@v14
- name: Install Rust nightly
- name: Install Rust stable
run: |
rustup toolchain install nightly
rustup default nightly
rustup toolchain install stable
rustup target add wasm32-wasip1
rustup component add rustfmt
- name: Install osmium
Expand Down
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [Name mapping](./call_rust_from_cpp/name_mapping.md)
- [Wellknown traits](./call_rust_from_cpp/wellknown_traits.md)
- [Layout policy](./call_rust_from_cpp/layout_policy.md)
- [Fields](./call_rust_from_cpp/fields.md)
- [Types with special support](./call_rust_from_cpp/special_types.md)
- [Panic and exceptions](./call_rust_from_cpp/panic_and_exceptions.md)
- [Calling C++ from Rust](./call_cpp_from_rust/index.md)
Expand Down
38 changes: 38 additions & 0 deletions book/src/call_rust_from_cpp/fields.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Fields as underlying types

When you declare fields in a tuple or struct using `field name (offset = X, type = T);`, the generated C++ exposes helper wrapper types:

- `rust::FieldOwned<T, OFFSET>` for fields on owning types
- `rust::FieldRef<T, OFFSET>` for fields on `Ref<Ty>`
- `rust::FieldRefMut<T, OFFSET>` for fields on `RefMut<Ty>`

These wrappers now act as their underlying type `T` in many contexts:

- `Ref<T>` construction from any `Field*<T, OFFSET>`
- Implicit read via `operator T()` and `.read()` for value-like access
Copy link
Owner

Choose a reason for hiding this comment

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

This .read() is leftover.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, good catch! Thanks.

- Method calls are forwarded when applicable

Example:

```C++
rust::Tuple<int32_t, rust::std::string::String> t{42, "hi"_rs.to_owned()};

// Read value
int32_t v = t.f0; // operator T() on FieldOwned<int32_t, 0>

// Get a Ref<T> from a field
rust::Ref<int32_t> r = t.f0;

// Access methods through Ref from Field wrappers
rust::Ref<rust::std::string::String> sref = t.f1;
auto len = sref.len();

// From references to container, fields become FieldRef/FieldRefMut
rust::Ref<decltype(t)> rt = t;
auto l1 = rt.f1.len();

rust::RefMut<decltype(t)> mt = t;
mt.f1.push_str("!"_rs);
```

See `examples/regression_test1` for a runnable demonstration.
9 changes: 9 additions & 0 deletions examples/regression_test1/expected_output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,12 @@ Test fields and constructor work -- started
[main.cpp:59] v4.f1.field2.len() = 12
Test fields and constructor work -- finished

Test Field* underlying conversions -- started
[main.cpp:70] v0 = 42
[main.cpp:76] sref.len() = 2
[main.cpp:79] int32_t(pref.f0) = 42
[main.cpp:80] pref.f1.len() = 2
[main.cpp:83] int32_t(pmut.f0) = 42
[main.cpp:85] pmut.f1.len() = 3
Test Field* underlying conversions -- finished

27 changes: 27 additions & 0 deletions examples/regression_test1/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,34 @@ void test_fields_and_constructor() {
zngur_dbg(v4.f1.field2.len());
}

void test_field_underlying_conversions() {
auto scope = rust::crate::Scoped::new_("Test Field* underlying conversions"_rs);

rust::Tuple<int32_t, rust::std::string::String> pair{42, "hi"_rs.to_owned()};

// FieldOwned conversion to Ref and value
rust::Ref<int32_t> r0 = pair.f0;
int32_t v0 = pair.f0;
zngur_dbg(v0);
// TODO: Add support for conversion to T for all fields.
// rust::std::string::String v1 = pair.f1;

// FieldOwned<String> to Ref<String> and call a method
rust::Ref<rust::std::string::String> sref = pair.f1;
zngur_dbg(sref.len());

rust::Ref<rust::Tuple<int32_t, rust::std::string::String>> pref = pair;
zngur_dbg(int32_t(pref.f0));
zngur_dbg(pref.f1.len());

rust::RefMut<rust::Tuple<int32_t, rust::std::string::String>> pmut = pair;
zngur_dbg(int32_t(pmut.f0));
pmut.f1.push_str("!"_rs);
zngur_dbg(pmut.f1.len());
}

int main() {
test_dbg_works_for_ref_and_refmut();
test_fields_and_constructor();
test_field_underlying_conversions();
}
10 changes: 9 additions & 1 deletion examples/regression_test1/main.zng
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,16 @@ type (::std::string::String, crate::Foo) {
field 1 (offset = 24, type = crate::Foo);
}

type (i32, ::std::string::String) {
#layout(size = 32, align = 8);
wellknown_traits(Debug);

field 0 (offset = 0, type = i32);
field 1 (offset = 8, type = ::std::string::String);
}

type crate::Scoped {
#layout(size = 16, align = 8);

fn new(&str) -> crate::Scoped;
}
}
37 changes: 34 additions & 3 deletions zngur-generator/src/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1658,13 +1658,19 @@ namespace rust {
struct RefMut;

template<typename T, size_t OFFSET>
struct FieldOwned;
struct FieldOwned {
inline operator T() const noexcept { return *::rust::Ref<T>(*this); }
};

template<typename T, size_t OFFSET>
struct FieldRef;
struct FieldRef {
inline operator T() const noexcept { return *::rust::Ref<T>(*this); }
};

template<typename T, size_t OFFSET>
struct FieldRefMut;
struct FieldRefMut {
inline operator T() const noexcept { return *::rust::Ref<T>(*this); }
};

template<typename... T>
struct Tuple;
Expand Down Expand Up @@ -1759,6 +1765,21 @@ namespace rust {
data = reinterpret_cast<size_t>(__zngur_internal_data_ptr(t));
}}

template<size_t OFFSET>
Ref(const FieldOwned< {ty}, OFFSET >& f) {{
data = reinterpret_cast<size_t>(&f) + OFFSET;
}}

template<size_t OFFSET>
Ref(const FieldRef< {ty}, OFFSET >& f) {{
data = *reinterpret_cast<const size_t*>(&f) + OFFSET;
}}

template<size_t OFFSET>
Ref(const FieldRefMut< {ty}, OFFSET >& f) {{
data = *reinterpret_cast<const size_t*>(&f) + OFFSET;
}}

{ty}& operator*() {{
return *reinterpret_cast< {ty}*>(data);
}}
Expand All @@ -1777,6 +1798,16 @@ namespace rust {
data = reinterpret_cast<size_t>(__zngur_internal_data_ptr(t));
}}

template<size_t OFFSET>
RefMut(const FieldOwned< {ty}, OFFSET >& f) {{
data = reinterpret_cast<size_t>(&f) + OFFSET;
}}

template<size_t OFFSET>
RefMut(const FieldRefMut< {ty}, OFFSET >& f) {{
data = *reinterpret_cast<const size_t*>(&f) + OFFSET;
}}

{ty}& operator*() {{
return *reinterpret_cast< {ty}*>(data);
}}
Expand Down
Loading