Pergunta

Eu tenho uma classe em C++ o que leva um std::ostream como um argumento, de forma contínua, a saída de texto (informações de rastreamento).Eu preciso ler este texto sobre o Java lado de forma tão eficiente quanto possível.Qual é a melhor maneira de fazer isso?Eu estava pensando em usar um direct buffer, mas outro método seria a de tomar todas as chamadas de função em Java e fazer todo o processamento de lá, mas parece que eu iria precisar de um monte de chamadas JNI.

Se um exemplo poderia ser mostrado de exatas, método de aplicação, seria muito útil, ou se algum código que já existe para fazer isso (talvez parte de outro projeto).Outra ajuda seria para ligá-la diretamente a um padrão de Java streaming de construir, de tal forma que toda a implementação foi completamente transparente para o desenvolvedor.

(Edição:Eu encontrei Partilha fluxos de saída através de uma interface JNI o que parece ser uma duplicata, mas não é realmente muito de ajuda-ele parecia não encontrar a resposta que estava procurando)

Foi útil?

Solução

A classe STD :: Ostream requer um objeto STD :: Streambuf para sua saída. Isso é usado pelas classes FStream e StringStream, que usam os recursos do Ostream, fornecendo uma implementação personalizada da classe StreamBuf.

Assim, você pode escrever sua própria implementação STD :: Streambuf com um método de transbordamento substituído, buffer os chars incríveis em um StringBuffer interno. Cada X chamadas ou ON EOF/NewLine gera uma linha Java e chama o método de impressão do seu Java PrintStream.

Uma classe de exemplo incompleta:

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

Ausência de:

  • Suporte multithread (o ponteiro envernizado é válido apenas para o thread JVM)

  • Manuseio de erros (verificando as exceções Java jogadas)

  • Teste (escrito nos últimos 70 min)

  • Método Java nativo para definir o PrintStream.

No lado Java, você precisa de uma classe para converter o PrintStream em um leitor de buffers.

Tem que haver alguns insetos lá, não gaste tempo suficiente para trabalhar neles.
A classe exige que todo o acesso seja do tópico em que foi criado.

Espero que isto ajude

Observação
Eu comecei a trabalhar com o Visual Studio, mas não consigo fazer funcionar com o G ++, tentarei depurar mais tarde.
EditarParece que eu deveria ter procurado um tutorial mais oficial sobre este bevore postando minha resposta, o Página msdn Nesse tópico, deriva o StringBuffer de uma maneira diferente.
Desculpe por postar isso sem testá-lo melhor :-(.
Uma pequena correção para o código acima em um ponto mais ou menos não relacionado: basta implementar o InputStream com uma classe personalizada e push byte [] em vez de strings de C ++.
O InputStream possui uma pequena interface e um leitor de buffers deve fazer a maior parte do trabalho.

Última atualização sobre este, já que não consigo fazer funcionar no Linux, mesmo com os comentários da classe Std :: Streambuf, afirmando que apenas o transbordamento precisa ser substituído.
Essa implementação empurra as cordas brutas para um InspStream, que pode ser lido por um outro thread. Como sou estúpido demais para fazer com que o depurador trabalhe seu não testado, novamente.

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

Desculpe por desperdiçar muito espaço ^^ (e tempo :-().

Outras dicas

Soa como se o resultado final aqui é uma subclasse de ostream.O imediato pergunta que eu gostaria de ser claro sobre o é, será que essa classe será responsável para o buffer de dados até que o Java chama-lo para recuperar, ou é esperado para imediatamente (de forma síncrona?) a chamada via JNI para passá-lo?Que vai ser o mais forte guia para como o código irá moldar-se.

Se você pode razoavelmente esperar que o texto seja exibido como uma série de linhas, eu pensava em apresentá-las para o Java em uma linha por chamada:esta parece ser uma feira de compromisso entre o número de chamadas JNI e sem atrasar indevidamente a passagem do texto.

No Java lado eu acho que você está olhando para a criação de um Leitor para que os clientes possam pegar o texto através de uma interface familiar, ou talvez uma subclasse de BufferedReader.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top