From 594df9a66d6cb302a4d12007dde453ee5d2448b3 Mon Sep 17 00:00:00 2001 From: Davis King Date: Tue, 8 Aug 2017 15:00:38 -0400 Subject: [PATCH 1/3] Relaxes test to avoid false alarms. --- dlib/test/dnn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlib/test/dnn.cpp b/dlib/test/dnn.cpp index eca9249f7..b8f16c538 100644 --- a/dlib/test/dnn.cpp +++ b/dlib/test/dnn.cpp @@ -870,7 +870,7 @@ namespace conv2.get_gradient_for_filters(true, gi, data, filter_gradient2); dlog << LINFO << "filter gradient error: "<< max(abs(mat(filter_gradient1)-mat(filter_gradient2))); - DLIB_TEST_MSG(max(abs(mat(filter_gradient1)-mat(filter_gradient2))) < 1e-3, max(abs(mat(filter_gradient1)-mat(filter_gradient2)))); + DLIB_TEST_MSG(max(abs(mat(filter_gradient1)-mat(filter_gradient2))) < 2e-3, max(abs(mat(filter_gradient1)-mat(filter_gradient2)))); } } From fa5c666b6efc1f35ef5a8ca69868d6ebc2cc96f2 Mon Sep 17 00:00:00 2001 From: Davis King Date: Tue, 8 Aug 2017 15:10:17 -0400 Subject: [PATCH 2/3] Added an overload of mat() that takes a row stride value. --- dlib/matrix/matrix_blas_bindings.h | 4 ++-- dlib/matrix/matrix_mat.h | 33 ++++++++++++++++++++++++++++-- dlib/matrix/matrix_mat_abstract.h | 29 ++++++++++++++++++++++++++ dlib/test/matrix.cpp | 15 ++++++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) diff --git a/dlib/matrix/matrix_blas_bindings.h b/dlib/matrix/matrix_blas_bindings.h index b70a721d0..c405766fc 100644 --- a/dlib/matrix/matrix_blas_bindings.h +++ b/dlib/matrix/matrix_blas_bindings.h @@ -424,7 +424,7 @@ namespace dlib template int get_ld (const matrix_op >& m) { return m.nc(); } template - int get_ld (const matrix_op >& m) { return m.nc(); } + int get_ld (const matrix_op >& m) { return m.op.stride; } // -------- @@ -443,7 +443,7 @@ namespace dlib template int get_inc (const matrix_op >& ) { return 1; } template - int get_inc (const matrix_op >& ) { return 1; } + int get_inc (const matrix_op >& m) { return m.op.stride==m.op.cols ? 1 : 0; } template int get_inc (const matrix& ) { return 1; } diff --git a/dlib/matrix/matrix_mat.h b/dlib/matrix/matrix_mat.h index defc22644..803d7d999 100644 --- a/dlib/matrix/matrix_mat.h +++ b/dlib/matrix/matrix_mat.h @@ -319,11 +319,19 @@ namespace dlib const T* ptr_, const long nr_, const long nc_ - ) : ptr(ptr_), rows(nr_), cols(nc_){} + ) : ptr(ptr_), rows(nr_), cols(nc_), stride(nc_){} + + op_pointer_to_mat( + const T* ptr_, + const long nr_, + const long nc_, + const long stride_ + ) : ptr(ptr_), rows(nr_), cols(nc_), stride(stride_){} const T* ptr; const long rows; const long cols; + const long stride; const static long cost = 1; const static long NR = 0; @@ -333,7 +341,7 @@ namespace dlib typedef default_memory_manager mem_manager_type; typedef row_major_layout layout_type; - const_ret_type apply (long r, long c) const { return ptr[r*cols + c]; } + const_ret_type apply (long r, long c) const { return ptr[r*stride + c]; } long nr () const { return rows; } long nc () const { return cols; } @@ -419,6 +427,27 @@ namespace dlib return matrix_op(op(ptr,nr,nc)); } + template < + typename T + > + const matrix_op > mat ( + const T* ptr, + long nr, + long nc, + long stride + ) + { + DLIB_ASSERT(nr >= 0 && nc >= 0 && stride > 0 , + "\tconst matrix_exp mat(ptr, nr, nc, stride)" + << "\n\t nr and nc must be >= 0 while stride > 0" + << "\n\t nr: " << nr + << "\n\t nc: " << nc + << "\n\t stride: " << stride + ); + typedef op_pointer_to_mat op; + return matrix_op(op(ptr,nr,nc,stride)); + } + // ---------------------------------------------------------------------------------------- } diff --git a/dlib/matrix/matrix_mat_abstract.h b/dlib/matrix/matrix_mat_abstract.h index 8903f5c33..7026f60a1 100644 --- a/dlib/matrix/matrix_mat_abstract.h +++ b/dlib/matrix/matrix_mat_abstract.h @@ -153,6 +153,35 @@ namespace dlib the pointer and thus will not delete or free it. !*/ +// ---------------------------------------------------------------------------------------- + + template < + typename T + > + const matrix_exp mat ( + const T* ptr, + long nr, + long nc, + long stride + ); + /*! + requires + - nr >= 0 + - nc >= 0 + - stride > 0 + - ptr == a pointer to at least (nr-1)*stride+nc T objects (or the NULL pointer if nr*nc==0) + ensures + - returns a matrix M such that: + - M.nr() == nr + - m.nc() == nc + - for all valid r and c: + M(r,c) == ptr[r*stride + c] + (i.e. the pointer is interpreted as a matrix laid out in memory + in row major order, with a row stride of the given stride amount.) + - Note that the returned matrix doesn't take "ownership" of + the pointer and thus will not delete or free it. + !*/ + // ---------------------------------------------------------------------------------------- template < diff --git a/dlib/test/matrix.cpp b/dlib/test/matrix.cpp index 7d95a8006..8403c3579 100644 --- a/dlib/test/matrix.cpp +++ b/dlib/test/matrix.cpp @@ -1350,6 +1350,21 @@ namespace DLIB_TEST(mm(3) == 4); } + { + const long n = 5; + matrix m1, m2, m3, truth; + m1 = randm(n,n); + m2 = randm(n,n); + + rectangle rect1(1,1,3,3); + rectangle rect2(2,1,4,3); + + truth = subm(m1,rect1)*subm(m2,rect2); + m3 = mat(&m1(0,0)+6, 3,3, m1.nc()) * mat(&m2(0,0)+7, 3,3, m2.nc()); + + DLIB_TEST(max(abs(truth-m3)) < 1e-13); + } + { const long n = 5; matrix m1, m2, m3, truth; From cf24f0250791db065e6c030077567e562ecfb467 Mon Sep 17 00:00:00 2001 From: Davis King Date: Wed, 9 Aug 2017 12:19:41 -0400 Subject: [PATCH 3/3] Added an object that lets you hold a copyable reference to a java array. Also renamed the objects and generally improved documentation. --- dlib/java/cmake_swig_jni | 20 +- dlib/java/java_array.h | 601 +++++++++++++++++++++++++++++++++++++++ dlib/java/jvector.h | 250 ---------------- dlib/java/swig_api.h | 91 +++--- dlib/java/swig_test.java | 15 + 5 files changed, 693 insertions(+), 284 deletions(-) create mode 100644 dlib/java/java_array.h delete mode 100644 dlib/java/jvector.h diff --git a/dlib/java/cmake_swig_jni b/dlib/java/cmake_swig_jni index 461c49a64..bce1fdc58 100644 --- a/dlib/java/cmake_swig_jni +++ b/dlib/java/cmake_swig_jni @@ -119,8 +119,26 @@ FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/swig.i %module global %{ - #include \"swig_api.h\" #include + #include + static JavaVM *cached_jvm = 0; + + JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { + cached_jvm = jvm; + return JNI_VERSION_1_6; + } + + static JNIEnv * JNI_GetEnv() { + JNIEnv *env; + jint rc = cached_jvm->GetEnv((void **)&env, JNI_VERSION_1_6); + if (rc == JNI_EDETACHED) + throw std::runtime_error(\"current thread not attached\"); + if (rc == JNI_EVERSION) + throw std::runtime_error(\"jni version not supported\"); + return env; + } + + #include \"swig_api.h\" %} // Convert all C++ exceptions into java.lang.Exception diff --git a/dlib/java/java_array.h b/dlib/java/java_array.h new file mode 100644 index 000000000..46b13a2be --- /dev/null +++ b/dlib/java/java_array.h @@ -0,0 +1,601 @@ +// Copyright (C) 2017 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_SWIG_JAVA_ARRAY_H_ +#define DLIB_SWIG_JAVA_ARRAY_H_ + + +/* + + This file defines three special classes: array, array_view, and array_view_crit. An + array is a simple opaque handle to a java array, like a double[] array. The array_view + and array_view_crit objects allow you to access the contents of an array. The + interfaces of these objects is shown below, but for an example use, suppose you had an + array of int in java and you wanted to pass it to C++. You could create a C++ function + like this: + + void my_function(const array_view& array); + + and then within java you could call it with code like this: + + int[] array = new int[100]; + my_function(array); + + and it will work just like you would expect. The array_view will usually result in + the JVM doing a copy in the background. However, you can also declare your function + like this: + + void my_function(const array_view_crit& array); + + and still call it the same way in java, however, using array_view_crit will usually + not result in any copying, and is therefore very fast. array_view_crit uses the JNI + routine GetPrimitiveArrayCritical() to get a lock on the java memory underlying the + array. So it will probably prevent the garbage collector from running while your + function is executing. The JNI documentation is somewhat vague on the limitations of + GetPrimitiveArrayCritical(), saying only that you shouldn't hold the lock on the array + for "an extended period" or call back into the JVM. Deciding whether or not this + matters in your application is left as an exercise for the reader. + + + There are two ways you can declare your methods if they take an array_view or + array_view_crit. Taking a const reference or a non-const reference. E.g. + void my_function(const array_view& array); + void my_function(array_view& array); + You can't declare them to be by value. The non-const version allows you to modify the + contents of the array and the modifications will be visible to java, as you would + expect. You can also make functions that take array objects directly, but that's only + useful if you want to store the array handle somewhere, like in a member of a long + lived class. You can also write functions that return arrays back to java. E.g. + array make_an_array(size_t s) + { + array arr(s); + array_view aview(arr); + // Use aview to put data into the array and generally do something useful. + ... + return arr; + } + This would create an array and return it as a java int[] array. + + + You can also of course use functions taking many arguments, as is normally the case + with SWIG. Finally, these classes work with the following primitive types: + - int16_t + - int32_t + - int64_t + - char (corresponding to java byte) + - float + - double + + + + +namespace java +{ + template + class array + { + /!* + WHAT THIS OBJECT REPRESENTS + This is a handle to a java array. I.e. a reference to an array instance in + java like a double[] or int[]. It doesn't do anything other than tell you + the size of the array and allow you to hold a reference to it. + + To access the array contents, you need to create an array_view or + array_view_crit from the array. + *!/ + public: + array(); + /!* + ensures + - #size() == 0 + - this array is a null reference, i.e. it doesn't reference any array. + *!/ + + explicit array(size_t new_size); + /!* + ensures + - #size() == new_size + - Allocates a new java array. + - This array is a reference to the newly allocated java array object. + *!/ + + size_t size() const; + /!* + ensures + - returns the number of elements in this java array. + *!/ + + void swap(array& item); + /!* + ensures + - swaps the state of *this and item. + *!/ + + array(const array& item); + array& operator= (const array& item) + array(array&& item); + array& operator= (array&& item); + /!* + ensures + - The array is copyable, assignable, and movable. All copies will + reference the same underlying array. So the copies are shallow, as is + normally the case with java reference semantics. + *!/ + }; + + + + template + class array_view + { + /!* + WHAT THIS OBJECT REPRESENTS + This is a view into a java array object. It allows you to access the + values stored in an array and modify them if you want to. + + You should only create array_view objects locally in a function since an + array_view is only valid as long as the array it references exists. So + don't store array_view objects in the member area of a class or globally. + *!/ + + public: + array_view(); + /!* + ensures + - #size() == 0 + - #data() == nullptr + *!/ + + array_view(const array& arr, bool might_be_modified=true); + /!* + ensures + - #size() == arr.size() + - #data() == a pointer to the beginning of the array data referenced by arr. + - When you get a view on a java array, sometimes the JVM will actually + give you a pointer to a copy of the original array. You therefore have + to tell the JVM if you modified the array when you are done using it. If + you say you modified it then the JVM will perform another copy from your + memory buffer back into the JVM. The state of might_be_modified controls + if we do this. So if you are going to modify the array via this + array_view you should set might_be_modified==true. + *!/ + + size_t size() const; + /!* + ensures + - returns the number of elements in this java array. + *!/ + + T* data(); + const T* data() const; + /!* + ensures + - returns a pointer to the beginning of the array. Or nullptr if this is a + handle to null, rather than an actual array instance. + *!/ + + T* begin(); + T* end(); + const T* begin() const; + const T* end() const; + /!* + ensures + - returns iterators to the start and one-past-the-end of the array, as is + the convention for iterator ranges in C++. + *!/ + + T& operator[](size_t i); + const T& operator[](size_t i) const; + /!* + ensures + - returns data()[i] + *!/ + + private: + // this object is non-copyable. + array_view(const array_view&); + array_view& operator=(const array_view&); + }; + + + template + class array_view_crit + { + /!* + WHAT THIS OBJECT REPRESENTS + This is just like an array_view and has an identical interface. The only + difference is that we use the JNI call GetPrimitiveArrayCritical() to get a + critical lock on the array's memory. Therefore, using array_view_crit is + usually faster than array_view since it avoids any unnecessary copying back + and forth between the JVM. + + However, this critical lock can block the JVM's garbage collector from + running. So don't create long lived array_view_crit objects. + *!/ + }; + +} +*/ + + + + + + + + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// IMPLEMENTATION DETAILS +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + + + + + + + +namespace java +{ + +template +class array_view_base +{ +public: + array_view_base() = default; + + size_t size() const { return sz; } + T* data() { return pdata; } + const T* data() const { return pdata; } + + T* begin() { return pdata; } + T* end() { return pdata+sz; } + const T* begin() const { return pdata; } + const T* end() const { return pdata+sz; } + + T& operator[](size_t i) { return pdata[i]; } + const T& operator[](size_t i) const { return pdata[i]; } + +protected: + T* pdata = nullptr; + size_t sz = 0; + +private: + // this object is non-copyable + array_view_base(const array_view_base&); + array_view_base& operator=(const array_view_base&); + +}; + + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + +template +struct find_java_array_type; + +template <> struct find_java_array_type { typedef jshortArray type; }; +template <> struct find_java_array_type { typedef jintArray type; }; +template <> struct find_java_array_type { typedef jlongArray type; }; +template <> struct find_java_array_type { typedef jbyteArray type; }; +template <> struct find_java_array_type { typedef jfloatArray type; }; +template <> struct find_java_array_type { typedef jdoubleArray type; }; + +jshortArray create_java_array(int16_t, size_t size) { return JNI_GetEnv()->NewShortArray(size); } +jintArray create_java_array(int32_t, size_t size) { return JNI_GetEnv()->NewIntArray(size); } +jlongArray create_java_array(int64_t, size_t size) { return JNI_GetEnv()->NewLongArray(size); } +jbyteArray create_java_array(char, size_t size) { return JNI_GetEnv()->NewByteArray(size); } +jfloatArray create_java_array(float, size_t size) { return JNI_GetEnv()->NewFloatArray(size); } +jdoubleArray create_java_array(double , size_t size) { return JNI_GetEnv()->NewDoubleArray(size); } + +template +class array +{ +public: + + typedef typename find_java_array_type::type java_type; + + array() {} + + explicit array(size_t size) + { + ref = create_java_array(T(),size); + is_global_ref = false; + } + + array(java_type ref_) + { + if (ref_) + { + ref = (java_type)JNI_GetEnv()->NewGlobalRef(ref_); + is_global_ref = true; + } + } + +#ifndef SWIG + array(array&& item) + { + ref = item.ref; + is_global_ref = item.is_global_ref; + item.ref = NULL; + item.is_global_ref = false; + } + array& operator= (array&& item) + { + array(std::move(item)).swap(*this); + return *this; + } +#endif + + ~array() + { + if (ref) + { + // Don't delete the reference if it's a local reference, since the only reason + // we will normally be using array object's that contain local references + // is because we plan on returning the newly constructed array back to the JVM, + // which automatically frees local references using the normal JVM garbage + // collection scheme. + if (is_global_ref) + JNI_GetEnv()->DeleteGlobalRef(ref); + + ref = NULL; + is_global_ref = false; + } + } + + size_t size() const + { + if (ref) + return JNI_GetEnv()->GetArrayLength(ref); + else + return 0; + } + + array(const array& item) + { + array(item.ref).swap(*this); + } + + array& operator= (const array& item) + { + array(item).swap(*this); + return *this; + } + + operator java_type() const { return ref;} + + void swap(array& item) { std::swap(ref, item.ref); } + +private: + java_type ref = NULL; + bool is_global_ref = false; +}; + +#ifdef SWIG +// Tell SWIG to not use it's SwigValueWrapper stuff on array objects since they aren't +// needed and it causes superfluous construction and destruction of array objects. +%feature("novaluewrapper") array; +%template() array; +%feature("novaluewrapper") array; +%template() array; +%feature("novaluewrapper") array; +%template() array; +%feature("novaluewrapper") array; +%template() array; +%feature("novaluewrapper") array; +%template() array; +%feature("novaluewrapper") array; +%template() array; +#endif + +#ifdef SWIG +%define tostring(token) + #token +%enddef + +%define define_javaObjectRef_converion(type, java_type) + // Define array conversions for non-const arrays + %typemap(jtype) (array) "java_type[]" + %typemap(jstype) (array) "java_type[]" + %typemap(jni) (array) tostring(j##java_type##Array) + %typemap(javain) (array) "$javainput" + %typemap(in) (array) { $1 = array($input); } + %typemap(javaout) (array) {return $jnicall; } + %typemap(out) (array) {jresult = result;} + + %typemap(jtype) (array&) "java_type[]" + %typemap(jstype) (array&) "java_type[]" + %typemap(jni) (array&) tostring(j##java_type##Array) + %typemap(javain) (array&) "$javainput" + %typemap(arginit) (array&) { $1 = &temp$argnum; } + %typemap(in) (array&) (array temp) { *($1) = array($input); } + + %typemap(jtype) (const array&) "java_type[]" + %typemap(jstype) (const array&) "java_type[]" + %typemap(jni) (const array&) tostring(j##java_type##Array) + %typemap(javain) (const array&) "$javainput" + %typemap(arginit) (const array&) { $1 = &temp$argnum; } + %typemap(in) (const array&) (array temp) { *($1) = array($input); } +%enddef +define_javaObjectRef_converion(int16_t,short) +define_javaObjectRef_converion(int32_t,int) +define_javaObjectRef_converion(int64_t,long) +define_javaObjectRef_converion(char,byte) +define_javaObjectRef_converion(float,float) +define_javaObjectRef_converion(double,double) + +#endif +// ---------------------------------------------------------------------------------------- + +template class array_view; + +#define JAVA_ARRAY_CLASS_SPEC(ctype, type, Type) \ +template <> class array_view : public array_view_base \ +{ \ +public: \ + ~array_view() { clear(); } \ + array_view() {} \ + array_view(const array& arr, bool might_be_modified_=true){reset(JNI_GetEnv(),arr,might_be_modified_);} \ + void reset(JNIEnv* jenv_, j##type##Array arr, bool might_be_modified_) { \ + clear(); \ + jenv = jenv_; \ + oldArr = arr; \ + if (arr) { \ + pdata = (ctype*)jenv->Get##Type##ArrayElements(arr, 0); \ + sz = jenv->GetArrayLength(arr); \ + } \ + might_be_modified = might_be_modified_; \ + } \ +private: \ + void clear() { \ + if (pdata) { \ + jenv->Release##Type##ArrayElements(oldArr, (j##type*)pdata, might_be_modified?0:JNI_ABORT); \ + pdata = nullptr; \ + sz = 0; \ + } \ + } \ + JNIEnv* jenv = nullptr; \ + j##type##Array oldArr; \ + bool might_be_modified; \ +}; + +JAVA_ARRAY_CLASS_SPEC(int16_t,short, Short) +JAVA_ARRAY_CLASS_SPEC(int32_t,int, Int) +JAVA_ARRAY_CLASS_SPEC(int64_t,long, Long) +JAVA_ARRAY_CLASS_SPEC(char,byte, Byte) +JAVA_ARRAY_CLASS_SPEC(float,float, Float) +JAVA_ARRAY_CLASS_SPEC(double,double, Double) + + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + +template +class array_view_crit_base +{ +public: + array_view_crit_base() = default; + + size_t size() const { return sz; } + T* data() { return pdata; } + const T* data() const { return pdata; } + + T* begin() { return pdata; } + T* end() { return pdata+sz; } + const T* begin() const { return pdata; } + const T* end() const { return pdata+sz; } + T& operator[](size_t i) { return pdata[i]; } + const T& operator[](size_t i) const { return pdata[i]; } + + ~array_view_crit_base() { clear(); } + + void reset(JNIEnv* jenv_, JARR arr, bool might_be_modified_) + { + clear(); + jenv = jenv_; + oldArr = arr; + if (arr) + { + pdata = (T*)jenv->GetPrimitiveArrayCritical(arr, 0); + sz = jenv->GetArrayLength(arr); + } + might_be_modified = might_be_modified_; + } + +private: + + void clear() + { + if (pdata) { + jenv->ReleasePrimitiveArrayCritical(oldArr, pdata, might_be_modified?0:JNI_ABORT); + pdata = nullptr; + sz = 0; + } + } + + // this object is non-copyable + array_view_crit_base(const array_view_crit_base&); + array_view_crit_base& operator=(const array_view_crit_base&); + + T* pdata = nullptr; + size_t sz = 0; + JNIEnv* jenv = nullptr; + JARR oldArr; + bool might_be_modified; +}; + +template class array_view_crit; + +template <> class array_view_crit : public array_view_crit_base { public: array_view_crit(){} array_view_crit(const array& arr, bool might_be_modified_=true){reset(JNI_GetEnv(),arr,might_be_modified_);} }; +template <> class array_view_crit : public array_view_crit_base { public: array_view_crit(){} array_view_crit(const array& arr, bool might_be_modified_=true){reset(JNI_GetEnv(),arr,might_be_modified_);} }; +template <> class array_view_crit : public array_view_crit_base { public: array_view_crit(){} array_view_crit(const array& arr, bool might_be_modified_=true){reset(JNI_GetEnv(),arr,might_be_modified_);} }; +template <> class array_view_crit : public array_view_crit_base { public: array_view_crit(){} array_view_crit(const array& arr, bool might_be_modified_=true){reset(JNI_GetEnv(),arr,might_be_modified_);} }; +template <> class array_view_crit : public array_view_crit_base { public: array_view_crit(){} array_view_crit(const array& arr, bool might_be_modified_=true){reset(JNI_GetEnv(),arr,might_be_modified_);} }; +template <> class array_view_crit : public array_view_crit_base { public: array_view_crit(){} array_view_crit(const array& arr, bool might_be_modified_=true){reset(JNI_GetEnv(),arr,might_be_modified_);} }; + +// ---------------------------------------------------------------------------------------- + +// Define SWIG typemaps so SWIG will know what to do with the array_view and array_view_crit +// objects. +#ifdef SWIG +%define define_array_converion(type, java_type) + // Define array conversions for non-const arrays + %typemap(jtype) (array_view&) "java_type[]" + %typemap(jstype) (array_view&) "java_type[]" + %typemap(jni) (array_view&) tostring(j##java_type##Array) + %typemap(javain) (array_view&) "$javainput" + %typemap(arginit) (array_view&) { $1 = &temp$argnum; } + %typemap(in) (array_view&) (array_view temp) { $1->reset(jenv, $input, true); } + + %typemap(jtype) (const array_view&) "java_type[]" + %typemap(jstype) (const array_view&) "java_type[]" + %typemap(jni) (const array_view&) tostring(j##java_type##Array) + %typemap(javain) (const array_view&) "$javainput" + %typemap(arginit) (const array_view&) { $1 = &temp$argnum; } + %typemap(in) (const array_view&) (array_view temp) { $1->reset(jenv, $input, false); } +%enddef +define_array_converion(int16_t,short) +define_array_converion(int32_t,int) +define_array_converion(int64_t,long) +define_array_converion(char,byte) +define_array_converion(float,float) +define_array_converion(double,double) + + + +%define define_array_crit_converion(type, java_type) + // Define array conversions for non-const arrays + %typemap(jtype) (array_view_crit&) "java_type[]" + %typemap(jstype) (array_view_crit&) "java_type[]" + %typemap(jni) (array_view_crit&) tostring(j##java_type##Array) + %typemap(javain) (array_view_crit&) "$javainput" + %typemap(arginit) (array_view_crit&) { $1 = &temp$argnum; } + %typemap(in) (array_view_crit&) (array_view_crit temp) { $1->reset(jenv, $input, true); } + + %typemap(jtype) (const array_view_crit&) "java_type[]" + %typemap(jstype) (const array_view_crit&) "java_type[]" + %typemap(jni) (const array_view_crit&) tostring(j##java_type##Array) + %typemap(javain) (const array_view_crit&) "$javainput" + %typemap(arginit) (const array_view_crit&) { $1 = &temp$argnum; } + %typemap(in) (const array_view_crit&) (array_view_crit temp) { $1->reset(jenv, $input, false); } +%enddef +define_array_crit_converion(int16_t,short) +define_array_crit_converion(int32_t,int) +define_array_crit_converion(int64_t,long) +define_array_crit_converion(char,byte) +define_array_crit_converion(float,float) +define_array_crit_converion(double,double) + +#endif // SWIG + +} + +#endif // DLIB_SWIG_JAVA_ARRAY_H_ + diff --git a/dlib/java/jvector.h b/dlib/java/jvector.h deleted file mode 100644 index d0b01000f..000000000 --- a/dlib/java/jvector.h +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright (C) 2017 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_SWIG_JVECTOR_H_ -#define DLIB_SWIG_JVECTOR_H_ - - -/* - - This file defines two special classes, jvector and jvector_crit. Both of them have the - interface defined by jvector_base, that is, the interface of a simple array object like - std::vector (except without any ability to be resized). These classes are simple - interfaces to java native arrays. So for example, suppose you had an array of int in - java and you wanted to pass it to C++. You could create a C++ function like this: - - void my_function(const jvector& array); - - and then within java you could call it with code like this: - - int[] array = new int[100]; - my_function(array); - - and it will work just like you would expect. The jvector will usually result in - the JVM doing a copy in the background. However, you can also declare your function - like this: - - void my_function(const jvector_crit& array); - - and still call it the same way in java, however, using jvector_crit will usually - not result in any copying, and is therefore very fast. jvector_crit uses the JNI - routine GetPrimitiveArrayCritical() to get a lock on the java memory underlying the - array. So it will probably prevent the garbage collector from running while your - function is executing. The JNI documentation is somewhat vague on the limitations of - GetPrimitiveArrayCritical(), saying only that you shouldn't hold the lock on the array - for "an extended period" or call back into the JVM. Deciding whether or not this - matters in your application is left as an exercise for the reader. - - - There are two ways you can declare your methods. Taking a const reference or a - non-const reference. E.g.: - void my_function(const jvector& array); - void my_function(jvector& array); - The non-const version allows you to modify the contents of the array and the - modifications will be visible to java, as you would expect. - - You can also of course use functions taking many arguments, as is normally the case - with SWIG. Finally, jvector works with the following primitive types: - - int16_t - - int32_t - - int64_t - - char (corresponding to java byte) - - float - - double -*/ - - -// ---------------------------------------------------------------------------------------- - -template -class jvector_base -{ -public: - jvector_base() = default; - - size_t size() const { return sz; } - T* data() { return pdata; } - const T* data() const { return pdata; } - - T* begin() { return pdata; } - T* end() { return pdata+sz; } - const T* begin() const { return pdata; } - const T* end() const { return pdata+sz; } - - T& operator[](size_t i) { return pdata[i]; } - const T& operator[](size_t i) const { return pdata[i]; } - -protected: - T* pdata = nullptr; - size_t sz = 0; - -private: - // this object is non-copyable - jvector_base(const jvector_base&); - jvector_base& operator=(const jvector_base&); - -}; - - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - -template class jvector; - -#define JVECTOR_CLASS_SPEC(ctype, type, Type) \ -template <> class jvector : public jvector_base \ -{ \ -public: \ - ~jvector() { clear(); } \ - void reset(JNIEnv* jenv_, j##type##Array arr, bool mightBeModified_) { \ - clear(); \ - jenv = jenv_; \ - oldArr = arr; \ - pdata = (ctype*)jenv->Get##Type##ArrayElements(arr, 0); \ - sz = jenv->GetArrayLength(arr); \ - mightBeModified = mightBeModified_; \ - } \ -private: \ - void clear() { \ - if (pdata) { \ - jenv->Release##Type##ArrayElements(oldArr, (j##type*)pdata, mightBeModified?0:JNI_ABORT); \ - pdata = nullptr; \ - } \ - } \ - JNIEnv* jenv = nullptr; \ - j##type##Array oldArr; \ - bool mightBeModified; \ -}; - -JVECTOR_CLASS_SPEC(int16_t,short, Short) -JVECTOR_CLASS_SPEC(int32_t,int, Int) -JVECTOR_CLASS_SPEC(int64_t,long, Long) -JVECTOR_CLASS_SPEC(char,byte, Byte) -JVECTOR_CLASS_SPEC(float,float, Float) -JVECTOR_CLASS_SPEC(double,double, Double) - - -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- -// ---------------------------------------------------------------------------------------- - -template -class jvector_crit_base -{ -public: - jvector_crit_base() = default; - - size_t size() const { return sz; } - T* data() { return pdata; } - const T* data() const { return pdata; } - - T* begin() { return pdata; } - T* end() { return pdata+sz; } - const T* begin() const { return pdata; } - const T* end() const { return pdata+sz; } - T& operator[](size_t i) { return pdata[i]; } - const T& operator[](size_t i) const { return pdata[i]; } - - ~jvector_crit_base() { clear(); } - - void reset(JNIEnv* jenv_, JARR arr, bool mightBeModified_) - { - clear(); - jenv = jenv_; - oldArr = arr; - pdata = (T*)jenv->GetPrimitiveArrayCritical(arr, 0); - sz = jenv->GetArrayLength(arr); - mightBeModified = mightBeModified_; - } - -private: - - void clear() - { - if (pdata) { - jenv->ReleasePrimitiveArrayCritical(oldArr, pdata, mightBeModified?0:JNI_ABORT); - pdata = nullptr; - } - } - - // this object is non-copyable - jvector_crit_base(const jvector_crit_base&); - jvector_crit_base& operator=(const jvector_crit_base&); - - T* pdata = nullptr; - size_t sz = 0; - JNIEnv* jenv = nullptr; - JARR oldArr; - bool mightBeModified; -}; - -template class jvector_crit; - -template <> class jvector_crit : public jvector_crit_base {}; -template <> class jvector_crit : public jvector_crit_base {}; -template <> class jvector_crit : public jvector_crit_base {}; -template <> class jvector_crit : public jvector_crit_base {}; -template <> class jvector_crit : public jvector_crit_base {}; -template <> class jvector_crit : public jvector_crit_base {}; - -// ---------------------------------------------------------------------------------------- - -// Define SWIG typemaps so SWIG will know what to do with the jvector and jvector_crit -// objects. -#ifdef SWIG -%define tostring(token) - #token -%enddef - -%define define_jvector_converion(type, java_type) - // Define array conversions for non-const arrays - %typemap(jtype) (jvector&) "java_type[]" - %typemap(jstype) (jvector&) "java_type[]" - %typemap(jni) (jvector&) tostring(j##java_type##Array) - %typemap(javain) (jvector&) "$javainput" - %typemap(arginit) (jvector&) { $1 = &temp$argnum; } - %typemap(in) (jvector&) (jvector temp) { $1->reset(jenv, $input, true); } - - %typemap(jtype) (const jvector&) "java_type[]" - %typemap(jstype) (const jvector&) "java_type[]" - %typemap(jni) (const jvector&) tostring(j##java_type##Array) - %typemap(javain) (const jvector&) "$javainput" - %typemap(arginit) (const jvector&) { $1 = &temp$argnum; } - %typemap(in) (const jvector&) (jvector temp) { $1->reset(jenv, $input, false); } -%enddef -define_jvector_converion(int16_t,short) -define_jvector_converion(int32_t,int) -define_jvector_converion(int64_t,long) -define_jvector_converion(char,byte) -define_jvector_converion(float,float) -define_jvector_converion(double,double) - - - -%define define_jvector_crit_converion(type, java_type) - // Define array conversions for non-const arrays - %typemap(jtype) (jvector_crit&) "java_type[]" - %typemap(jstype) (jvector_crit&) "java_type[]" - %typemap(jni) (jvector_crit&) tostring(j##java_type##Array) - %typemap(javain) (jvector_crit&) "$javainput" - %typemap(arginit) (jvector_crit&) { $1 = &temp$argnum; } - %typemap(in) (jvector_crit&) (jvector_crit temp) { $1->reset(jenv, $input, true); } - - %typemap(jtype) (const jvector_crit&) "java_type[]" - %typemap(jstype) (const jvector_crit&) "java_type[]" - %typemap(jni) (const jvector_crit&) tostring(j##java_type##Array) - %typemap(javain) (const jvector_crit&) "$javainput" - %typemap(arginit) (const jvector_crit&) { $1 = &temp$argnum; } - %typemap(in) (const jvector_crit&) (jvector_crit temp) { $1->reset(jenv, $input, false); } -%enddef -define_jvector_crit_converion(int16_t,short) -define_jvector_crit_converion(int32_t,int) -define_jvector_crit_converion(int64_t,long) -define_jvector_crit_converion(char,byte) -define_jvector_crit_converion(float,float) -define_jvector_crit_converion(double,double) - -#endif // SWIG - -#endif // DLIB_SWIG_JVECTOR_H_ - diff --git a/dlib/java/swig_api.h b/dlib/java/swig_api.h index 2742b4d06..086989012 100644 --- a/dlib/java/swig_api.h +++ b/dlib/java/swig_api.h @@ -1,30 +1,32 @@ #ifndef EXAMPLE_SWIG_ApI_H_ #define EXAMPLE_SWIG_ApI_H_ -// This file is essentially a small unit test for the swig cmake scripts and the jvector +// This file is essentially a small unit test for the swig cmake scripts and the java array // classes. All it does it define a few simple functions for writing to and summing // arrays. The swig_test.java file then calls these C++ functions and checks if they work // correctly. -// Let's use the jvector, a tool for efficiently binding java native arrays to C++ function -// arguments. You do this by putting this pair of include statements in your swig_api.h -// file. Then after that you can use the jvector and jvector_crit classes. -#include "jvector.h" +// Let's use java_array.h, a tool for efficiently binding java native arrays to C++ +// function arguments. You do this by putting this pair of include statements in your +// swig_api.h file. Then after that you can use the java::array, java::array_view, and +// java::array_view_crit classes. +#include "java_array.h" #ifdef SWIG -%include "jvector.h" +%include "java_array.h" #endif -// ---------------------------------------------------------------------------------------- +using namespace java; + // SWIG can't expose templated functions to java. We declare these here as helper // functions to make the non-templated routines swig will expose easier to write. You can // see these java exposed methods below (i.e. sum(), sum_crit(), assign(), and // assign_crit()). template -T tsum(const jvector_crit& arr) +T tsum(const array_view_crit& arr) { T s = 0; for (auto& v : arr) @@ -32,7 +34,7 @@ T tsum(const jvector_crit& arr) return s; } template -T tsum(const jvector& arr) +T tsum(const array_view& arr) { T s = 0; for (auto& v : arr) @@ -56,41 +58,64 @@ void tassign(T& arr) // "global", which is where these sum and assign routines will appear. You can see // examples of java code that calls them in swig_test.java. -inline int sum_crit(const jvector_crit& arr) { return tsum(arr); } -inline int sum(const jvector& arr) { return tsum(arr); } -inline void assign_crit(jvector_crit& arr) { tassign(arr); } -inline void assign(jvector& arr) { tassign(arr); } +inline int sum_crit(const array_view_crit& arr) { return tsum(arr); } +inline int sum(const array_view& arr) { return tsum(arr); } +inline void assign_crit(array_view_crit& arr) { tassign(arr); } +inline void assign(array_view& arr) { tassign(arr); } -inline int sum_crit(const jvector_crit& arr) { return tsum(arr); } -inline int sum(const jvector& arr) { return tsum(arr); } -inline void assign_crit(jvector_crit& arr) { tassign(arr); } -inline void assign(jvector& arr) { tassign(arr); } +inline int sum_crit(const array_view_crit& arr) { return tsum(arr); } +inline int sum(const array_view& arr) { return tsum(arr); } +inline void assign_crit(array_view_crit& arr) { tassign(arr); } +inline void assign(array_view& arr) { tassign(arr); } -inline int sum_crit(const jvector_crit& arr) { return tsum(arr); } -inline int sum(const jvector& arr) { return tsum(arr); } -inline void assign_crit(jvector_crit& arr) { tassign(arr); } -inline void assign(jvector& arr) { tassign(arr); } +inline int sum_crit(const array_view_crit& arr) { return tsum(arr); } +inline int sum(const array_view& arr) { return tsum(arr); } +inline void assign_crit(array_view_crit& arr) { tassign(arr); } +inline void assign(array_view& arr) { tassign(arr); } -inline int sum_crit(const jvector_crit& arr) { return tsum(arr); } -inline int sum(const jvector& arr) { return tsum(arr); } -inline void assign_crit(jvector_crit& arr) { tassign(arr); } -inline void assign(jvector& arr) { tassign(arr); } +inline int sum_crit(const array_view_crit& arr) { return tsum(arr); } +inline int sum(const array_view& arr) { return tsum(arr); } +inline void assign_crit(array_view_crit& arr) { tassign(arr); } +inline void assign(array_view& arr) { tassign(arr); } -inline double sum_crit(const jvector_crit& arr) { return tsum(arr); } -inline double sum(const jvector& arr) { return tsum(arr); } -inline void assign_crit(jvector_crit& arr) { tassign(arr); } -inline void assign(jvector& arr) { tassign(arr); } +inline double sum_crit(const array_view_crit& arr) { return tsum(arr); } +inline double sum(const array_view& arr) { return tsum(arr); } +inline void assign_crit(array_view_crit& arr) { tassign(arr); } +inline void assign(array_view& arr) { tassign(arr); } -inline float sum_crit(const jvector_crit& arr) { return tsum(arr); } -inline float sum(const jvector& arr) { return tsum(arr); } -inline void assign_crit(jvector_crit& arr) { tassign(arr); } -inline void assign(jvector& arr) { tassign(arr); } +inline float sum_crit(array arr) +{ + array_view_crit a(arr); + return tsum(a); +} +inline float sum(const array& arr) +{ + array_view a(arr); + return tsum(a); +} +inline void assign_crit(array_view_crit& arr) { tassign(arr); } +inline void assign(array& arr) +{ + array_view a(arr); + tassign(a); +} + +array make_an_array(size_t s) +{ + array arr(s); + array_view_crit a(arr); + + for (size_t i = 0; i < a.size(); ++i) + a[i] = i; + + return arr; +} // ---------------------------------------------------------------------------------------- diff --git a/dlib/java/swig_test.java b/dlib/java/swig_test.java index 1d33a50ce..e75edb913 100644 --- a/dlib/java/swig_test.java +++ b/dlib/java/swig_test.java @@ -69,6 +69,14 @@ public class swig_test } } + public static void assertIsEqual(int val1, int val2) + { + if (val1 != val2) + { + throw new RuntimeException("Test failed " + val1 + " should be equal to " + val2); + } + } + public static double sum(double[] arr) { double s = 0; @@ -233,6 +241,13 @@ public class swig_test assertIs28(global.sum_crit(arr)); } } + { + int[] a = global.make_an_array(4); + for (int i = 0; i < a.length; ++i) + { + assertIsEqual(a[i], i); + } + } System.out.println("\n\n ALL TESTS COMPLETED SUCCESSFULLY\n"); }