1
extern crate proc_macro;
2
use proc_macro::Delimiter;
3
use proc_macro::TokenStream;
4
use proc_macro::TokenTree::Group;
5
use proc_macro::TokenTree::Ident;
6
use proc_macro::TokenTree::Punct;
8
/// Macro for generating the C API stubs for normal functions
9
#[proc_macro_attribute]
10
pub fn cl_entrypoint(_attr: TokenStream, item: TokenStream) -> TokenStream {
13
let mut ret_type = None;
15
let mut iter = item.clone().into_iter();
16
while let Some(item) = iter.next() {
18
Ident(ident) => match ident.to_string().as_str() {
19
// extract the function name
20
"fn" => name = Some(iter.next().unwrap().to_string()),
26
let mut ret_type_tmp = String::new();
28
for ident in iter.by_ref() {
29
if ident.to_string() == ">" {
33
if ret_type_tmp.ends_with("mut") || ret_type_tmp.ends_with("const") {
34
ret_type_tmp.push(' ');
37
ret_type_tmp.push_str(ident.to_string().as_str());
40
ret_type = Some(ret_type_tmp);
49
if group.delimiter() != Delimiter::Parenthesis {
53
// the first group are our function args :)
54
args = Some(group.stream());
60
let name = name.as_ref().expect("no name found!");
61
let args = args.as_ref().expect("no args found!");
62
let ret_type = ret_type.as_ref().expect("no ret_type found!");
64
let mut arg_names = Vec::new();
65
let mut collect = true;
67
// extract the variable names of our function arguments
68
for item in args.clone() {
72
arg_names.push(ident);
76
// we ignore everything between a `:` and a `,` as those are the argument types
77
Punct(punct) => match punct.as_char() {
78
':' => collect = false,
79
',' => collect = true,
87
// convert to string and strip `mut` specifiers
88
let arg_names: Vec<_> = arg_names
91
.map(|ident| ident.to_string())
92
.filter(|ident| ident != "mut")
95
let arg_names_str = arg_names.join(",");
96
let mut args = args.to_string();
97
if !args.ends_with(',') {
101
// depending on the return type we have to generate a different match case
102
let mut res: TokenStream = if ret_type == "()" {
103
// trivial case: return the `Err(err)` as is
105
"pub extern \"C\" fn cl_{name}(
108
match {name}({arg_names_str}) {{
109
Ok(_) => CL_SUCCESS as cl_int,
115
// here we write the error code into the last argument, which we also add. All OpenCL APIs
116
// which return an object do have the `errcode_ret: *mut cl_int` argument last, so we can
117
// just make use of this here.
119
"pub extern \"C\" fn cl_{name}(
121
errcode_ret: *mut cl_int,
123
let (ptr, err) = match {name}({arg_names_str}) {{
124
Ok(o) => (o, CL_SUCCESS as cl_int),
125
Err(e) => (std::ptr::null_mut(), e),
127
if !errcode_ret.is_null() {{
143
/// Special macro for generating C function stubs to call into our `CLInfo` trait
144
#[proc_macro_attribute]
145
pub fn cl_info_entrypoint(attr: TokenStream, item: TokenStream) -> TokenStream {
147
let mut args = Vec::new();
148
let mut iter = item.clone().into_iter();
150
let mut collect = false;
152
// we have to extract the type name we implement the trait for and the type of the input
153
// parameters. The input Parameters are defined as `T` inside `CLInfo<T>` or `CLInfoObj<T, ..>`
154
while let Some(item) = iter.next() {
159
} else if ident.to_string() == "for" {
160
name = Some(iter.next().unwrap().to_string());
163
Punct(punct) => match punct.as_char() {
164
'<' => collect = true,
165
'>' => collect = false,
172
let name = name.as_ref().expect("no name found!");
173
assert!(!args.is_empty());
175
// the 1st argument is special as it's the actual property being queried. The remaining
176
// arguments are additional input data being passed before the property.
178
let (args_values, args) = args[1..]
181
.map(|(idx, arg)| (format!("arg{idx},"), format!("arg{idx}: {arg},")))
182
.reduce(|(a1, b1), (a2, b2)| (a1 + &a2, b1 + &b2))
183
.unwrap_or_default();
185
// depending on the amount of arguments we have a different trait implementation
186
let method = if args.len() > 1 {
192
let mut res: TokenStream = format!(
193
"pub extern \"C\" fn {attr}(
197
param_value_size: usize,
198
param_value: *mut ::std::ffi::c_void,
199
param_value_size_ret: *mut usize,
201
match input.{method}(
206
param_value_size_ret,
208
Ok(_) => CL_SUCCESS as cl_int,