Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static or shared C++ runtime for 3rd-party Java library #796

Open
afjoseph opened this issue Sep 18, 2018 · 7 comments

Comments

3 participants
@afjoseph
Copy link

commented Sep 18, 2018

Description

I'm compiling a 3rd-party Java library for Android that uses JNI. I read the relevant pages on adding C++ support on developer.android but I'm still confused about a couple of issues regarding C++ STL runtime that I was hoping I could clear up here:

1- My library has no control over the app it will be embedded in, so I don't know if there will be other libraries that might use a static/shared STLs. If I use a static C++ runtime with ANDROID_STL=c++_static, is it safe, or should I have to worry about another library that could be using something like gnustl_static which might conflict with mine?

2- If I use a shared C++ runtime with ANDROID_STL=c++_shared, is it a guarantee that a specific element in the STL will use the libc++ runtime or could it be possible to use gnustl if it doesn't exist? For example, If I was using std::string with a shared c++ runtime (c++_shared) in an app that has another library of gnustl_static, will my std::string implementation be taken from libc++ or gnustl?

Ideally, I'd like to have a very stripped down version of a static c++ runtime with (c++_static) that only includes std::vector, std::string and std::map. I was actually planning to use something like -ffunction-sections as described here and #768.

Please advise and thank you.

Environment Details
Pkg.Desc = Android NDK
Pkg.Revision = r15c
Android Studio = 3.1.2
system: cmake
Host OS: Arch Linux ($ uname -r % 4.18.5-arch1-1-ARCH)
Compiler: Clang++
STL: c++_static/c++_shared
API: 27

@DanAlbert

This comment has been minimized.

Copy link
Member

commented Sep 18, 2018

As you've discovered, there's some nuance here and I'm having some trouble forming a simple answer. Bear with me :)

The first thing to note is that there's no difference between the static and shared variant of the STL when building a static library. Static libraries are not linked, and static vs shared STL is a link-time decision. You do have to name one of them for the build system, but it's not a real decision for a static library; the actual choice is deferred to the user of the static library.

Second: yes, if you use libc++ your users must use libc++. If you use gnustl, your users must use gnustl. If you want your users to have both options you need to provide two libraries.

The answer is pretty straightforward if you and your users are both using at least NDK r16: use libc++ shared. gnustl is no longer relevant at this point. If you ship a shared library you should use the shared STL because to do otherwise gets into the multiple STL issues you read about.

To answer your specific questions in case the above doesn't clear things up:

1- My library has no control over the app it will be embedded in, so I don't know if there will be other libraries that might use a static/shared STLs. If I use a static C++ runtime with ANDROID_STL=c++_static, is it safe, or should I have to worry about another library that could be using something like gnustl_static which might conflict with mine?

As above, you must distribute both a libc++ and gnustl version of your library if you want your users to have that choice.

There's technically a loophole here if you're distributing a shared library and do not use the STL anywhere in your library's interface. This loophole violates ODR so is technically undefined behavior, but it's something people have been doing that mostly works. Link the static version of an STL and use a version script to ensure that they are linked with hidden visibility. i.e.

LIBMYLIB_1_0 {
  global:
    my_api_1;
    my_api_2;
    ...
  local:
    *;
};

Not everything works if you do this. Standard exceptions thrown from your library can not be caught by your users. std::cout will not be properly buffered. Etc.

2- If I use a shared C++ runtime with ANDROID_STL=c++_shared, is it a guarantee that a specific element in the STL will use the libc++ runtime or could it be possible to use gnustl if it doesn't exist? For example, If I was using std::string with a shared c++ runtime (c++_shared) in an app that has another library of gnustl_static, will my std::string implementation be taken from libc++ or gnustl?

It's... tricky. libc++ and gnustl mangle their names differently so they are actually distinct types... but there are a few types within each of them that do mangle the same way (std::exception and friends are this way). If you do this, I suspect exception handling will not work properly.

@DanAlbert DanAlbert closed this Sep 18, 2018

@afjoseph

This comment has been minimized.

Copy link
Author

commented Sep 19, 2018

Thanks @DanAlbert. That cleared up everything.
If I can make a small comment, I really like phrasing of this old document regarding static/shared runtimes as opposed to the current document since it provided a clearer example.

This could just be me since I was misinformed about the situation, so please take my criticism with a heavy grain of salt and thanks again.

@DanAlbert

This comment has been minimized.

Copy link
Member

commented Sep 19, 2018

That does have some good pieces worth cherry picking I think. Will reopen this to track.

@DanAlbert DanAlbert reopened this Sep 19, 2018

@alexcohn

This comment has been minimized.

Copy link

commented Sep 19, 2018

@Obaied, you cite the old document that says,

In other words, if your project requires several shared library modules, then use the shared library variant of your C++ runtime.

This is not exactly true. Let me explain.

  1. It is true that if you control all the shared library modules of your app, the best and easiest choice is to choose a common shared variant of C++ runtime. Furthermore, at the time of this writing, this choice will ultimately be libc++.
  2. If your app uses some 3rd party libraries that require some specific variant of C++ shared runtime, the easiest choice is to use the same runtime for all libraries that you can rebuild.
  3. If your app uses some 3rd party static library that requires static C++ runtime, you can link it with shared C++ runtime. It will simply work, except very special situations. So, we are back in §2.
  4. If your app uses some 3rd party shared library that uses some static C++ runtime, try not communicate with this library from your native code. Hopefully it comes with its own Java wrapper classes. If you let them separate from the rest of your native code, you are essentially back to §1.
  5. If your app uses some 3rd party shared library that requires some static C++ runtime, and you must communicate with it from native code, you should not share C++ objects (including streams and exceptions) with it. Working with it through a C API will probably be OK.
  6. If your app uses some 3rd party shared library that requires some static C++ runtime, and another 3rd party library requires some other C++ runtime, this will hopefully be OK if they know nothing about each other, and only communicate with their Java classes.
  7. If your app uses 3rd party shared libraries that use different shared C++ runtime, you are in a big trouble.

@DanAlbert DanAlbert added the docs label Sep 19, 2018

@DanAlbert

This comment has been minimized.

Copy link
Member

commented Sep 19, 2018

I'm always split between trying to give the simple answer and trying to give all the (probably confusing for people that haven't already been down this road) detail necessary to give the most correct answer like @alexcohn has. Like I said, there's a bunch of nuance, and I'm not sure what the right thing to put in our docs is that doesn't have newcomers running away screaming.

Maybe it'd be best for me to keep the straightforward but incomplete answer in the docs but upload a sample that shows all the gross tricks?

@afjoseph

This comment has been minimized.

Copy link
Author

commented Sep 21, 2018

Thanks @alexcohn for the super detailed and answer in both here and SO. This was a tremendous help.

@DanAlbert, If I can make a humble suggestion, I think this post was much more illuminating that the current docs, so I highly encourage you guys cherrypick some of the answers provided by you and Mr. Cohn.

Thanks again to both of you. This was very helpful.

@alexcohn

This comment has been minimized.

Copy link

commented Sep 21, 2018

I believe that these nuances should not be promoted to the top docs. They may be too confusing for a novice, and not complete for an expert. The level of required detail depends actually on the specific situation.

@DanAlbert DanAlbert added this to Low priority in Triage Sep 28, 2018

@DanAlbert DanAlbert added this to the unscheduled docs milestone Oct 18, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.