7

我在 C++ 中有一个类,它以 astd::ostream作为参数以连续输出文本(跟踪信息)。我需要尽可能高效地将此文本传递到 Java 端。最好的方法是什么?我正在考虑使用直接缓冲区,但另一种方法是将所有函数调用传递到 Java 并在那里进行所有处理,但似乎我需要很多 JNI 调用。

如果可以显示一个确切实现方法的示例,那将非常有帮助,或者如果已经存在一些代码来执行此操作(可能是另一个项目的一部分)。另一个帮助是将其直接连接到标准 Java 流结构,这样整个实现对开发人员来说是完全透明的。

(编辑:我发现通过 JNI 接口共享输出流,这似乎是重复的,但并没有太大帮助——他似乎没有找到他正在寻找的答案)

4

2 回答 2

5

std::ostream 类的输出需要一个 std::streambuf 对象。这由 fstream 和 stringstream 类使用,它们通过提供 streambuf 类的自定义实现来使用 ostream 的特性。

因此,您可以使用覆盖的溢出方法编写自己的 std::streambuf 实现,将传入的字符缓冲在内部字符串缓冲区中。每个 x 调用或在 eof/newline 上生成一个 java-string 并调用 java PrintStream 的 print 方法。

一个不完整的示例类:

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 页面以不同的方式派生字符串缓冲区。
很抱歉在没有更好地测试的情况下发布此内容:-(。
在或多或少无关的点上对上面的代码进行了一个小更正:只需使用自定义类实现 InputStream 并推送 byte[] 数组而不是 c++ 中的字符串
。InputStream 有一个小接口和 BufferedReader 应该完成大部分工作。

关于这个的最后更新,因为我无法让它在 linux 上工作,即使在 std::streambuf 类上的注释指出只有溢出必须被覆盖。
此实现将原始字符串推送到输入流中,该输入流可以被其他线程读取。因为我太愚蠢了,无法让调试器再次未经测试地工作。

//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 调用来传递它?这将是代码如何形成的最有力的指南。

如果您可以合理地期望文本显示为一系列行,我会考虑在每次调用中将它们呈现给 Java:这似乎是 JNI 调用数量与不过度延迟传递之间的公平折衷文本。

在 Java 方面,我认为您正在考虑创建一个 Reader,以便客户端可以通过熟悉的界面或者可能是 BufferedReader 的子类来获取文本。

于 2010-02-24T21:19:23.277 回答