Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
39 changes: 39 additions & 0 deletions book/src/call_rust_from_cpp/special_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,42 @@ tries to support them using C++ feature called User-defined Literals.
| `'a'_rs_b` | `b'a'` | `uint8_t` | Not Implemented | unconditionally |
| `"hello"_rs_b` | `b"hello"` | `rust::Ref<rust::Slice<uint8_t>>` | Not Implemented | `[u8]` |
| `"hello"_rs_c` | `c"hello"` | `rust::Ref<rust::ffi::CStr>` | Not Implemented | `::rust::ffi::CStr` |

## 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
- 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:74] sref.len() = 2
[main.cpp:77] pref.f0.read() = 42
[main.cpp:78] pref.f1.len() = 2
[main.cpp:81] pmut.f0.read() = 42
[main.cpp:83] pmut.f1.len() = 3
Test Field* underlying conversions -- finished

25 changes: 25 additions & 0 deletions examples/regression_test1/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,32 @@ 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; // Ref from FieldOwned
int32_t v0 = pair.f0; // value read via operator T()
zngur_dbg(v0);

// 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(pref.f0.read());
zngur_dbg(pref.f1.len());

rust::RefMut<rust::Tuple<int32_t, rust::std::string::String>> pmut = pair;
zngur_dbg(pmut.f0.read());
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();
}
8 changes: 8 additions & 0 deletions examples/regression_test1/main.zng
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ 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);

Expand Down
43 changes: 40 additions & 3 deletions zngur-generator/src/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1658,13 +1658,25 @@ namespace rust {
struct RefMut;

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

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

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

template<typename... T>
struct Tuple;
Expand Down Expand Up @@ -1759,6 +1771,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 +1804,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