Совместное использование выходных потоков через интерфейс 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.