Porting a library that requires RTTI, exception, and STL support

The Android platform provides a C++ runtime library at /system/lib/libstdc++.so. This default runtime library does not provide C++ exception and RTTI. The support for a standard C++ library is also limited. Fortunately, Android NDK provides alternatives to the default C++ runtime library, which makes porting of a large number of existing libraries that require exception, RTTI, and STL support, possible. This recipe discusses how to port a C++ library that requires RTTI, exception, and STL support. You will widely use the boost library as an example.

How to do it...

The following steps describe how to build and use the boost library for Android NDK:

  1. Install a customized Android toolchain with the following command:
    $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --platform=android-9 --install-dir=/tmp/my-android-toolchain

    This should install the toolchain at the /tmp/my-android-toolchain folder.

  2. Create an Android application named PortingBoost with native support. Set the package name as cookbook.chapter8.portingboost. Please refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface for more detailed instructions.
  3. Add a Java file MainActivity.java under the cookbook.chapter8.portingboost package. This Java file simply loads the shared library PortingBoost, and calls the native method naExtractSubject.
  4. Download the boost library from http://sourceforge.net/projects/boost/files/boost/. In this recipe, you will build the boost library 1.51.0. Extract the downloaded archive file to the jni folder. This will create a folder named boost_1_51_0 under the jni folder as follows:
    How to do it...
  5. In a command line shell, go to the boost_1_51_0 directory. Enter the following command:
    $ ./bootstrap.sh
  6. Edit the user-config.jam file under the jni/boost_1_51_0/tools/build/v2 folder. Append the following content to the end of the file. You can refer to http://www.boost.org/boost-build2/doc/html/bbv2/overview/configuration.html for more information about boost configuration:
    NDK_TOOLCHAIN = /tmp/my-android-toolchain ;
    using gcc : android4.6 :
       $(NDK_TOOLCHAIN)/bin/arm-linux-androideabi-g++ :
       <archiver>$(NDK_TOOLCHAIN)/bin/arm-linux-androideabi-ar
       <ranlib>$(NDK_TOOLCHAIN)/bin/arm-linux-androideabi-ranlib
       <compileflags>--sysroot=$(NDK_TOOLCHAIN)/sysroot
       <compileflags>-I$(NDK_TOOLCHAIN)/arm-linux-androideabi/include/c++/4.6
       <compileflags>-I$(NDK_TOOLCHAIN)/arm-linux-androideabi/include/c++/4.6/arm-linux-androideabi
       <compileflags>-DNDEBUG
       <compileflags>-D__GLIBC__
       <compileflags>-DBOOST_FILESYSTEM_VERSION=3
       <compileflags>-lstdc++
       <compileflags>-mthumb
       <compileflags>-fno-strict-aliasing
       <compileflags>-O2
           ;
  7. Try building the boost library with the following command:
    $ ./b2 --without-python --without-mpi  toolset=gcc-android4.6 link=static runtime-link=static target-os=linux --stagedir=android > log.txt &

    This command will execute the boost build in the background. You can monitor the build output by using the following command:

    $ tail -f log.txt

    The build will take some time to finish. It will fail to build some targets. We can examine the errors in the log.txt file.

    The first error is that the sys/statvfs.h file is not found. You can fix this by updating the libs/filesystem/src/operations.cpp file. The updated parts are highlighted as follows:

    #   include <sys/types.h>
    #   include <sys/stat.h>
    #   if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__ANDROID__)
    #     include <sys/statvfs.h>
    #     define BOOST_STATVFS statvfs
    #     define BOOST_STATVFS_F_FRSIZE vfs.f_frsize
    #   else
    #     ifdef __OpenBSD__
    #       include <sys/param.h>
    #     elif defined(__ANDROID__)
    #         include <sys/vfs.h>
    #     endif
    #     include <sys/mount.h>
    #     define BOOST_STATVFS statfs
    #     define BOOST_STATVFS_F_FRSIZE   static_cast<boost::uintmax_t>(vfs.f_bsize)
    #   endif

    The second error is that the bzlib.h file is not found. This is because bzip is available on Android. You can disable bzip by adding the following line at the top of jni/boost_1_51_0/tools/build/v2/user-config.jam:

    modules.poke : NO_BZIP2 : 1 ;

    The third error is that PAGE_SIZE is not declared in this scope. You can fix this by adding the following line to boost_1_51_0/boost/thread/thread.hpp and boost_1_51_0/boost/thread/pthread/thread_data.hpp:

    #define PAGE_SIZE sysconf(_SC_PAGESIZE)
  8. Try building the library again with the same command in step 5. This time the library will build successfully.
  9. Add the mylog.h and PortingBoost.cpp files under the jni folder. The PortingBoost.cpp file contains the implementation for the native method naExtractSubject. The function will match each line of the input string pInputStr with a regular expression using the boost library's regex_match method:
    void naExtractSubject(JNIEnv* pEnv, jclass clazz, jstring pInputStr) {
       std::string line;
       boost::regex pat( "^Subject: (Re: |Aw: )*(.*)" );
       const char *str;
       str = pEnv->GetStringUTFChars(pInputStr, NULL);
       std::stringstream stream;  
       stream << str;
       while (1) {
           std::getline(stream, line);
           LOGI(1, "%s", line.c_str());
           if (!stream.good()) {
             break;
           }
           boost::smatch matches;
           if (boost::regex_match(line, matches, pat)) {
               LOGI(1, "matched: %s", matches[0].str().c_str());
           } else {
             LOGI(1, "not matched");
           }
       }
    }
  10. Add an Android.mk file under the jni folder with the following content:
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := boost_regex
    LOCAL_SRC_FILES := boost_1_51_0/android/lib/libboost_regex.a
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/boost_1_51_0
    include $(PREBUILT_STATIC_LIBRARY)
    include $(CLEAR_VARS)
    LOCAL_MODULE    := PortingBoost
    LOCAL_SRC_FILES := PortingBoost.cpp
    LOCAL_LDLIBS := -llog
    LOCAL_STATIC_LIBRARIES := boost_regex
    include $(BUILD_SHARED_LIBRARY)
  11. Add an Application.mk file under the jni folder with the following content:
    APP_STL := gnustl_static
    APP_CPPFLAGS := -fexceptions
  12. Build and run the project. You can monitor the logcat output with the following command:
    $ adb logcat -v time PortingBoost:I *:S

    The following is a screenshot of the logcat output:

    How to do it...

How it works...

In your sample project, you first built the boost library using the Android toolchain as a standalone compiler. You then used the regex library from boost as a prebuilt module. Note that the boost library requires support for C++ exceptions and STL. Let's discuss how to enable support for these features at Android NDK.

C++ runtime at Android NDK: By default, Android comes with a minimal C++ runtime library at /system/lib/libstdc++.so. The library does not support most C++ standard library functions, C++ exceptions, and RTTI. Fortunately, Android NDK comes with additional C++ runtime libraries that we can use. The following table summarizes the features provided by different runtime libraries at NDK r8:

 

C++ standard library

C++ exceptions

C++ RTTI

system

minimal

No

No

gabi++

minimal

No (yes if NDK r8d or later)

Yes

stlport

yes

No (yes if NDK r8d or later)

Yes

gnustl

yes

yes

Yes

Note

The C++ exceptions have been added to gabi++ and stlport since Android NDK r8d.

The system library refers to the default value that comes with the Android system. There is only a minimal C++ standard library support, and no C++ exceptions and RTTI. The C++ headers supported include the following:

cassert, cctype, cerrno, cfloat, climits, cmath, csetjmp, csignal, cstddef, cstdint, cstdio, cstdlib, cstring, ctime, cwchar, new, stl_pair.h, typeinfo, utility
  • gabi++ is a runtime library, which supports RTTI in addition to the C++ functions provided by the system default.
  • stlport provides a complete set of C++ standard library headers and RTTI. However, C++ exception is not supported. In fact, Android NDK stlport is based on gabi++.
  • gnustl is the GNU standard C++ library. It comes with a complete set of C++ headers, and supports C++ exceptions and RTTI.

    Tip

    The shared library file gnustl is named as libgnustl_shared.so instead of libstdc++.so in other platforms. This is because the name libstdc++.so is used by the system default C++ runtime.

The Android NDK build system allows us to specify the C++ library runtime to link to the Application.mk file. Based on the library type (shared or static) and which runtime to use, we can define APP_STL as follows:

 

Static library

Shared library

gabi++

gabi++_static

gabi++_shared

stlport

stlport_static

stlport_shared

gnustl

gnustl_static

gnustl_shared

In your sample project, add the following line in Application.mk to use the gnustl static library:

APP_STL := gnustl_static

Tip

You can only link a static C++ library into a single shared library. If a project uses multiple shared libraries and all libraries link to a static C++ library, each shared library will include a copy of the library's code in its binary. This will cause some problems, because some global variables used by the C++ runtime library are duplicated.

The sources, headers, and binaries of these libraries can be found at the sources/cxx-stl folder of Android NDK. You can also refer to docs/CPLUSPLUS-SUPPORT.html for more information.

Enable the C++ exception support: By default, all C++ sources are compiled with -fno-exceptions. In order to enable C++ exceptions, you will need to choose a C++ library, which supports exceptions (gnustl_static or gnustl_shared), and do one of the following:

  • At Android.mk, add exceptions to LOCAL_CPP_FEATURES as follows:
    LOCAL_CPP_FEATURES += exceptions
  • At Android.mk, add -fexceptions to LOCAL_CPPFLAGS as follows:
    LOCAL_CPPFLAGS += -fexceptions
  • At Application.mk, add the following line:
    APP_CPPFLAGS += -fexceptions

Enable the C++ RTTI support: By default, C++ sources are compiled with -fno-rtti. In order to enable the RTTI support, you will need to use a C++ library, which supports RTTI, and do one of the following:

  • At Android.mk, add rtti to LOCAL_CPP_FEATURES as follows:
    LOCAL_CPP_FEATURES += rtti
  • At Android.mk, add -frtti to LOCAL_CPPFLAGS as follows:
    LOCAL_CPPFLAGS += -frtti
  • At Application.mk, add -frtti to APP_CPPFLAGS as follows:
    APP_CPPFLAGS += -frtti
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset