Существует довольно много приложений для Android, сочетающих в себе код C++ и Java. Где Java действует как оболочка/слой, а C++ выполняет всю грязную работу.
Пожалуй, ярким примером являются игры.
В связи с этим часто приходится вызывать Java-код из нативного, чтобы получить доступ к системным свойствам и преимуществам, которые предоставляет система (переключиться на другую деятельность, отправить или скачать что-либо из Интернета).
Причин много, но проблема только одна: каждый раз приходится писать в лучшем случае 5 строк кода и помнить, какую сигнатуру функции нужно запихнуть в параметр.
Потом еще нужно преобразовать эти параметры к нужному типу.
Стандартный пример из туториалов:
Строка подписи для этого метода будет (ILjava/язык/строка;F)J .long f (int n, String s, float g);
Удобно ли вам все это помнить? Как насчет преобразования C-строк в jstring? Нет мне.
Я хочу написать: CallStaticMethod<long>(className, “f”, 1, 1.2f);
Постановка задачи
Для начала давайте поймем, что нам нужно.По сути, это четыре вещи:
- Метод вызова;
- Вам необходимо извлечь строку подписи из параметров.
Да-да, вот этот (ILjava/lang/String;F)J;
- Преобразовать параметры к нужному типу;
- Верните тип данных, который хочет видеть пользователь нашего класса.
Кажется, это просто.
Давайте начнем?
Вызов метода
Теперь стоит отметить, как мы будем вызывать нашу функцию-обертку.Так как параметров может быть разное количество (от нуля и больше), то в стандартной библиотеке нужна функция типа print, но так, чтобы было удобно извлекать тип параметра и сам параметр.
В C++11 представлены шаблоны с переменным числом вариантов.
Мы будем использовать их.
template <typename MethodType, typename. Args>
MethodType CallStaticMethod(Args. args);
Делаем подпись
Сначала нам нужно получить строку, указанную в документации для этого типа.Есть два варианта:
- Мы используем typeid и цепочку if.else. Это должно выглядеть примерно так:
if (typeid(arg) == typeid(int)) return “I”; else if (typeid(arg) == typeid(float)) return “F”;
И так для всех нужных вам типов. - Мы используем шаблоны и их частичные типизации.
Способ интересен тем, что у вас будут функции в одной строке и не будет лишних сравнений типов.
Причём всё это будет на этапе создания шаблона.
Все будет выглядеть примерно так:
template <typename T> std::string GetTypeName(); // int template <> std::string GetTypeName<int>() { return “I”; } // string template <> std::string GetTypeName<const char*>() { return “Ljava/lang/String;”; }
Давайте сначала посмотрим на рекурсивный вызов.
void GetTypeRecursive(std::string&)
{ }
template <typename T, typename. Args>
void GetTypeRecursive(std::string& signatureString, T value, Args. args)
Теги: #C++ #jni #android ndk #templates #C++ #Разработка для Android
-
Пост Весеннего Позитива
19 Oct, 24