JNA - przekazywanie wskaźnika funkcji i argumentu

0

Mam poważny problem z ogarnięciem JNA. Mam taki oto kod napisany w C

typedef void (*example_ptr)(const char*);

void exampleMethod(const char* value)
{
    printf("This is the string: %s\n", value);
}

void example_triggerCallback(const example_ptr func, const char* str) {
    printf("provided str: %s", str);
    func(str);
}

Guru jakimś C nie jestem, ale kod wydaje się być poprawny. Co chcę osiągnąć? Chciałbym podać do funkcji example_triggerCallback oba argumenty z poziomu Javy. Przykład jest dość uproszczoną formą tego co chcę osiągnąć. Na chwilę obecną kod po stronie Javy wygląda tak:

public class Main {

    public interface TestLibrary extends Library {
        TestLibrary INSTANCE = (TestLibrary)
                Native.loadLibrary("libtest",
                        TestLibrary.class);

        interface ExampleCallback extends Callback {
            void invoke(String str);
        }

        class ExampleCallbackImpl implements ExampleCallback {
            public void invoke(String str) {
                System.out.println("example: " + str);
            }
        }
        void example_triggerCallback(ExampleCallback callback, String str);
    }

    public static void main(String[] args) {

        TestLibrary testLibrary = TestLibrary.INSTANCE;
        final TestLibrary.ExampleCallbackImpl callback = new TestLibrary.ExampleCallbackImpl();
        testLibrary.example_triggerCallback(callback, "testiddy test test");
    }
}

No i okazuje się, że funkcja w C example_triggerCallback owszem jest wywoływana, bo w konsoli dostaję informację z linijki printf("provided str: %s", str); Problem pojawia się, przy wywołaniu wskaźnika na funkcję z podanym argumentem const char* str (który po stronie Javy jest Stringiem). Jest on zwyczajnie pomijany. No i tutaj moje pytanie, czego brakuje mi po stronie Javy, tak aby linijka func(str); faktycznie została wywołana?

0

Nie mogę sprawdzić, bo zaraz konczy mi sie net. NewStringUtf czy cos w tym stylu. Musisz opakowac char*

0
jarekr000000 napisał(a):

Nie mogę sprawdzić, bo zaraz konczy mi sie net. NewStringUtf czy cos w tym stylu. Musisz opakowac char*

Według dokumentacji JNA const char* mapowany jest na String, więc tutaj akurat powinno być dobrze.

Kopiowane z dokumentacji
const char* - NUL-terminated array (native encoding or jna.encoding) - String

0

Problem raczej polega na tym, że w Java nie ma wskaźników na funkcję.
Masz interfejsy.
A w interfejsie kryje się dodatkowy argument wskazujący na obiekt.
Czyli to void invoke(String str); powinno mieć po stronie C odpowiednik czegoś takiego void invoke(void *pThis, const char *str);.
Lub inaczej nie wiadomo jak callback z C typedef void (*example_ptr)(const char*); przedstawić w Java.

W C nie ma lambd i nie możesz użyć zmiennej globalne żeby przemycić this do callback-a.
Problem więc nie jest trywialny, ale fundamentalny i jedyną drogę jaką widzę by go obejść, jest dodanie dodatkowego argumentu do callback-a po stronie C.

Zwróć uwagę, że gdy ktoś projektuje API z callbackiem w C (np OpenSSL) to robi to zwykle tak, że dodaje dodatkowy argument typu void* by można było przemycić własne informację do callbacka:

typedef void (*example_ptr)(const char*, void *context);

void example_triggerCallback(const example_ptr func, const char* str*, void *context) {
    printf("provided str: %s", str);
    func(str, context);
}

Zwróć uwagę, że example_triggerCallback nigdy nic nie robi z context jedynie przekazuje go do callbacka func.
Brak tego kontekstu w twoim kodzie C stanowi właśnie problem.

0
MarekR22 napisał(a):

Czyli to void invoke(String str); powinno mieć po stronie C odpowiednik czegoś takiego void invoke(void *pThis, const char *str);.
Lub inaczej nie wiadomo jak callback z C typedef void (*example_ptr)(const char*); przedstawić w Java.

W C nie ma lambd i nie możesz użyć zmiennej globalne żeby przemycić this do callback-a.
Problem więc nie jest trywialny, ale fundamentalny i jedyną drogę jaką widzę by go obejść, jest dodanie dodatkowego argumentu do callback-a po stronie C.

void invoke(String str); jest mapowane na func(str);, gdzie podczas wywołania programu, jeśli zrobię hardcode func("4programmers"); to po stronie Javy w linijce System.out.println("example: " + str); dostanę w konsoli Javy example invoke: 4programmers

Tutaj akurat typedef void (*example_ptr)(const char*); nie jest jakoś zbytnio problemem (tak mi się wydaje).
Co masz na myśli przez dodanie dodatkowego argumentu do callbacku po stronie C? Dalej nie wiem jakby miało mi to pomóc w przekazaniu referencji do metody exampleMethod :/

0
MarekR22 napisał(a):
typedef void (*example_ptr)(const char*, void *context);

void example_triggerCallback(const example_ptr func, const char* str*, void *context) {
    printf("provided str: %s", str);
    func(str, context);
}

Zwróć uwagę, że example_triggerCallback nigdy nic nie robi z context jedynie przekazuje go do callbacka func.
Brak tego kontekstu w twoim kodzie C stanowi właśnie problem.

Mniej więcej rozumiem o co chodzi, ale dalej nie wiem w jaki sposób miałoby mi to pomóc... W sensie, że dodanie dodatkowego pola context średnio pomoże mi, aby po stronie Javy faktyczni mógł wywołać wszystko tak jak chciałbym.

2

W sumie to ciekawe, znalazłem przykład w dokumentacji JNA:
https://www.eshayne.com/jnaex/index.html?example=22

Wiec nie miałem racji, i coś takiego powinno działać (JNA jest o wiele sprytniejsze od JNIów):

typedef void (*ExampleCallback)(const char*);

void exampleMethod(const char* value)
{
    printf("This is the string: %s\n", value);
}

void example_triggerCallback(const example_ptr func, const char* str) {
    printf("provided str: %s", str);
    func(str);
}
public interface CLibrary extends Library {
	// define an interface that wraps the callback code
	public interface ExampleCallbackInterface extends Callback {
		void invoke(String val);
	}

        // functions available in library (name must match)
	public void exampleMethod(String  value);
	public void example_triggerCallback(ExampleCallbackInterface callback);
}


// define an implementation of the callback interface
public static class CallbackExample implements Example22CallbackInterface {
	private CLibrary lib;

	public CallbackExample(CLibrary useLib) {
		lib = useLib;
	}

	@Override
	public void invoke(String val) {
		lib.exampleMethod(val);
	}
}

...
final CLibrary clib = (CLibrary)Native.loadLibrary("testlib", CLibrary.class);
...
// instantiate a callback wrapper instance
final CallbackExample callback = new CallbackExample(clib);

// pass the callback wrapper to the C library
clib.example_triggerCallback(callback);
0
MarekR22 napisał(a):

W sumie to ciekawe, znalazłem przykład w dokumentacji JNA:
https://www.eshayne.com/jnaex/index.html?example=22

Jezusku, to żyje. Dziękuję Ci bardzo :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1