?
Solved

Copying jobjectarray to CString[] with JNI

Posted on 2006-04-18
18
Medium Priority
?
2,063 Views
Last Modified: 2008-01-09
0
Comment
Question by:jsm11482
  • 9
  • 7
17 Comments
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 400 total points
ID: 16478925
//CString array to jobjectArray
jobjectArray JNIUtil::CopyArray(JNIEnv* env, CString* in)
{
     jobjectArray out = env->NewObjectArray(in->GetLength(), env->FindClass("java/lang/String"), NULL);

     for (int i = 0; i < in->GetLength(); i++)
          env->SetObjectArrayElement(out, i, env->NewStringUTF(in[i]));

     return out;
}

Do you really want to add any single character as a JNI string to the object array? If so, the function seems to be ok though I don't know whether JNIEnv::NewStringUTF takes a char as an argument (note, in[i] is a single char). If not you need to use the CString::Mid member function:

     env->SetObjectArrayElement(out, i, env->NewStringUTF(in.Mid(i, 1)));

in.Mid(i, 1) returns a substring of length 1 beginning at position i. As CString automatically converts to 'const char*' - if required - that should compile.

//CStringList to jobjectArray
jobjectArray JNIUtil::CopyArray(JNIEnv* env, CStringList* in)
{
     jobjectArray out = env->NewObjectArray(in->GetCount(), env->FindClass("java/lang/String"), NULL);
     // return out;  //  SEEMS TO BE A TYPO ???

     // for (int i = 0; i < in->GetCount(); i++)
     //     env->SetObjectArrayElement(out, i, env->NewStringUTF((CString)in->GetAt((POSITION)i)));

     int i = 0;
     for (POSITION pos = in.GetHeadPosition(); pos != NULL)
           env->SetObjectArrayElement(out, i++, env->NewStringUTF((CString)in->GetNext(pos)));
     return out;
}

Note, POSITION is *not* a counter but an iterator. GetNext returns the CString at the current position and increments the iterator pos.


//double array to jdoubleArray
jdoubleArray JNIUtil::CopyArray(JNIEnv*env, double* in, int siz)
{
     jdoubleArray out = env->NewDoubleArray(siz);

     env->SetDoubleArrayRegion(out, 0, siz, in);

     return out;
}

In C/C++ an array turns to a pointer (to the first array element) when passed as an argument. Thus, the array size got lost and you need to pass the array size as an additionally argument (or use some sort of termination, e. g. by setting the last element to the minimum double value).

//jobjectArray to CString array
void JNIUtil::CopyArray(JNIEnv* env, jobjectArray in, CString* out)
{
     for (int i = 0; i < env->GetArrayLength(in); i++)
          out += env->GetStringUTFChars((jstring)env->GetObjectArrayElement(in, i), FALSE);
}

You would need to concatinate the string you got from object array (assuming GetStringUTFChars returns a const char*).

//jobjectArray to double array
void JNIUtil::CopyArray(JNIEnv* env, jdoubleArray inObj, double* out)
{
     jdouble* in = env->GetDoubleArrayElements(inObj, FALSE);
     int siz = GetArrayLength(inObj);
     if (siz > 0)
     {
        out = new double[siz];

        for (int i = 0; i < siz; i++)
            out[i] = in[i];
     }
     else
           out = NULL;
   
}

Assuming 'out' wasn't allocated yet.

Regards, Alex
0
 

Author Comment

by:jsm11482
ID: 16479186
For the first one, i do not want to add each char to the jobjectArray, i want each string, CString[] arr = new CString[] {"this", "is", "a", "string", "array"};  Can you post good code to accomplish this?

I am trying out your other code changes and will get back to you!

Thanks!
0
 
LVL 25

Expert Comment

by:InteractiveMind
ID: 16479249
<This _should_ really be continued in only 1 thread; jsm11482, which thread do you wish to continue this in? C++ or Java?>
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16479587
>>>> For the first one, i do not want to add each char to the jobjectArray,

//CString array to jobjectArray

jobjectArray JNIUtil::CopyArray(JNIEnv* env, CString in[], int siz)
{
     jobjectArray out = env->NewObjectArray(siz, env->FindClass("java/lang/String"), NULL);

     for (int i = 0; i < siz; i++)
          env->SetObjectArrayElement(out, i, env->NewStringUTF(in[i]));

     return out;
}

'CString in[]' is equivalent to CString* but you easier can see that it is an array of CStrings. The size of the array can't be detected from the argument as it turned to a pointer (size is always 4) when passed as an argument. So, you would need an additional argument as it would be with any C array. As an alternative you might use std::vector<CString> or CStringArray that were container classes with a size() or GetSize() function.

jobjectArray JNIUtil::CopyArray(JNIEnv* env, CStringArray in)
{
     jobjectArray out = env->NewObjectArray(in->GetSize(), env->FindClass("java/lang/String"), NULL);

     for (int i = 0; i < in.GetSize(); i++)
          env->SetObjectArrayElement(out, i, env->NewStringUTF(in[i]));

     return out;
}

Regards, Alex


>>>> This _should_ really be continued in only 1 thread

As it is C++ code that is required I would suggest to let the solution here in C++ TA.

The Java thread may be closed as well as the maximum points for one question is not exceeded.



0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16479600
correction:

jobjectArray JNIUtil::CopyArray(JNIEnv* env, const CStringArray& in)
{
     jobjectArray out = env->NewObjectArray(in.GetSize(), env->FindClass("java/lang/String"), NULL);

     for (int i = 0; i < in.GetSize(); i++)
          env->SetObjectArrayElement(out, i, env->NewStringUTF(in[i]));

     return out;
}

0
 

Author Comment

by:jsm11482
ID: 16479604
please continue this thread in the C++ area
0
 

Author Comment

by:jsm11482
ID: 16480071
ok so if i send a double array of {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} i am getting back an array of (| delimited):
0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|4.9E-324|-999.0|-999.0|-999.0|-999.0|-999.0|-999.0|0.0|0.05|1.0|0.0|1.0|0.0|0.0|0.0|0.0|1.016208805E-315|0.045|0.0|0.0|1.4967570022479021E-249|2.7585945288E-313|0.0|0.0|1.4784031306360101E-249|1.06099789573E-313|1.2288611814499066E-249|1.9199678765E-313|4.4E-323|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|0.0|2.0|1.016208805E-315|0.03|0.04|0.0|1.537211390420101E-249|2.223731269E-314|1.5372071280969523E-249|2.121995796E-314

trace:
calc.setPctSharesVestArray(new double[]  {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
-> to C++ DLL convert jobjectArray to double array

calc.setPctSharesVestArray()
-> to C++ DLL convert double array to jobjectArray

returns the long array above (after making the suggested code changes)
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16480236
>>>> calc.setPctSharesVestArray(new double[]  {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});

The code above isn't valid C++ code.

Please, post the original call that failed (plus array creation) and the code of the function that does the conversion( should be JNIUtil::CopyArray).

Regards, Alex
0
 

Author Comment

by:jsm11482
ID: 16480305
The line: calc.setPctSharesVestArray(new double[]  {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});

is run in java via JNI, the array is passed to the following C++ function:

JNIEXPORT void JNICALL Java_com_towers_ltip_LTIPCalc_setPctSharesVestArray (JNIEnv *env, jobject obj, jint ptr, jdoubleArray value)
{
      CLTIPCalc* calc = (CLTIPCalc*)ptr;
      JNIUtil* util = new JNIUtil();

      util->CopyArray(env, value, calc->m_adPctSharesVest);
}

i just want to copy the passed-in jdoubleArray to the member variable m_adPctSharesVest.

Sorry for the confusion.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16480405
>>>> Sorry for the confusion

No problem, but I need both the class/struct definition of CLTIPCalc and the code of JNIUtil::CopyArray.

You always should be aware that C++ needs allocation if handling with plain C arrays rather than with container classes.

Regards, Alex

0
 

Author Comment

by:jsm11482
ID: 16480496
Definition of variable in CLTIPCalc:
    double m_adPctSharesVest[11];

JNIUtil::CopyArray definitions:

//jdoubleArray to double[]
void JNIUtil::CopyArray(JNIEnv* env, jdoubleArray inObj, double* out)
{
      jdouble* in = env->GetDoubleArrayElements(inObj, FALSE);
      int size = env->GetArrayLength(inObj);

      if (size > 0)
      {
            out = new double[size];

            for (int i = 0; i < size; i++)
                  out[i] = in[i];
      }
      else
            out = NULL;
}


and then....
//double[] to jdoubleArray
jdoubleArray JNIUtil::CopyArray(JNIEnv*env, double* in, int size)
{
      jdoubleArray out = env->NewDoubleArray(size);

      env->SetDoubleArrayRegion(out, 0, size, in);

      return out;
}
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16480541
>>>> void JNIUtil::CopyArray(JNIEnv* env, jdoubleArray inObj, double* out)

That is wrong cause we need to return 'out' pointer but were changing it locally only.

Either change to

    void JNIUtil::CopyArray(JNIEnv* env, jdoubleArray inObj, double*& out)

Then out pointer was returned. Or - maybe better -

double* JNIUtil::CopyArray(JNIEnv* env, jdoubleArray inObj)
{
    jdouble* in = env->GetDoubleArrayElements(inObj, FALSE);
     int size = env->GetArrayLength(inObj);

     double out* = NULL;
     if (size > 0)
     {
          out = new double[size];

          for (int i = 0; i < size; i++)
               out[i] = in[i];
     }

     return out;
}

Regards, Alex
 
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16480604
correction:

I saw that you already have an allocation for the double array. So, you could change the code to

jdoubleArray to double[]
void JNIUtil::CopyArray(JNIEnv* env, jdoubleArray inObj, double* out)
{
     jdouble* in = env->GetDoubleArrayElements(inObj, FALSE);
     int size = env->GetArrayLength(inObj);

     if (size > 0 && size <= 11)
     {
          for (int i = 0; i < size; i++)
               out[i] = in[i];
     }
}

Returning a pointer is senseful only if you would change the array in the struct CLTIPCalc to a pointer initialized by NULL. Then, you would call like

   calc->m_adPctSharesVest = util->CopyArray(env, value );

Regards, Alex


0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16480686
>>>> Definition of variable in CLTIPCalc:
>>>>    double m_adPctSharesVest[11];

Did you initialize the array somehow? You could do it in a constructor:


   CLTIPCalc::CLTIPCalc()
   {
     memset(m_adPctSharesVest, 0, sizeof(m_adPctSharesVest));
     // init other members or use an initialiser list

  }

Or - maybe simpler - initialize the struct when creating an instance:

   CLTIPCalc any = { 0 };  // make all zero

Note, that can't be done if there are class members.

Regards, Alex
0
 

Author Comment

by:jsm11482
ID: 16480957
it is initialized as a static array in the header file: double m_adPctSharesVest[11];
0
 

Author Comment

by:jsm11482
ID: 16700763
Seems OK, I ended up using a solution not posted here. Thank you.
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 16701509
>>>> Seems OK, I ended up using a solution not posted here. Thank you.

I made at least 6 comments with valid C++ code responding to various additional questions made by jsm11482.

If the questioner doesn't honour that, I don't want points, but a refund isn't appropriate, especially as there is no open question left - as far as I can see.

Regards, Alex
 
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.
Suggested Courses

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question