7

std::ostreamテキスト (トレース情報) を継続的に出力するために、引数としてを受け取る C++ のクラスがあります。このテキストをできるだけ効率的に Java 側に渡す必要があります。これを行う最善の方法は何ですか?ダイレクトバッファを使おうと思っていたのですが、関数呼び出しをすべてJavaに渡して処理するという方法もあるのですが、JNI呼び出しが多く必要になりそうです。

正確な実装方法の例を示すことができれば、非常に役に立ちます。または、これを行うためのコードが既に存在する場合 (おそらく別のプロジェクトの一部)。もう 1 つの助けは、実装全体が開発者に対して完全に透過的であるように、標準の Java ストリーミング構造に直接接続することです。

(編集:重複しているように見えるJNIインターフェースを介して出力ストリームを共有していることがわかりましたが、実際にはあまり役に立ちませんでした.彼は探していた答えを見つけられなかったようです)

4

2 に答える 2

5

std::ostream クラスには、出力用の std::streambuf オブジェクトが必要です。これは、streambuf クラスのカスタム実装を提供することによって ostream の機能を使用する fstream および stringstream クラスによって使用されます。

そのため、上書きされたオーバーフロー メソッドを使用して独自の std::streambuf 実装を記述し、着信文字を内部文字列バッファーにバッファーすることができます。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);
  }
}

ない:

  • マルチスレッドのサポート (env ポインターは jvm スレッドに対してのみ有効です)

  • エラー処理 (スローされた Java 例外のチェック)

  • テスト (過去 70 分以内に書かれたもの)

  • 印刷ストリームを設定するネイティブ Java メソッド。

Java 側では、PrintStream を BufferedReader に変換するクラスが必要です。

そこにはいくつかのバグがあるはずで、それらに取り組むのに十分な時間を費やしていません。
クラスは、それが作成されたスレッドからのすべてのアクセスを必要とします。

お役に立てれば


Visual Studioで動作するようになりましたが、g ++で動作しないことに注意してください。後でデバッグを試みます。
編集 私の回答を投稿する前に、これに関するより公式なチュートリアルを探すべきだったようです。このトピックに関するMSDNページは、別の方法でstringbufferを導出しています。
より良いテストをせずにこれを投稿して申し訳ありません:-(。
多かれ少なかれ関係のない点で上記のコードに小さな修正を加えます。カスタム クラスで InputStream を実装し、C++ の文字列の代わりに byte[] 配列をプッシュするだけです
。InputStream には、小さなインターフェイスと BufferedReader がほとんどの作業を行う必要があります。

オーバーフローのみを上書きする必要があるという std::streambuf クラスのコメントがあっても、Linux で動作させることができないため、これに関する最後の更新です。
この実装は生の文字列を入力ストリームにプッシュし、他のスレッドから読み取ることができます。私はあまりにも愚かなので、デバッガーをテストしていない状態で動作させることはできません。

//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);
    }
}

スペースを無駄にしてすみません^^(そして時間:-()。

于 2010-03-03T18:44:36.940 に答える
0

ここでの成果物は、ostream のサブクラスのように思えます。私が明確にしたい当面の質問は、このクラスは、Java が呼び出して取得するまでデータをバッファリングする責任があるのか​​、それとも、JNI を介してすぐに (同期的に?) 呼び出してデータを渡すことが期待されるのかということです。これは、コードがどのように形成されるかについての最も強力なガイドになります。

テキストが一連の行として表示されることを合理的に期待できる場合は、呼び出しごとに 1 行で Java に表示することを検討します。文章。

Java 側では、Reader を作成して、クライアントが使い慣れたインターフェイスまたはおそらく BufferedReader のサブクラスを介してテキストを取得できるようにすることを検討していると思います。

于 2010-02-24T21:19:23.277 に答える