الحصول على بيانات نصية من C ++ باستخدام JNI من خلال std :: ostream في Java

StackOverflow https://stackoverflow.com/questions/2328090

سؤال

لدي فصل في C ++ يأخذ std::ostream كوسيطة من أجل إخراج النص بشكل مستمر (تتبع المعلومات). أحتاج إلى نقل هذا النص إلى جانب جافا بكفاءة قدر الإمكان. ما هي الطريقة المثلى لعمل هذا؟ كنت أفكر في استخدام مخزن مؤقت مباشر ، ولكن هناك طريقة أخرى تتمثل في أخذ جميع مكالمات الوظائف عبر Java والقيام بكل المعالجة هناك ، لكن يبدو أنني أحتاج إلى الكثير من مكالمات JNI.

إذا كان من الممكن عرض مثال على طريقة التنفيذ الدقيقة ، فسيكون ذلك مفيدًا للغاية ، أو إذا كانت هناك بعض التعليمات البرمجية بالفعل للقيام بذلك (ربما جزء من مشروع آخر). تتمثل مساعدة أخرى في توصيلها مباشرةً ببناء تدفق Java قياسي ، بحيث كان التنفيذ بأكمله شفافًا تمامًا للمطور.

(تحرير: لقد وجدت مشاركة تدفقات الإخراج من خلال واجهة JNI الذي يبدو أنه مكرر ، ولكن ليس في الحقيقة الكثير من المساعدة - لا يبدو أنه يجد الإجابة التي كان يبحث عنها)

هل كانت مفيدة؟

المحلول

تتطلب فئة STD :: Ostream كائن STD :: Streambuf لإخراجها. يتم استخدام ذلك بواسطة فئات FStream و StringStream ، والتي تستخدم ميزات Ostream من خلال توفير تنفيذ مخصص لفئة Streambuf.

حتى تتمكن من كتابة تطبيق STD :: Streambuf الخاص بك بطريقة فائض فائقة مكتوبة ، تخزن العزف على chars غير المنفصلة في سلسلة stringbuffer الداخلية. كل مكالمات X أو على EOF/NEWLINE ، قم بإنشاء سلسلة Java واتصل بالطريقة المطبوعة لـ Java PrintStream.

فئة مثال غير مكتملة:

class JavaStreamBuff : std::streambuf
{
  std::stringstream buff;
  int size;
  jobject handle;
  JNIEnv* env

  //Ctor takes env pointer for the working thread and java.io.PrintStream
  JavaStreamBuff(JNIEnv* env, jobject jobject printStream, int buffsize = 50)
  {
     handle = env->NewGlobalRef(printStream);
     this->env = env;
     this->size = size;
  }
  //This method is the central output of the streambuf class, every charakter goes here
  int overflow(int in)
  {
    if(in == eof || buff.size() == size)
   {
     std::string blub = buff.str();

     jstring do = //magic here, convert form current locale unicode then to java string

     jMethodId id = env->(env->GetObjectClass(handle),"print","(java.lang.String)V");

     env->callVoidMethod(id,handle,do);

     buff.str("");
    }
    else
    {buff<<in;}
  }

  virtual ~JavaStreamBuff()
  {
     env->DeleteGlobalRef(handle);
  }
}

مفقود:

  • دعم Multithread (مؤشر ENV صالح فقط لخيط JVM)

  • معالجة الأخطاء (التحقق من استثناءات Java ألقا)

  • الاختبار (مكتوب خلال آخر 70 دقيقة)

  • طريقة Java الأصلية لتعيين printstream.

على جانب Java ، تحتاج إلى فئة لتحويل PrintStream إلى قائد Bufferreader.

يجب أن يكون هناك بعض الأخطاء هناك ، ولم تقضي وقتًا كافيًا للعمل عليها.
يتطلب الفصل كل الوصول إلى الخيط الذي تم إنشاؤه فيه.

أتمنى أن يساعدك هذا

ملحوظة
لقد حصلت على العمل مع Visual Studio ، لكن لا يمكنني تشغيله مع G ++ ، سأحاول تصحيح ذلك لاحقًا.
تعديليبدو أنه كان ينبغي علي البحث عن برنامج تعليمي أكثر رسمية في هذا النشر الذي ينشر إجابتي ، صفحة MSDN في هذا الموضوع ، يستمد StringBuffer بطريقة مختلفة.
آسف لنشر هذا دون اختباره بشكل أفضل :-(.
تصحيح صغير على الكود أعلاه في نقطة غير مرتبطة بدرجة أو أقل: ما عليك سوى تنفيذ inputstream مع فئة مخصصة ودفع المصفوفات [] بدلاً من السلاسل من C ++.
يحتوي InputStream على واجهة صغيرة ويجب على قائد Bufferreader القيام بمعظم العمل.

آخر تحديث حول هذا التحديث ، حيث إنني غير قادر على العمل على Linux ، حتى مع التعليقات على فئة STD :: Streambuf تفيد بأنه لا بد من الكتابة فوق الفائض فقط.
يدفع هذا التنفيذ السلاسل الأولية إلى inputstream ، والتي يمكن قراءتها بواسطة مؤشر ترابط آخر. بما أنني غبي جدًا بحيث لا يعمل تصحيح الأخطاء ، مرة أخرى.

//The c++ class
class JavaStreamBuf :public std::streambuf
{
  std::vector<char> buff;
  unsigned int size;
  jobject handle;
  JNIEnv* env;
public:
  //Ctor takes env pointer for the working thread and java.io.PrintStream
  JavaStreamBuf(JNIEnv* env, jobject  cppstream, unsigned int buffsize = 50)
  {
     handle = env->NewGlobalRef(cppstream);
     this->env = env;
     this->size = size;
     this->setbuf(0,0);
  }
  //This method is the central output of the streambuf class, every charakter goes here
  virtual int_type overflow(int_type in  = traits_type::eof()){
    if(in == std::ios::traits_type::eof() || buff.size() == size)
    {
        this->std::streambuf::overflow(in);
         if(in != EOF)
             buff.push_back(in);

         jbyteArray o = env->NewByteArray(buff.size());
         env->SetByteArrayRegion(o,0,buff.size(),(jbyte*)&buff[0]);
         jmethodID id = env->GetMethodID(env->GetObjectClass(handle),"push","([B)V");

         env->CallVoidMethod(handle,id,o);
         if(in == EOF)
             env->CallVoidMethod(handle,id,NULL);

         buff.clear();
    }
    else
    {
        buff.push_back(in);
    }

    return in;
  }

  virtual ~JavaStreamBuf()
  {
      overflow();
      env->DeleteGlobalRef(handle);
  }

//The java class
/**
 * 
 */
package jx;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author josefx
 *
 */
public class CPPStream extends InputStream {

    List<Byte> data = new ArrayList<Byte>();
    int off = 0;
    private boolean endflag = false;
    public void push(byte[] d)
    {
        synchronized(data)
        {
            if(d == null)
            {
                this.endflag = true;
            }
            else
            {
                for(int i = 0; i < d.length;++i)
                {
                    data.add(d[i]);
                }
            }
        }
    }
    @Override
    public int read() throws IOException 
    {
        synchronized(data)
        {

            while(data.isEmpty()&&!endflag)
            {

                try {
                        data.wait();
                    } catch (InterruptedException e) {
                        throw new InterruptedIOException();
                    }
            }
        }
        if(endflag)return -1;
        else return data.remove(0);
    }
}

آسف لإضاعة الكثير من الفضاء^^(والوقت :-().

نصائح أخرى

يبدو كما لو أن التسليم هنا هو فئة فرعية من Ostream. السؤال الفوري الذي أريد أن أكون واضحًا بشأنه هو ، هل سيكون هذا الفصل مسؤولاً عن تخزين البيانات إلى أن تتصل بها Java لاستردادها ، أم أنه من المتوقع أن تستدعي على الفور (متزامن؟) الاتصال عبر JNI لتمريرها؟ سيكون ذلك أقوى دليل لكيفية تشكيل الرمز.

إذا كنت تستطيع أن تتوقع بشكل معقول أن يظهر النص كسلسلة من الخطوط ، فأنا أفكر في تقديمها إلى Java في سطر واحد لكل مكالمة: يبدو هذا حل وسط عادل بين عدد مكالمات JNI ولا يؤخر ما لا مبرر له لتمرير نص.

على جانب Java ، أعتقد أنك تبحث عن إنشاء قارئ حتى يتمكن العملاء من التقاط النص عبر واجهة مألوفة ، أو ربما فئة فرعية من Bufferreader.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top