Boost + Android? CrystaX NDK!
20.01.2015 14:40

У вас есть код, который использует библиотеки Boost C++? Вы хотите максимально простым способом портировать этот код под Android? Или, может быть, вы просто начинаете новый проект под Android и хотите использовать Boost с самого начала? С CrystaX NDK 10.1.0 вы получаете возможность разрабатывать под Android на C++ с использованием Boost прямо "из коробки".

Давайте посмотрим, как это можно сделать.

Для простоты изложения, будем считать, что вы используете систему сборки проекта из NDK, основанную на файлах Application.mk и Android.mk. Вот структура каталога простейшего проекта, который не содержит Java-части, и результатом сборки которого является один исполняемый файл:

.
└── jni
    ├── Android.mk
    ├── Application.mk
    └── test.cpp

Начальные файлы

Ниже приведено содержимое этих файлов:

Application.mk
# Application.mk
APP_ABI := all
Android.mk
# Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := test-boost
LOCAL_SRC_FILES := test.cpp
include $(BUILD_EXECUTABLE)
test.cpp
// test.cpp
#include <iostream>

int main()
{
    return 0;
}

Добавим Boost

Теперь из этого простого шаблона мы создадим приложение, использующее Boost. В этом примере мы будем использовать библиотеку Boost Serialization. Демонстрируемый подход работает для любой библиотеки из проекта Boost, единственное, что нужно изменить - это собственно имя библиотеки в файле Android.mk.

Сначала добавим в проект файлы, использующие библиотеку Boost Serialization (мы используем официальный пример из Boost):

gps.hpp
// gps.hpp
#ifndef GPS_HPP_7D5AF29629F64210BE00F3AF697BA650
#define GPS_HPP_7D5AF29629F64210BE00F3AF697BA650

// include headers that implement a archive in simple text format
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

/////////////////////////////////////////////////////////////
// gps coordinate
//
// illustrates serialization for a simple type
//
class gps_position
{
private:
    friend class boost::serialization::access;
    friend std::ostream &operator<<(std::ostream &, gps_position const &);
    // When the class Archive corresponds to an output archive, the
    // & operator is defined similar to <<.  Likewise, when the class Archive
    // is a type of input archive the & operator is defined similar to >>.
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & degrees;
        ar & minutes;
        ar & seconds;
    }
    int degrees;
    int minutes;
    float seconds;
public:
    gps_position(){}
    gps_position(int d, int m, float s) :
        degrees(d), minutes(m), seconds(s)
    {}

    bool operator==(gps_position const &g) const
    {
        return degrees == g.degrees &&
            minutes == g.minutes &&
            seconds == g.seconds;
    }

    bool operator!=(gps_position const &g) const
    {
        return !(*this == g);
    }
};

void save(gps_position const &g);
void load(gps_position &g);

#endif // GPS_HPP_7D5AF29629F64210BE00F3AF697BA650
gps.cpp
// gps.cpp
#include <fstream>

#include "gps.hpp"

const char *FILENAME = "gps.dat";

std::ostream &operator<<(std::ostream &s, gps_position const &g)
{
    s << "GPS(" << g.degrees << "/" << g.minutes << "/" << g.seconds << ")";
    return s;
}

void save(gps_position const &g)
{
    // create and open a character archive for output
    std::ofstream ofs(FILENAME);
    boost::archive::text_oarchive oa(ofs);
    // write class instance to archive
    oa << g;
    // archive and stream closed when destructors are called
}

void load(gps_position &g)
{
    // create and open an archive for input
    std::ifstream ifs(FILENAME);
    boost::archive::text_iarchive ia(ifs);
    // read class state from archive
    ia >> g;
    // archive and stream closed when destructors are called
}

Теперь добавим в test.cpp код, использующий классы и методы из gps.hpp:

test.cpp
// test.cpp
#include <iostream>
#include <iomanip>
#include "gps.hpp"

int main()
{
    // create class instance
    const gps_position g(35, 59, 24.567f);
    std::cout << "Initial value: " << g << std::endl;
    save(g);

    // ... some time later restore the class instance to its orginal state
    gps_position newg;
    load(newg);
    std::cout << "After load: " << newg << std::endl;

    if (g != newg)
    {
        std::cerr << "ERROR: Loaded object differs from the saved one" << std::endl;
        return 1;
    }

    std::cout << "Congratulations! GPS object was successfully saved and then loaded" << std::endl;
    return 0;
}

И наконец, добавим файлы в Android.mk и укажем, что мы хотим использовать библиотеку Boost Serialization:

Android.mk
# Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE           := test-boost
LOCAL_SRC_FILES        := test.cpp gps.cpp
LOCAL_STATIC_LIBRARIES := boost_serialization_static
# Or, if you need to link with Boost Serialization library dynamically:
#LOCAL_SHARED_LIBRARIES := boost_serialization_shared
include $(BUILD_EXECUTABLE)

$(call import-module,boost/1.57.0)

После всех приведенных изменений дерево нашего проекта выглядит так:

.
└── jni
    ├── Android.mk
    ├── Application.mk
    ├── gps.cpp
    ├── gps.hpp
    └── test.cpp

Конечный результат

Готово! Можно проверить, как это все работает. Заметьте, что мы использовали параметр APP_ABI при вызове ndk-build только для того, чтобы уменьшить вывод на консоль; тем не менее, приведенный код работает для всех возможных ABI.

$ ndk-build APP_ABI=armeabi-v7a
[armeabi-v7a] Compile++ thumb: test-boost <= test.cpp
[armeabi-v7a] Compile++ thumb: test-boost <= gps.cpp
[armeabi-v7a] Prebuilt       : libgnustl_shared.so <= <NDK>/sources/cxx-stl/gnu-libstdc++/4.9/libs/armeabi-v7a/thumb/
[armeabi-v7a] Executable     : test-boost
[armeabi-v7a] Prebuilt       : libboost_atomic.so <= <NDK>/sources/boost/1.57.0/libs/armeabi-v7a/
[armeabi-v7a] Install        : libcrystax.so => libs/armeabi-v7a/libcrystax.so
[armeabi-v7a] Install        : test-boost => libs/armeabi-v7a/test-boost
[armeabi-v7a] Install        : libgnustl_shared.so => libs/armeabi-v7a/libgnustl_shared.so

$ adb push libs/armeabi-v7a/libcrystax.so /data/local/tmp
1231 KB/s (537036 bytes in 0.425s)

$ adb push libs/armeabi-v7a/libgnustl_shared.so /data/local/tmp
1234 KB/s (718448 bytes in 0.568s)

$ adb push libs/armeabi-v7a/test-boost /data/local/tmp
840 KB/s (50536 bytes in 0.058s)

$ adb shell 'cd /data/local/tmp && LD_LIBRARY_PATH=/data/local/tmp ./test-boost'
Initial value: GPS(35/59/24.567)
After load: GPS(35/59/24.567)
Congratulations! GPS object was successfully saved and then loaded

Как вы могли заметить, Boost в Crystax NDK просто работает без каких-либо модификаций - это означает, что вы сможете использовать один и тот же код как для мобильных, так и для всех остальных платформ. Более того, Boost в Crystax NDK работает для всех поддерживаемых версий Android, т.е. начиная с версии 2.3 (API level 9).

Впечатляет? Тогда скачайте CrystaX NDK и попробуйте его прямо сейчас!

Back
Home
Map
Back
Home
Map

Наши авторы: