Skip to content

Commit 0057aea

Browse files
authored
opaque structs (#44)
1 parent e665a95 commit 0057aea

File tree

10 files changed

+182
-13
lines changed

10 files changed

+182
-13
lines changed

generators/c-oo-bindgen/src/lib.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,15 @@ fn write_struct_definition(
299299
handle: &NativeStructHandle,
300300
lib: &Library,
301301
) -> FormattingResult<()> {
302-
doxygen(f, |f| doxygen_print(f, &handle.doc, lib))?;
302+
let doc = match handle.struct_type {
303+
NativeStructType::Public => handle.doc.clone(),
304+
NativeStructType::Opaque => handle
305+
.doc
306+
.clone()
307+
.warning("This struct should never be initialized or modified by user code"),
308+
};
309+
310+
doxygen(f, |f| doxygen_print(f, &doc, lib))?;
303311

304312
// Write the struct definition
305313
f.writeln(&format!("typedef struct {}", handle.to_c_type()))?;
@@ -355,7 +363,19 @@ fn write_struct_definition(
355363
})?;
356364
f.writeln(&format!("}} {};", handle.to_c_type()))?;
357365

358-
// Write the struct initializer
366+
// user should never try to initialize opaque structs, so don't suggest this is OK
367+
if handle.struct_type != NativeStructType::Opaque {
368+
f.newline()?;
369+
write_struct_initializer(f, handle)?;
370+
}
371+
372+
Ok(())
373+
}
374+
375+
fn write_struct_initializer(
376+
f: &mut dyn Printer,
377+
handle: &NativeStructHandle,
378+
) -> FormattingResult<()> {
359379
let params = handle
360380
.elements()
361381
.filter(|el| !el.element_type.has_default())
@@ -369,8 +389,6 @@ fn write_struct_definition(
369389
.collect::<Vec<String>>()
370390
.join(", ");
371391

372-
f.newline()?;
373-
374392
f.writeln(&format!(
375393
"static {} {}_init({})",
376394
handle.to_c_type(),
@@ -470,9 +488,7 @@ fn write_struct_definition(
470488
Ok(())
471489
})?;
472490
f.writeln("};")
473-
})?;
474-
475-
Ok(())
491+
})
476492
}
477493

478494
fn write_enum_definition(

generators/dotnet-oo-bindgen/src/structure.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@ use crate::*;
22
use heck::{CamelCase, MixedCase};
33
use oo_bindgen::native_struct::*;
44

5+
fn field_visibility(struct_type: NativeStructType) -> &'static str {
6+
match struct_type {
7+
NativeStructType::Opaque => "internal",
8+
NativeStructType::Public => "public",
9+
}
10+
}
11+
12+
fn constructor_visibility(struct_type: NativeStructType) -> &'static str {
13+
match struct_type {
14+
NativeStructType::Opaque => "internal",
15+
NativeStructType::Public => "public",
16+
}
17+
}
18+
519
pub(crate) fn generate(
620
f: &mut impl Printer,
721
native_struct: &StructHandle,
@@ -14,10 +28,18 @@ pub(crate) fn generate(
1428
print_imports(f)?;
1529
f.newline()?;
1630

31+
let doc = match native_struct.definition.struct_type {
32+
NativeStructType::Public => native_struct.doc().clone(),
33+
NativeStructType::Opaque => native_struct
34+
.doc()
35+
.clone()
36+
.warning("This class is an opaque handle and cannot be constructed by user code"),
37+
};
38+
1739
namespaced(f, &lib.name, |f| {
1840
documentation(f, |f| {
1941
// Print top-level documentation
20-
xmldoc_print(f, &native_struct.doc(), lib)
42+
xmldoc_print(f, &doc, lib)
2143
})?;
2244

2345
f.writeln(&format!("public class {}", struct_name))?;
@@ -73,7 +95,8 @@ pub(crate) fn generate(
7395
})?;
7496

7597
f.writeln(&format!(
76-
"public {} {}",
98+
"{} {} {}",
99+
field_visibility(native_struct.definition.struct_type),
77100
el.element_type.to_type().as_dotnet_type(),
78101
el.name.to_camel_case()
79102
))?;
@@ -188,7 +211,11 @@ pub(crate) fn generate(
188211

189212
// Write constructor
190213
if !native_struct.definition().is_default_constructed() {
191-
f.writeln(&format!("public {}(", struct_name))?;
214+
f.writeln(&format!(
215+
"{} {}(",
216+
constructor_visibility(native_struct.definition.struct_type),
217+
struct_name
218+
))?;
192219
f.write(
193220
&native_struct
194221
.elements()

generators/java-oo-bindgen/src/java/structure.rs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,37 @@ use super::*;
33
use heck::{CamelCase, MixedCase, ShoutySnakeCase};
44
use oo_bindgen::native_struct::*;
55

6+
fn constructor_visibility(struct_type: NativeStructType) -> &'static str {
7+
match struct_type {
8+
NativeStructType::Public => "public",
9+
NativeStructType::Opaque => "private",
10+
}
11+
}
12+
13+
fn field_visibility(struct_type: NativeStructType) -> &'static str {
14+
match struct_type {
15+
NativeStructType::Public => "public",
16+
NativeStructType::Opaque => "private final",
17+
}
18+
}
19+
620
pub(crate) fn generate(
721
f: &mut impl Printer,
822
native_struct: &StructHandle,
923
lib: &Library,
1024
) -> FormattingResult<()> {
1125
let struct_name = native_struct.name().to_camel_case();
1226

27+
let doc = match native_struct.definition.struct_type {
28+
NativeStructType::Public => native_struct.doc().clone(),
29+
NativeStructType::Opaque => native_struct
30+
.doc()
31+
.clone()
32+
.warning("This class is an opaque handle and cannot be constructed by user code"),
33+
};
34+
1335
// Documentation
14-
documentation(f, |f| javadoc_print(f, &native_struct.doc(), lib))?;
36+
documentation(f, |f| javadoc_print(f, &doc, lib))?;
1537

1638
// Structure definition
1739
f.writeln(&format!("public class {}", struct_name))?;
@@ -63,7 +85,8 @@ pub(crate) fn generate(
6385
})?;
6486

6587
f.writeln(&format!(
66-
"public {} {}",
88+
"{} {} {}",
89+
field_visibility(native_struct.definition.struct_type),
6790
el.element_type.to_type().as_java_primitive(),
6891
el.name.to_mixed_case()
6992
))?;
@@ -177,7 +200,11 @@ pub(crate) fn generate(
177200
f.newline()?;
178201

179202
if !native_struct.definition().is_default_constructed() {
180-
f.writeln(&format!("public {}(", struct_name,))?;
203+
f.writeln(&format!(
204+
"{} {}(",
205+
constructor_visibility(native_struct.definition.struct_type),
206+
struct_name,
207+
))?;
181208
f.write(
182209
&native_struct
183210
.elements()

oo-bindgen/src/native_struct.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,15 @@ impl From<Type> for StructElementType {
142142
}
143143
}
144144

145+
/// struct type affects the type of code the backend will generate
146+
#[derive(Copy, Clone, Debug, PartialEq)]
147+
pub enum NativeStructType {
148+
/// struct members are public
149+
Public,
150+
/// struct members are private (except C of course), and the struct is just an opaque "token"
151+
Opaque,
152+
}
153+
145154
#[derive(Debug)]
146155
pub struct NativeStructElement {
147156
pub name: String,
@@ -152,6 +161,7 @@ pub struct NativeStructElement {
152161
/// C-style structure definition
153162
#[derive(Debug)]
154163
pub struct NativeStruct {
164+
pub struct_type: NativeStructType,
155165
pub declaration: NativeStructDeclarationHandle,
156166
pub elements: Vec<NativeStructElement>,
157167
pub doc: Doc,
@@ -179,6 +189,7 @@ pub type NativeStructHandle = Handle<NativeStruct>;
179189

180190
pub struct NativeStructBuilder<'a> {
181191
lib: &'a mut LibraryBuilder,
192+
struct_type: NativeStructType,
182193
declaration: NativeStructDeclarationHandle,
183194
elements: Vec<NativeStructElement>,
184195
element_names_set: HashSet<String>,
@@ -192,13 +203,19 @@ impl<'a> NativeStructBuilder<'a> {
192203
) -> Self {
193204
Self {
194205
lib,
206+
struct_type: NativeStructType::Public, // defaults to a public struct
195207
declaration,
196208
elements: Vec::new(),
197209
element_names_set: HashSet::new(),
198210
doc: None,
199211
}
200212
}
201213

214+
pub fn make_opaque(mut self) -> Self {
215+
self.struct_type = NativeStructType::Opaque;
216+
self
217+
}
218+
202219
pub fn add<S: Into<String>, T: Into<StructElementType>, D: Into<Doc>>(
203220
mut self,
204221
name: S,
@@ -250,6 +267,7 @@ impl<'a> NativeStructBuilder<'a> {
250267
};
251268

252269
let handle = NativeStructHandle::new(NativeStruct {
270+
struct_type: self.struct_type,
253271
declaration: self.declaration.clone(),
254272
elements: self.elements,
255273
doc,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
using Xunit;
3+
using foo;
4+
5+
namespace foo.Tests
6+
{
7+
8+
public class OpaqueStructureTest
9+
{
10+
[Fact]
11+
public void StructureByValueEchoTest()
12+
{
13+
Assert.Equal(42ul, OpaqueStruct.CreateMagicValue().GetId());
14+
}
15+
16+
}
17+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.stepfunc.foo_test;
2+
3+
import io.stepfunc.foo.OpaqueStruct;
4+
import org.junit.jupiter.api.Assertions;
5+
import org.junit.jupiter.api.Test;
6+
import static org.joou.Unsigned.*;
7+
8+
class OpaqueStructTest {
9+
@Test
10+
void OpaqueStructureCanRoundTripValues() {
11+
Assertions.assertEquals(ulong(42), OpaqueStruct.createMagicValue().getId());
12+
}
13+
}

tests/foo-ffi/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod enums;
88
mod integer;
99
mod iterator;
1010
mod lifetime;
11+
mod opaque_struct;
1112
mod strings;
1213
mod structure;
1314

@@ -19,6 +20,7 @@ pub use enums::*;
1920
pub use integer::*;
2021
pub use iterator::*;
2122
pub use lifetime::*;
23+
pub(crate) use opaque_struct::*;
2224
pub use strings::*;
2325
pub(crate) use structure::*;
2426

tests/foo-ffi/src/opaque_struct.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
pub(crate) unsafe fn opaque_struct_get_id(value: Option<&crate::ffi::OpaqueStruct>) -> u64 {
2+
value.unwrap().id
3+
}
4+
5+
pub(crate) fn opaque_struct_magic_init() -> crate::ffi::OpaqueStruct {
6+
crate::ffi::OpaqueStruct { id: 42 }
7+
}

tests/foo-schema/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod enums;
99
mod integer;
1010
mod iterator;
1111
mod lifetime;
12+
mod opaque_struct;
1213
mod strings;
1314
mod structure;
1415

@@ -36,6 +37,7 @@ pub fn build_lib() -> Result<Library, BindingError> {
3637
enums::define(&mut builder)?;
3738
integer::define(&mut builder)?;
3839
iterator::define(&mut builder)?;
40+
opaque_struct::define(&mut builder)?;
3941
strings::define(&mut builder)?;
4042
structure::define(&mut builder)?;
4143
lifetime::define(&mut builder)?;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use oo_bindgen::native_function::{ReturnType, Type};
2+
use oo_bindgen::*;
3+
4+
pub fn define(lib: &mut LibraryBuilder) -> Result<(), BindingError> {
5+
let opaque_struct = lib.declare_native_struct("OpaqueStruct")?;
6+
7+
let opaque_struct = lib
8+
.define_native_struct(&opaque_struct)?
9+
.make_opaque()
10+
.add("id", Type::Uint64, "64-bit id")?
11+
.doc("Opaque structure")?
12+
.build()?;
13+
14+
let get_id_fn = lib
15+
.declare_native_function("opaque_struct_get_id")?
16+
.param(
17+
"value",
18+
Type::StructRef(opaque_struct.declaration.clone()),
19+
"struct value",
20+
)?
21+
.return_type(ReturnType::Type(Type::Uint64, "value of id field".into()))?
22+
.doc("Get the id field of the struct")?
23+
.build()?;
24+
25+
let opaque_struct_magic_init_fn = lib
26+
.declare_native_function("opaque_struct_magic_init")?
27+
.return_type(ReturnType::Type(
28+
Type::Struct(opaque_struct.clone()),
29+
"initialized value".into(),
30+
))?
31+
.doc("Create an OpaqueStruct initialized with a magic id")?
32+
.build()?;
33+
34+
lib.define_struct(&opaque_struct)?
35+
.method("GetId", &get_id_fn)?
36+
.static_method("CreateMagicValue", &opaque_struct_magic_init_fn)?
37+
.build();
38+
39+
Ok(())
40+
}

0 commit comments

Comments
 (0)