flapigen-rs icon indicating copy to clipboard operation
flapigen-rs copied to clipboard

error: Do not know conversation from Java type to such rust type 'Vec < String >'

Open archseer opened this issue 6 years ago • 7 comments

Similar to #270, I'm not able to use Vec<String> or Vec<&str> as an input parameter.

I tried declaring something like:

mod swig_foreign_types_map {}

foreign_typemap!(
    ($p:r_type) Vec<String> => jobjectArray {
        $out = jobject_array_to_vec_of_objects(env, $p);
    };
);

but that didn't seem to work.

archseer avatar Oct 04 '19 06:10 archseer

I think if String implemented SwigForeignClass, the default implementation would just work?

archseer avatar Oct 04 '19 06:10 archseer

Something like this seems to work, although untested:

mod swig_foreign_types_map {}

fn jstring_array_to_vec_of_strings(env: *mut JNIEnv, arr: jobjectArray) -> Vec<String> {
    let length = unsafe { (**env).GetArrayLength.unwrap()(env, arr) };
    let len = <usize as ::std::convert::TryFrom<jsize>>::try_from(length)
        .expect("invalid jsize, in jsize => usize conversation");
    let mut result = Vec::with_capacity(len);
    for i in 0..length {
        let native: String = unsafe {
            let obj: jstring = (**env).GetObjectArrayElement.unwrap()(env, arr, i);
            if (**env).ExceptionCheck.unwrap()(env) != 0 {
                panic!("Failed to retrieve element {} from this `jobjectArray'", i);
            }
            let jstr = JavaString::new(env, obj);
            jstr.to_str().to_string()
        };
        result.push(native);
    }

    result
}

#[swig_from_foreigner_hint = "java.lang.String []"]
impl SwigFrom<jobjectArray> for Vec<String> {
    fn swig_from(x: jobjectArray, env: *mut JNIEnv) -> Self {
        jstring_array_to_vec_of_strings(env, x)
    }
}

archseer avatar Oct 04 '19 07:10 archseer

I think if String implemented SwigForeignClass, the default implementation would just work?

Yes, but then this code

foreign_class(class Foo {
   fn f() -> String;
});

would be converted to Java code that return custom String from user's package, not java.lang.String.

Though you can define in your code:

type VecOfStrings = Vec<String>;

foreigner_class!(class VecOfStrings {
    self_type VecOfStrings;
    constructor create() -> VecOfStrings {
        Vec::new()
    }
    fn VecOfStrings::len(&self) -> usize;
    fn at(&self, i: usize) -> QRustStrView {
        &this[i]
    }
    fn push_back(&mut self, s: QString) {
        this.push(s);
    }
});

Code above is part of my code to use Rust from Qt/C++, but I suppose you can get the idea how it can be used in Java case.

Dushistov avatar Oct 04 '19 10:10 Dushistov

@Dushistov Would you be willing to accept the code I provided in the followup comment here https://github.com/Dushistov/rust_swig/issues/289#issuecomment-538271349 as a pull request?

After some testing, it seems to work fine locally and it allows accepting Vec<String> as an input parameter without having to wrap it.

archseer avatar Oct 09 '19 08:10 archseer

@archseer

Would you be willing to accept the code I provided in the followup comment

Not sure. At first java.lang.String (which uses utf-16 internally) converted by JVM to utf-8 via memory allocation, then result is not used "as is" instead we make additional n+1 allocations to convert it into Vec<String>.

It may be suitable if your API really use Vec<String> as input parameter, but if it can accept something like Iterator<Item = &str> it can be done without additional memory allocations.

So I am not sure yet about what type mapping related to String [] to use in rust_swig and which one is up to users of rust_swig to use in their custom type mappings via merge_type_map

Dushistov avatar Oct 09 '19 10:10 Dushistov

So I am not sure yet about what type mapping related to String [] to use in rust_swig and which one is up to users of rust_swig to use in their custom type mappings via merge_type_map

if blow feature to be support, maybe this solution more smart (lazy memory allocation):

foreign_callback!(callback BuildList<String>{
    self_type Iterator<Item = String>;
    onNext = Iterator<Item = String >::next(&mut self) -> Option<Self::Item>;
});

And java part like below:


// gen by rust_swig
interface BuildList<T> {
    Optional<T> onNext();
}

class BuildListImpl<T> implement BuildList<T> {
    List<T> list = new LinkList();

    @override
    Optional<T> onNext() {
         // TODO: list.getNext();
    }
}

class Demo {
    
      native consumeList(BuildList<String> list);
}

rust part:

struct Demo;

impl Demo {
      consumeList(list: Box<Iterator<Item = String>>) {
           list.iter().map(….) …….
      }
}

foreign_class!(class Demo {
    self_type Demo;
    fn Demo::consumeList(list: Box<Iterator<Item = String>>);
});

Guang1234567 avatar Dec 06 '19 18:12 Guang1234567

It may be suitable if your API really use Vec<String> as input parameter, but if it can accept something like Iterator<Item = &str> it can be done without additional memory allocations.

So I am not sure yet about what type mapping related to String [] to use in rust_swig and which one is up to users of rust_swig to use in their custom type mappings via merge_type_map

I've been trying to figure out but can't. How can I accept a string (or object for that matter) iterator in my Rust API?

Thanks!

tasn avatar Oct 15 '20 10:10 tasn