Pessoal, alguém pode me ajudar com o pedaço de código abaixo?
#include <jni.h>
#include <stdio.h>
#include "Prompt.h"
//Chama o tipo string da biblioteca JNI
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const jbyte *str;
//Não entendi a linha abaixo:
str = (*env)->GetStringUTFChars(env, prompt, NULL);
}
Env é um ponteiro que aponta para o método GetStringUTFChars(lê strings no formato UTF-8), da biblioteca jni.
Alguém sabe me dizer o que significa esse "(*env)->"? Eu sei que em C, as setas e os pontos relacionados a um ponteiro chamam o que está depois deles.
Mas não entendi o que se passa naquela linha exatamente. Se alguém puder me ajudar, agradeço.
No JNIEnv, nele tem o método GetStringUTFChars(env, prompt, NULL).
Quando você usa o JNIEnv *env você usa o operador [quote]->[/quote], quando você usa o JNIEnv env você usa [quote].[/quote] para acessar os métodos e atributos.
-> e . são operadores de seleção de membros, assim como o . é em Java.
A diferença é que -> seleciona a partir do ponteiro para o objeto, enquanto . seleciona a partir do próprio objeto.
Então o segredo está na diferença entre “JNIEnv *env” e “JNIEnv env”. *env é um ponteiro e env seria só uma estrutura. Logo, devo usar a -> para atribuir valor a um atributo da estrutura por meio do ponteiro, e o operador “.” para passar um valor direto a um atributo da estrutura, o que seria equivalente a um membro static no Java, isso mesmo?
Se for isso, essa parte eu entendi, só não saquei direito o que se passa naquela linha.
Olhando com mais cuidado o jni.h.
Em C "p->m" é uma abreviação para "(*p).m".
No seu caso, você tem isto aqui:
(*env)->GetStringUTFChars
onde env é do tipo "JNIEnv *"
env : tipo JNIEnv*
*env : tipo JNIEnv (que é definido como
typedef const struct JNINativeInterface_ *JNIEnv;
Ou seja, JNIEnv é um tipo ponteiro (mais propriamente um const *) que aponta para um struct do tipo JNINativeInterface_.
(env)->GetStringUTFChars equivale a ((*env)).GetStringUTFChars, ou seja, está pegando um membro de JNINativeInterface_ chamado GetStringUTFChars (a definição dele é:
const char* (JNICALL *GetStringUTFChars)
(JNIEnv *env, jstring str, jboolean *isCopy);
Ou seja, é um ponteiro para um método que recebe 3 parâmetros: (JNIEnv *env, jstring str, jboolean isCopy)
e retorna um parâmetro const char.
Como em C é possível chamar um ponteiro de função como se fosse uma função, então você pode chamar o tal método GetStringUTFChars que está implementado, em C, em algum lugar. Note que em C não existem classes, quanto mais métodos virtuais; então foi por isso que foi feita toda essa "salada" de você chamar o método através de um ponteiro de função.
Huuuum, agora deu uma clareada na minha cabeça. Que salada heim. Puta complicação esse JNI, e olha que eu tô no começo, rsrs Mas agora as coisas fizeram mais sentido. Obrigado a todos mais uma vez.
Só mais uma pergunta: Isso acontece porque JNIEnv é um ponteiro que aponta para um local que tem outros ponteiros para funções da biblioteca JNI, como eu disse antes?
Por isso o *env pode referenciar, digo, chamar uma função?
O velho C
Olhei em alguns exemplos e pelo que entendi.
O env é o próprio ponteiro, o segundo prompt é a string de entrada, como em C não tem o tipo String é criado uma variável *str do tipo jbyte, o restante o método se encarrega de fazer a conversão, depois é só usar o printf do C para imprimir o conteúdo da variável str.
Quando você usa * você tem uma refêrencia do objeto (um endereço na memória) e o . é uma referencia, não tem nada a ver com static do java.
Exatamente. Quando o JNI é inicializado, a JVM cria uma estrutura contendo esses ponteiros de funções. Essa estrutura é o “environment” e o JNIEnv é um tipo que é um ponteiro para essa estrutura.
Muito bom pessoal, muito bom mesmo. Olha, mais uma vez muito obrigado. Agora eu posso estudar JNI em paz.
Obrigado a todos.
Vou dar um exemplo.
// Um método tradicional...
long teste(int x) {
printf ("Hello, world");
return -2;
}
// Definindo uma struct que contém um ponteiro para um método que recebe 1 parâmetro int e retorna 1 long
struct Env {
long (*pTeste)(int);
};
// Definindo o tipo "ponteiro para Env"
typedef Env* PENV;
int main (int argc, char *argv[]) {
Env env;
env.pTeste = teste; // atribuindo o ponteiro de função
PENV pEnv = &env;
pEnv->pTeste (3); // Chamando a função
(*pEnv->pTeste) (3); // outra forma de chamar a a função
}
Valeu de novo. Ótimo exemplo.