Совместное использование выходных потоков через интерфейс JNI

Я пишу Java-приложение, которое использует библиотеку C++ через интерфейс JNI. Библиотека C++ создает объекты типа Foo, которые должным образом передаются через JNI в Java.

Предположим, что библиотека имеет функцию вывода

    void Foo::print(std::ostream &os)

и у меня есть Java OutputStream out, Как я могу призвать Foo::print с Java, так что вывод появляется на out? Есть ли способ заставить OutputStream к std::ostream в слое JNI? Могу ли я захватить вывод в буфер уровня JNI, а затем скопировать его в out?

3 ответа

Я хотел бы реализовать ostream C++, который буферизует записи (до некоторого установленного размера), прежде чем сбрасывать эти записи в java OutputStream через JNI.

На стороне Java вы можете использовать обычный экземпляр OutputStream или реализовать постановку в очередь буферных блоков (по существу, byte[]), чтобы избежать возможного конфликта блокировок между потоками. Реальный выходной поток используется только задачей другого потока, которая извлекает блоки из очереди и записывает их в OutpuStream. Я не могу сказать, является ли это необходимым или нет на этом уровне детализации - вы можете найти запись непосредственно в выходной поток из работ JNI.

Я не разделяю проблемы других постеров с JNI и не вижу проблем с использованием JNI для этого. Несомненно, ваши сопровождающие должны будут знать свое дело, но это все, и сложность уровня Java/C++ можно управлять с помощью документации, примеров и тестовых случаев. В прошлом я реализовывал Java<>COM-мост с довольно болтливым интерфейсом - никаких проблем с производительностью, многопоточностью или поддержкой.

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

Я разместил рецензию в своем блоге, в которой подробно описан мой недавний опыт с этой же проблемой. В общем, вы хотите избегать попыток подключить входной или выходной поток к клиенту на любом языке, поскольку это подразумевает потоки. Вы можете постепенно доставлять данные, используя обратные вызовы.

Как я могу вызвать Foo::print из Java, чтобы вывод был включен?

Концептуально говоря, способ заставить Foo::print(...) записать в существующий экземпляр Java OutputStream - написать реализацию C++ std::ostream, которая фактически выполняет обратный вызов в Java для вывода.

Это звучит возможно, но я не хотел бы писать / поддерживать код. Во время выполнения у вас будут вызовы, исходящие из Java -> C++ -> Java, и существует множество возможностей для ошибок, которые случайным образом приводят к сбоям в вашей JVM.

Есть ли способ привести OutputStream к std:: ostream в слое JNI?

AFAIK нет.

Могу ли я захватить вывод в буфер уровня JNI и затем скопировать его в out?

Вы имеете в виду что-то вроде этого?

    MyJNIThing m = ...
    int myOstream = m.createMemoryBackedOStream(...); // native method
    ...
    m.someMethodWrapper(... myOStream); // native method
    ...
    byte[] data = m.getCapturedData(myOStream); // native method
    out.write(data);

Вы, вероятно, можете сделать что-то подобное... в хороший день с последующим ветром.

Но я думаю, что вы действительно должны стремиться устранить код C++, а не пытаться делать все более сложные вещи в JNI. IMO, JNI следует использовать только как последнее средство, а не как кратчайший путь, чтобы избежать перекодирования в Java.

Другие вопросы по тегам