Tutorial: How to set up Smart Pointers and TR1 C++ in the Android NDK

Optimus prime is a smart pointer.  Memory leaks?! He'll straight up kick you in the throat! This isn't Go Bots son!

What's the point


If you have ever coded in C or C++ you know you've had a memory leak some time or another. Ownership semantics are best, but sometimes those are muddy and, most of the time, the benefit of automatically reclaimed pointers far outweighs any costs (they are not much in terms of resources).

What are smart pointers?  


These are pointer containers/classes with reference counting and ownership semantics built in.  The easiest to understand and use is shared_ptr, which when all references are gone delete's the object from memory, and as expected calls the objects destructor (if it exists). For example if you create a raw pointer in a function (and don't store a reference to it anywhere else)  you would get a memory leak!


void leak_function()
{
  Sprite* sprite = new Sprite();
  // Use sprite

  // After function ends, we forgot to delete sprite :Z
}

But if we were to use a smart pointer, such as shared_ptr, we no longer have to worry about this.

Note, in this case of just using scope this smart pointer is better.

void function()
{
  std::shared_ptr<sprite> sprite =
    new std::shared_ptr<sprite>(new Sprite());

  // Use sprite

  // After function ends, sprite memory is reclaimed
}

So what happened? Once the shared_ptr went out of scope, the shared_ptr's destructor was called and its reference count was decremented.  Since the reference count was zero, it deleted itself.  Yay! Easy right?

Yeah, but it's easy to remember to delete, just do it before the end of the function.

Michael Bolton always forgets mundane details!


Sure, but there are many cases where you might want to pass a reference around to many containers, and you're not quite sure on the life time.  Especially as your project changes, sometimes you can make a mistake and your newest version has a memory leak, simply because you've forgotten some mundane detail . Using smart pointers is a cure for memory leaks (almost).


History of smart pointers in Android


These smart points sound great you say?  Ever wanted to have smart pointers in the Android NDK? How about the tr1 standards in the C++11 aka C++0x features? These features are great, but it has not been easy to get these into Android in past versions of the NDK.  

In past NDKs we would cross compile libboost for Android which contains much of the smart pointer code the TR1 standard is based on. Boost is a *great* library with loads of conveniences.  However to wrestle boost into Android, you would run into a few compile issues here and there and have to do a little exception handling trickery with the boost namespace.



This is a fine solution, using boost etc. However, I am here today to tell you a way to get all the TR1 standard C++ inclusions that are already part of the Android NDK, with no need for a reliance on a ported library. These features are not enabled in most examples you find on the NET. No problem... I am here to tell you how to grab these suckers and put them to work.

Here all the steps needed here to get you to these great C++ conveniences in your Android NDK code.

Steps to add TR1 Support for your C++ Android project:


  1. Open up jni/Application.mk 
  2. Delete or comment out any current definition of APP_STL and replace with:  APP_STL := gnustl_static 
  3. Delete or comment out any reference in Application.mk to stlport_static (you won't need it anymore) 
  4. Update to newer version of NDK without bug noted here
    1. Must be >= ndk 8d . (A bug in earlier versions of gcc in the ndk causes build issues) 
  5. Add #include   to any files that will use shared_ptr. 
  6. Add to Android.mk: LOCAL_CPPFLAGS =: -std=c++0x

Great now how do I use it?


Well for shared pointers all you need to do is #include <memory>   and reference them like so.

(I like to make a type for all my SharedPtrs):

#include  <memory>
// Your other code etc. class Sprite; //  Smart pointer type for convenience typedef std::shared_ptr<sprite> SpritePtr; Now I have a shared_ptr type definition to use like so:
SpritePtr sprite = SpritePtr(new Sprite());

Special tip:


If you have a type that you NEVER want to have a pointer to and force all instances to be in a smart pointer you can do something like this:

class Sprite
{
  // Sprite definition... blah blah

private:
  Sprite(){}

public:
  SpritePtr createSprite()
  {
     return SpritePtr(new Sprite());
  }
};

This forces all users of the class to obtain a smart pointer instance. It does negate any stack creation benefits, however (unless you override new operator etc), but that's not too important in most cases.

So that's it for now folks.  I hope you have fun not having memory leaks in your Android C++ code!

Comments

  1. You mentioned I don't need boost, but in my external/stlport/stlport/memory, I see the code is only compiled if boost is supported
    #if !defined(_STLP_NO_EXTENSIONS) && defined(_STLP_USE_BOOST_SUPPORT)

    ReplyDelete
    Replies
    1. Yes, but we are not using stlport. Delete any reference to stlport in the Makefile, mentioned above. It isn't needed when using gnustl_static. Then Follow the steps to include c++0.

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. I am building this on the mobile env

    in project/jni/Android.mk

    I have:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    APP_CPPFLAGS += -fno-exception
    APP_STL += gnustl-static

    LOCAL_SRC_FILE := \
    { include file which uses std::tri1::shared_ptr)

    LOCAL_CPPFLAGS := -std=c++0
    ...


    However, when I run ndk-build, it complains unrecognized command line option '-std=c++0'

    Thanks

    ReplyDelete
    Replies
    1. Oh, sorry I had a typo. It should be -std=c++0x

      Which version of the NDK are you using?

      If you're using more modern versions of the NDK, they have a newer version of GCC (maybe 4.7 or greater), you may need to change the option slightly.

      http://gcc.gnu.org/projects/cxx0x.html

      As you can see here, the options (well, namings) are slightly different. They no longer refer to the standard as C++0x, but the full standard name of C++11.

      So now the option is just: -std=c++11

      Try the -std=c++0x first, then if not try the c++11 and let me know. I am running on an older NDK version 8d, but I will update soon and update this tutorial based on that.

      Here's my full Android.mk:

      LOCAL_PATH := $(call my-dir)
      MY_PATH := $(LOCAL_PATH)
      TARGET_ABI := android-4-armeabi

      #include $(call all-subdir-makefiles)

      include jni/libpng/Android.mk
      include jni/Box2D/Android.mk
      include jni/libzip/Android.mk

      include $(CLEAR_VARS)
      LOCAL_PATH := $(MY_PATH)

      LOCAL_MODULE := libmain

      LOCAL_C_INCLUDES := \
      $(LOCAL_PATH)/Box2D \
      $(LOCAL_PATH)/libpng \
      $(LOCAL_PATH)/libzip \

      LOCAL_CFLAGS := \
      -Wall \
      -g3 \
      -ggdb \
      -gstabs+ \
      -DANDROID_NDK \

      LOCAL_CPPFLAGS := \
      -std=c++0x \


      #-DDISABLE_IMPORTGL \

      #---------------------------------------------------------
      # Nonsense, for some reason if I use a wildcard without local
      # path, it doesn't find the source files.
      # If I do it appends LOCAL_PATH to the prefix twice!
      #---------------------------------------------------------
      MY_PREFIX := $(LOCAL_PATH)/../
      MY_LOCAL_SRC_FILES := \
      $(wildcard $(MY_PATH)/*.cpp)

      # This fixes the problem!
      LOCAL_SRC_FILES := \
      $(subst jni/, , $(MY_LOCAL_SRC_FILES))
      #$(MY_LOCAL_SRC_FILES)

      LOCAL_LDLIBS := -lGLESv1_CM -llog -lz
      LOCAL_STATIC_LIBRARIES := libBox2D libzip libpng

      include $(BUILD_SHARED_LIBRARY)


      # Here's my full Application.mk

      APP_OPTIM := debug
      APP_STL := gnustl_static
      #APP_STL := stlport_static
      APP_ABI := armeabi armeabi-v7a
      APP_PLATFORM := android-9

      Delete
    2. By the way, you don't need to use the tr1 namespace anymore. At least not for the gcc_version/libraries/etc we are using:

      You just access the shared_ptr type like so:

      #include < memory >
      class Sprite;
      typedef std::shared_ptr SpritePtr;

      Delete
  4. Do I need to do LOCAL_LDLIBS :+ /libgnustl_static.a?

    ReplyDelete
    Replies
    1. No, this should be enough (in the Application.mk, but you can probably put in the Android.mk file instead)

      APP_STL := gnustl_static

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Thanks. I got the shared_ptr compiled now

    ReplyDelete
  7. Smart pointers contribute a great deal to exception safety and they do make sense also in local scopes. Imagine you create an object with new, do something for a few lines and delete the object before leaving the function. If these few lines in between throw an exception the delete instruction may never get called, essentially leaking memory. The use of smart pointers avoids this since they themselves are allocated on the stack and even if an exception gets thrown it is guaranteed that their destructor method gets called no matter what happens. In that case the smart pointer takes proper care of deleting the object.

    ReplyDelete
    Replies
    1. Very true and great point! Thanks for reading my article.

      Delete
  8. Thanks so much man! This was a huge help.

    ReplyDelete
    Replies
    1. Your entirely welcome sir! Thanks for reading my article. More to come very soon.

      Delete

Post a Comment

Popular posts from this blog

Development on Android NDK Tutorial - Gluing C++ Native Code to Java

Creating a Fake Refraction, Bubble Shader for your 2D Godot Game!

Godot Tutorial: How to read all files in EVERY sub directory (Breadth/Depth first Search)