Skip to content

Commit 2ade29a

Browse files
authored
Add generic pointer implementation for VaArg and VaArgGet (#82)
This allows passing pointers to user defined structs and to non-primitive objects to variadic functions.
1 parent bb40ec3 commit 2ade29a

4 files changed

Lines changed: 346 additions & 23 deletions

File tree

libcc2rs/src/va_args.rs

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,23 @@ macro_rules! impl_va_arg_from {
2727
fn from(v: $ty) -> Self { VaArg::$variant(v as $cast) }
2828
}
2929
)*};
30-
(ptr: $($ty:ty),*) => {$(
31-
impl From<*mut $ty> for VaArg {
32-
fn from(v: *mut $ty) -> Self { VaArg::RawPtr(v as *mut c_void) }
33-
}
34-
impl From<*const $ty> for VaArg {
35-
fn from(v: *const $ty) -> Self { VaArg::RawPtr(v as *mut c_void) }
36-
}
37-
)*};
3830
}
3931

4032
impl_va_arg_from!(direct: i32 => Int, u32 => UInt, i64 => Long, u64 => ULong, f64 => Double, AnyPtr => Ptr);
4133
impl_va_arg_from!(promote: i8 => Int as i32, i16 => Int as i32, u8 => UInt as u32, u16 => UInt as u32, f32 => Double as f64);
42-
impl_va_arg_from!(ptr: c_void, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize);
4334

44-
impl<T: Clone + crate::reinterpret::ByteRepr + 'static> From<crate::rc::Ptr<T>> for VaArg {
35+
impl<T> From<*mut T> for VaArg {
36+
fn from(v: *mut T) -> Self {
37+
VaArg::RawPtr(v as *mut c_void)
38+
}
39+
}
40+
impl<T> From<*const T> for VaArg {
41+
fn from(v: *const T) -> Self {
42+
VaArg::RawPtr(v as *mut c_void)
43+
}
44+
}
45+
46+
impl<T: crate::reinterpret::ByteRepr + 'static> From<crate::rc::Ptr<T>> for VaArg {
4547
fn from(v: crate::rc::Ptr<T>) -> Self {
4648
VaArg::Ptr(v.to_any())
4749
}
@@ -95,23 +97,27 @@ macro_rules! impl_va_arg_get {
9597
}
9698
}
9799
)*};
98-
(ptr: $($ty:ty),*) => {$(
99-
impl VaArgGet for *mut $ty {
100-
fn get(v: &VaArg) -> Self {
101-
match v { VaArg::RawPtr(p) => *p as Self, _ => panic!("VaArgGet: expected pointer") }
102-
}
103-
}
104-
impl VaArgGet for *const $ty {
105-
fn get(v: &VaArg) -> Self {
106-
match v { VaArg::RawPtr(p) => *p as Self, _ => panic!("VaArgGet: expected pointer") }
107-
}
108-
}
109-
)*};
110100
}
111101

112102
impl_va_arg_get!(int: i8, i16, i32, i64, u8, u16, u32, u64);
113103
impl_va_arg_get!(float: f32, f64);
114-
impl_va_arg_get!(ptr: c_void, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize);
104+
105+
impl<T> VaArgGet for *mut T {
106+
fn get(v: &VaArg) -> Self {
107+
match v {
108+
VaArg::RawPtr(p) => *p as Self,
109+
_ => panic!("VaArgGet: expected pointer"),
110+
}
111+
}
112+
}
113+
impl<T> VaArgGet for *const T {
114+
fn get(v: &VaArg) -> Self {
115+
match v {
116+
VaArg::RawPtr(p) => *p as Self,
117+
_ => panic!("VaArgGet: expected pointer"),
118+
}
119+
}
120+
}
115121

116122
impl<T: 'static> VaArgGet for crate::rc::Ptr<T> {
117123
fn get(v: &VaArg) -> Self {
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
extern crate libcc2rs;
2+
use libcc2rs::*;
3+
use std::cell::RefCell;
4+
use std::collections::BTreeMap;
5+
use std::io::prelude::*;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::AsFd;
8+
use std::rc::{Rc, Weak};
9+
#[derive(Default)]
10+
pub struct node {
11+
pub data: Value<i32>,
12+
pub next: Value<Ptr<node>>,
13+
}
14+
impl ByteRepr for node {}
15+
#[derive(Clone, Copy, PartialEq, Debug, Default)]
16+
enum opt {
17+
#[default]
18+
OPT_STRING_OUT = 0,
19+
OPT_FILE = 1,
20+
OPT_NODE = 2,
21+
OPT_NODE_OUT = 3,
22+
}
23+
impl From<i32> for opt {
24+
fn from(n: i32) -> opt {
25+
match n {
26+
0 => opt::OPT_STRING_OUT,
27+
1 => opt::OPT_FILE,
28+
2 => opt::OPT_NODE,
29+
3 => opt::OPT_NODE_OUT,
30+
_ => panic!("invalid opt value: {}", n),
31+
}
32+
}
33+
}
34+
pub fn dispatch_0(option: i32, __args: &[VaArg]) -> i32 {
35+
let option: Value<i32> = Rc::new(RefCell::new(option));
36+
let ap: Value<VaList> = Rc::new(RefCell::new(VaList::default()));
37+
(*ap.borrow_mut()) = VaList::new(__args);
38+
let result: Value<i32> = Rc::new(RefCell::new(0));
39+
'switch: {
40+
let __match_cond = (*option.borrow());
41+
match __match_cond {
42+
v if v == (opt::OPT_STRING_OUT as i32) => {
43+
let out: Value<Ptr<Ptr<u8>>> = Rc::new(RefCell::new(
44+
((*ap.borrow_mut()).arg::<Ptr<Ptr<u8>>>()).clone(),
45+
));
46+
(*out.borrow()).write(Ptr::from_string_literal("hello"));
47+
(*result.borrow_mut()) = 1;
48+
break 'switch;
49+
}
50+
v if v == (opt::OPT_FILE as i32) => {
51+
let f: Value<Ptr<::std::fs::File>> = Rc::new(RefCell::new(
52+
((*ap.borrow_mut()).arg::<Ptr<::std::fs::File>>()).clone(),
53+
));
54+
(*result.borrow_mut()) = ((!((*f.borrow()).is_null())) as i32).clone();
55+
break 'switch;
56+
}
57+
v if v == (opt::OPT_NODE as i32) => {
58+
let n: Value<Ptr<node>> = Rc::new(RefCell::new(
59+
((*ap.borrow_mut()).arg::<Ptr<node>>()).clone(),
60+
));
61+
(*result.borrow_mut()) = (*(*(*n.borrow()).upgrade().deref()).data.borrow());
62+
break 'switch;
63+
}
64+
v if v == (opt::OPT_NODE_OUT as i32) => {
65+
let out: Value<Ptr<Ptr<node>>> = Rc::new(RefCell::new(
66+
((*ap.borrow_mut()).arg::<Ptr<Ptr<node>>>()).clone(),
67+
));
68+
(*out.borrow()).write(Ptr::<node>::null());
69+
(*result.borrow_mut()) = 2;
70+
break 'switch;
71+
}
72+
_ => {}
73+
}
74+
};
75+
return (*result.borrow());
76+
}
77+
pub fn main() {
78+
std::process::exit(main_0());
79+
}
80+
fn main_0() -> i32 {
81+
let s: Value<Ptr<u8>> = Rc::new(RefCell::new(Ptr::<u8>::null()));
82+
assert!(
83+
(((({
84+
let _option: i32 = (opt::OPT_STRING_OUT as i32);
85+
dispatch_0(_option, &[(s.as_pointer()).into()])
86+
}) == 1) as i32)
87+
!= 0)
88+
);
89+
assert!((((!((*s.borrow()).is_null())) as i32) != 0));
90+
assert!(
91+
(((({
92+
let _option: i32 = (opt::OPT_FILE as i32);
93+
dispatch_0(_option, &[libcc2rs::cout().into()])
94+
}) == 1) as i32)
95+
!= 0)
96+
);
97+
assert!(
98+
(((({
99+
let _option: i32 = (opt::OPT_FILE as i32);
100+
dispatch_0(
101+
_option,
102+
&[(AnyPtr::default())
103+
.cast::<::std::fs::File>()
104+
.expect("ub:wrong type")
105+
.into()],
106+
)
107+
}) == 0) as i32)
108+
!= 0)
109+
);
110+
let head: Value<node> = Rc::new(RefCell::new(node {
111+
data: Rc::new(RefCell::new(42)),
112+
next: Rc::new(RefCell::new(Ptr::<node>::null())),
113+
}));
114+
assert!(
115+
(((({
116+
let _option: i32 = (opt::OPT_NODE as i32);
117+
dispatch_0(_option, &[(head.as_pointer()).into()])
118+
}) == 42) as i32)
119+
!= 0)
120+
);
121+
let outp: Value<Ptr<node>> = Rc::new(RefCell::new((head.as_pointer())));
122+
assert!(
123+
(((({
124+
let _option: i32 = (opt::OPT_NODE_OUT as i32);
125+
dispatch_0(_option, &[(outp.as_pointer()).into()])
126+
}) == 2) as i32)
127+
!= 0)
128+
);
129+
assert!(((((*outp.borrow()).is_null()) as i32) != 0));
130+
return 0;
131+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
#[repr(C)]
10+
#[derive(Copy, Clone, Default)]
11+
pub struct node {
12+
pub data: i32,
13+
pub next: *mut node,
14+
}
15+
#[derive(Clone, Copy, PartialEq, Debug, Default)]
16+
enum opt {
17+
#[default]
18+
OPT_STRING_OUT = 0,
19+
OPT_FILE = 1,
20+
OPT_NODE = 2,
21+
OPT_NODE_OUT = 3,
22+
}
23+
impl From<i32> for opt {
24+
fn from(n: i32) -> opt {
25+
match n {
26+
0 => opt::OPT_STRING_OUT,
27+
1 => opt::OPT_FILE,
28+
2 => opt::OPT_NODE,
29+
3 => opt::OPT_NODE_OUT,
30+
_ => panic!("invalid opt value: {}", n),
31+
}
32+
}
33+
}
34+
pub unsafe fn dispatch_0(mut option: i32, __args: &[VaArg]) -> i32 {
35+
let mut ap: VaList = VaList::default();
36+
ap = VaList::new(__args);
37+
let mut result: i32 = 0;
38+
'switch: {
39+
let __match_cond = option;
40+
match __match_cond {
41+
v if v == (opt::OPT_STRING_OUT as i32) => {
42+
let mut out: *mut *const u8 = ap.arg::<*mut *const u8>();
43+
(*out) = b"hello\0".as_ptr().cast_mut().cast_const();
44+
result = 1;
45+
break 'switch;
46+
}
47+
v if v == (opt::OPT_FILE as i32) => {
48+
let mut f: *mut ::std::fs::File = ap.arg::<*mut ::std::fs::File>();
49+
result = ((!((f).is_null())) as i32).clone();
50+
break 'switch;
51+
}
52+
v if v == (opt::OPT_NODE as i32) => {
53+
let mut n: *mut node = ap.arg::<*mut node>();
54+
result = (*n).data;
55+
break 'switch;
56+
}
57+
v if v == (opt::OPT_NODE_OUT as i32) => {
58+
let mut out: *mut *mut node = ap.arg::<*mut *mut node>();
59+
(*out) = std::ptr::null_mut();
60+
result = 2;
61+
break 'switch;
62+
}
63+
_ => {}
64+
}
65+
};
66+
return result;
67+
}
68+
pub fn main() {
69+
unsafe {
70+
std::process::exit(main_0() as i32);
71+
}
72+
}
73+
unsafe fn main_0() -> i32 {
74+
let mut s: *const u8 = std::ptr::null();
75+
assert!(
76+
((((unsafe {
77+
let _option: i32 = (opt::OPT_STRING_OUT as i32);
78+
dispatch_0(_option, &[(&mut s as *mut *const u8).into()])
79+
}) == (1)) as i32)
80+
!= 0)
81+
);
82+
assert!((((!((s).is_null())) as i32) != 0));
83+
assert!(
84+
((((unsafe {
85+
let _option: i32 = (opt::OPT_FILE as i32);
86+
dispatch_0(_option, &[libcc2rs::cout_unsafe().into()])
87+
}) == (1)) as i32)
88+
!= 0)
89+
);
90+
assert!(
91+
((((unsafe {
92+
let _option: i32 = (opt::OPT_FILE as i32);
93+
dispatch_0(
94+
_option,
95+
&[((0 as *mut ::libc::c_void) as *mut ::std::fs::File).into()],
96+
)
97+
}) == (0)) as i32)
98+
!= 0)
99+
);
100+
let mut head: node = node {
101+
data: 42,
102+
next: std::ptr::null_mut(),
103+
};
104+
assert!(
105+
((((unsafe {
106+
let _option: i32 = (opt::OPT_NODE as i32);
107+
dispatch_0(_option, &[(&mut head as *mut node).into()])
108+
}) == (42)) as i32)
109+
!= 0)
110+
);
111+
let mut outp: *mut node = (&mut head as *mut node);
112+
assert!(
113+
((((unsafe {
114+
let _option: i32 = (opt::OPT_NODE_OUT as i32);
115+
dispatch_0(_option, &[(&mut outp as *mut *mut node).into()])
116+
}) == (2)) as i32)
117+
!= 0)
118+
);
119+
assert!(((((outp).is_null()) as i32) != 0));
120+
return 0;
121+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include <assert.h>
2+
#include <stdarg.h>
3+
#include <stdio.h>
4+
5+
struct node {
6+
int data;
7+
struct node *next;
8+
};
9+
10+
enum opt {
11+
OPT_STRING_OUT,
12+
OPT_FILE,
13+
OPT_NODE,
14+
OPT_NODE_OUT,
15+
};
16+
17+
int dispatch(int option, ...) {
18+
va_list ap;
19+
va_start(ap, option);
20+
int result = 0;
21+
switch (option) {
22+
case OPT_STRING_OUT: {
23+
const char **out = va_arg(ap, const char **);
24+
*out = "hello";
25+
result = 1;
26+
break;
27+
}
28+
case OPT_FILE: {
29+
FILE *f = va_arg(ap, FILE *);
30+
result = (f != NULL);
31+
break;
32+
}
33+
case OPT_NODE: {
34+
struct node *n = va_arg(ap, struct node *);
35+
result = n->data;
36+
break;
37+
}
38+
case OPT_NODE_OUT: {
39+
struct node **out = va_arg(ap, struct node **);
40+
*out = NULL;
41+
result = 2;
42+
break;
43+
}
44+
}
45+
va_end(ap);
46+
return result;
47+
}
48+
49+
int main() {
50+
const char *s = NULL;
51+
assert(dispatch(OPT_STRING_OUT, &s) == 1);
52+
assert(s != NULL);
53+
54+
assert(dispatch(OPT_FILE, stdout) == 1);
55+
assert(dispatch(OPT_FILE, (FILE *)NULL) == 0);
56+
57+
struct node head = {42, NULL};
58+
assert(dispatch(OPT_NODE, &head) == 42);
59+
60+
struct node *outp = &head;
61+
assert(dispatch(OPT_NODE_OUT, &outp) == 2);
62+
assert(outp == NULL);
63+
64+
return 0;
65+
}

0 commit comments

Comments
 (0)