Получение текстовых данных из C++ с помощью JNI через станд.:: ostream в Java

У меня есть класс в C++, который берет std::ostream как аргумент, чтобы к непрерывно синтезируемому тексту (трассировочная информация). Я должен закончить этот текст стороне Java максимально эффективно. Что лучший способ состоит в том, чтобы сделать это? Я думал об использовании прямого буфера, но другой метод должен будет взять все вызовы функции через к Java и сделать всю обработку там, но кажется, что мне было бы нужно много вызовов JNI.

Если бы пример можно было бы показать точного метода внедрения, было бы очень полезно, или если некоторый код существует уже, чтобы сделать это (возможно, часть другого проекта). Другая справка должна была бы подключить его непосредственно к стандартной конструкции потоковой передачи Java, такой, что вся реализация была абсолютно очевидна для разработчика.

(Редактирование: Я нашел Совместное использование потоков вывода через интерфейс JNI, который, кажется, дубликат, но не действительно очень полезный - он, казалось, не нашел ответ, который он искал),

6
задан Ahmed Ashour 24 May 2019 в 13:03
поделиться

2 ответа

Класс std :: ostream требует для вывода объекта std :: streambuf. Это используется классами fstream и stringstream, которые используют функции ostream, предоставляя настраиваемую реализацию класса streambuf.

Таким образом, вы можете написать свою собственную реализацию 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 по этой теме выводит строковый буфер по-другому.
Извините, что опубликовал это, не проверив его лучше :-(.
Небольшое исправление в приведенном выше коде в более или менее несвязанном пункте: просто реализуйте InputStream с пользовательским классом и вставьте массивы byte [] вместо String из 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);
    }
}

Извините за потраченное впустую так много места ^^ (и времени :-().

4
ответ дан 17 December 2019 в 07:03
поделиться

Похоже, результат здесь является подклассом ostream. Непосредственный вопрос, который я хотел бы прояснить, заключается в том, будет ли этот класс отвечать за буферизацию данных до тех пор, пока Java не вызовет его для извлечения, или ожидается, что он немедленно (синхронно?) Вызовет через JNI, чтобы передать его? Это будет сильнейшее руководство по формированию кода.

Если вы можете разумно ожидать, что текст будет отображаться в виде серии строк, я бы подумал о том, чтобы представлять их Java в одной строке для каждого вызова: это кажется справедливым компромиссом между количеством вызовов JNI и отсутствием чрезмерной задержки передачи на тексте.

Что касается Java, я думаю, вы собираетесь создать Reader, чтобы клиенты могли получать текст через знакомый интерфейс, или, возможно, подкласс BufferedReader.

0
ответ дан 17 December 2019 в 07:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: