GNU与c++STL的区别与联系

调研场景:

应用背景:
Andrid NDK 一共提供三种类型的c++运行时库

  1. system
    system 运行时指的是 /system/lib/libstdc++.so。
    非完全stl,完全stl需使用上面的libc++。这是与Android发布绑定的库。
    System运行库指的是Android版本里的/system/lib/libstdc++.so,提供基 本的c++运行支持, 提供new/delete支持,仅提供c标准库的c++封装,比如cstdio
  2. None
    没有标准库支持
  3. 可供自由选择的STL
    gnustl、libc++、STLport

重点弄清gnu与libc++stl的一些区别

gnu与libc++的stl都是stl的不同实现,其接口规范都是一致的,但是内部实现不同

简要介绍:

1. gnustl

1.1 简介
gnustl: Android上的GNU C++库,对应GNU/Linux系统中的libstdc++,gnustl是基于SGI的stl基础上进行的开发。

    这个库和GCC仅仅绑定,但是后期不再更新,最新NDK不再支持
    此库和Clang存在部分冲突
    Note: 新版NDK将会删除此库,从NDK16开始,被libc++替代。

   - 静态库:libgnustl_static.a
   - 动态库:libgnustl_shared.so

1.2应用

只能在Application.mk设置,鉴于gnustl_share存在诸多不稳定问题,所以尽量采用gnustl_static替代gnustl_share

(1)gnustl_share : 当使用static library时,相关代码被连接到编译输出库中,这会造成目标文件变大。

 APP_STL := gnustl_shared 
   优点:
   目标文件已经包含所依赖的代码,不受运行环境的影响。
   缺点:
   目标文件变大,不利于复用
   优缺点同标准静态连接库相同。

(2)gnustl_static
动态链接库:依赖代码不编译到目标文件中。

 APP_STL := gnustl_static
   优点:
   目标文件体积小。
   缺点:
   需要动态链接依赖库,由于gnustl是系统库,所以不需要额外输出libgnustl_share.so。但是由于Android系统的碎片化,各个版本的libgnustl_share.so不一致,就导致崩溃或者运行异常问题。

2.libc++
2.1 简介
libc++:是LLVM c++标准库。从NDK r18之后是唯一的STL

      NDK开发无论是静态库还是动态库,libc++都是用NDK里的发布版本打包在应用里:
      动态库直接在apk里带上libc++_shared.so;
      静态库已经把程序需要的STL的代码直接打到应用程序或其所用的native库里。
      port从 r18开始从NDK中被移除)
      
      - 动态库: libc++_shared.so
      -  静态库: libc++_static.a      

2.2应用

(1). lib++_shared

  APP_STL := c++_shared

libc++ 不是系统库。如果使用 libc++_shared.so,必须将其包含在 APK 中。如果使用 Gradle构建应用,此步骤会自动完成。

 共享运行时

如果应用包括多个共享库,应使用 libc++_shared.so。 在 Android 系统中,NDK 使用的 libc++ 与操作系统的 libc++ 是不同的。如此一来,即使应用以旧版 Android 为目标平台,NDK 用户也能获得最新的 libc++功能和问题修复。需要权衡的是,如果使用 libc++_shared.so,就必须将其包含在您的 APK 中。如果使用 Gradle构建应用,此步骤会自动完成。 旧版 Android 的 PackageManager和动态链接器存在错误,导致原生库的安装、更新和加载不可靠。具体而言,如果应用以早于 Android 4.3(Android API r18的 Android 版本为目标,并且使用 libc++_shared.so,必须先加载共享库,再加载依赖于共享库的其他库。

ReLinker 项目能够解决所有已知的原生库加载问题,而且相较于自行编写解决方法,它通常是更好的选择。

(2). lib++_static

  APP_STL := c++_static
静态运行时

如果应用的所有原生代码均位于一个共享库中,建议使用静态运行时。这样可让链接器最大限度内联和精简未使用的代码,使应用达到最优化状态且文件最小巧。这样做还能避免旧版 Android 中的 PackageManager 和动态链接器出现错误,此类错误可导致处理多个共享库变得困难,且容易出错。

然而,在 C++ 中,在单一程序中定义多个相同函数或对象的副本并不安全。这是 C++ 标准中提出的单一定义规则的一个方面。

如果使用静态运行时(以及一般静态库),很容易在不经意间破坏这条规则。例如,以下应用就破坏了这一规则:

# Application.mk
APP_STL := c++_static
# Android.mk

include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo.cpp
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.cpp
LOCAL_SHARED_LIBRARIES := foo
include $(BUILD_SHARED_LIBRARY)

在这种情况下,包括全局数据和静态构造函数在内的 STL
将同时存在于两个库中。此应用的运行时行为未定义,因此在实际运行过程中,应用会经常崩溃。其他可能存在的问题包括:

内存在一个库中分配,而在另一个库中释放,从而导致内存泄漏或堆损坏。 libfoo.so 中引发的异常在 libbar.so 中未被捕获,从而导致应用崩溃。 std::cout 的缓冲未正常运行。将静态运行时链接至多个库,除了会导致行为问题,还会在每个共享库中复制代码,从而增加应用的大小。

一般情况下,只有在应用中有且只有一个共享库时,才能使用 C++ 运行时的静态变体。

 虽然都是LLVM的c++ STL,此处NDK里的libc++不是Android源码中编译出的c++系
 统STL(libc++.so),此处的libc++是基于NDK开发时,NDK中已经编译好的库。如
 果NDK开发的应用用到libc++_shared.so, .so会被打包到编译出的APK里;用到
 libc++_static.a, .a里被用到的程序会被打到使用者的程序中的。也就是发布应用
 时,会带着stl一起发布,不依赖Android版本内部的stl。

3.STLport
由STLport项目编写的C++标准库的第三方实现,自08年以来就不怎么被应用,与gnustl一样STLport被移除

   - 静态库:stlport_static.a
   - 动态库:stlport_shared.so                                                                                

4.ndk-build

ndk-build 的默认值为 none。可以使用 Application.mk 文件中的 APP_STL 变量指定
c++_shared、c++static、none 或 system。
ndk-build 仅允许为应用选择一个运行时,并且只能在 Application.mk 中进行选择。

例如:

APP_STL := c++_shared

5.直接使用 clang

如果构建系统中直接使用 clang,则 clang++ 将默认使用 c++_shared。如需使用静态变体,请将
-static-libstdc++ 添加至链接器标志中。请注意,尽管由于历史原因该选项使用的名称是“libstdc++”,但也适用于 libc++

6.clang 与Gcc的简单比较

Clang 是一个 C、C++、Objective-C 和 Objective-C++编程语言的编译器前端,采用底层虚拟机(LLVM)作为后端。至于为什么有了 GCC 还要开发 Clang?
(1)Clang 采用的是 BSD 协议的许可证,而 GCC 采用的是 GPL协议,显然前者更为宽松;
(2)Clang 是一个高度模块化开发的轻量级编译器,编译速度快、占用内存小、有着友好的出错提示

7.Andriod 移除gnustl选择libc++的原因

( 1)、clang库 由于 Android OS 放弃GCC转向了 Clang 编译器, 所以 NDK 将移除GCC, 所以建议用Clang编译程序;同时libc++是针对clang编译器特别重写的C++标准库,而gnustl会和clang库产生冲突
(2)、版权原因 标准的GNU STL是由libstdc++提供的,本身虽然是GPL,但是只要不修改它的代码,就可以自由使用。而在Android平台上,因为很多适配上的问题,不经修改的libstdc++是无法直接使用的,所以NDK无法直接提供
(3)、同时GNU STL功能全但是大,有一些不太常用的功能开销

8、Andriod没有自带STL的原因

C++ ABI兼容性一直无法保证。目前Android NDK 只能保证C API向前向后兼容。C++API的兼容性在官方roadmap中,但尚未排期。那么STL的兼容性保证就要更晚了。相比C API,C++ API保持好ABI兼容性的难度很大。

9.其他的一些注意事项

9.1CMake设置:

CMake 默认设置为 C++ Clang 默认设置的版本(目前为 C++14),需要将 CMakeLists.txt 中的标准CMAKE_CXX_STANDARD 设置为适当的值,才能使用 C++17 或更高版本的功能。

ndk-build 默认情况下仍将此决定留给 Clang,因此 ndk-build 用户应使用 APP_CPPFLAGS 来添加-std=c++17 或任何所需内容

9.2 不要在ndk中使用系统预编译好的C++库

NDK的libc++的C++11命名空间为std::__ndk1
NDK的gnustl的C++11命名空间为std
安卓系统的libc++的C++11命名空间为std::__1

由于命名空间不一样,所以无法在NDK中使用安卓系统预编译好的C++库

源码分析:

gnu stl 详细的英文介绍文档
https://gcc.gnu.org/onlinedocs/libstdc++/

sgi stl 详细的英文介绍文档
http://www.sgi.com/tech/stl/

llvm stl 详细的英文介绍文档
http://libcxx.llvm.org/

stlport 详细的英文介绍文档
http://www.stlport.org/


一些编译问题的解决方案:

一 、gnustl_share崩溃解决方案
统一采用NDK10中的gnustl_share的库,然后在Android.mk和代码中显示加载此库。具体步骤如下:

使用NDKr9c或者r10e

libgnustl_shared.so <= /sources/cxx-stl/gnu-libstdc++/4.8/libs/armeabi/

显示加载复制出来的gnustl_share

System.loadLibrary("gnustl_shared")

二、Android NDK APP_STL gnustl_shared is no longer supported 报错处理

错误原因:APP_STL gnustl_shared 不再被ndk支持。

修改 Applicaiton.mk 中的相关配置

1. APP_STL  := gnustl_static 改为 APP_STL := c++_static;
2.删除NDK_TOOLCHAIN or NDK_TOOLCHAIN_VERSION;

对于cmake编译:

1.删除 ANDROID_TOOLCHAIN

对于独立的toolchains

用clang/clang++ binaries 代替 gcc/g++.

补充libc++和libstdc++:

libc++和libstdc++的区别或者比较

一、lib c++

libc++是针对clang编译器特别重写的C++标准库
libc++ 是 C++ 标准库的实现,面向 C++11、C++14 及更高版本
详细英文介绍 见链接4

优点

ABI 与 gcc 的 libstdc++ 兼容,用于一些低级功能,例如异常对象、rtti 和内存分配
快速执行、占用内存少、快速编译

支持的平台
在这里插入图片描述

二、libstdc++
libstdc++针对gcc编译器的c++标准库;
详细英文介绍 见链接5

优点

clang对libstdc++的支持更好,执行速度更快(这一点不一定,只是看到有人这样说

缺点

libstdc++ 在构建时考虑了效率,因此很少或不执行 C++ 标准不需要的错误检查。这意味着错误地使用 C++
标准库的程序将表现出不可移植的行为,甚至可能无法预测,因为它们会进入特定于实现的或未定义的行为。为了在这些错误出现问题之前检测其中的一些错误,libstdc++提供了一种调试模式,该模式提供了对库设施的额外检查,并且一旦检测到错误,就会通过向标准发出问题描述来报告使用 libstdc++的错误。错误并中止程序。此调试模式可用于 GCC 3.4.0 及更高版本。

三、一些注意事项

不同操作系统使用的本地库有所区别,在GNU / Linux上一般使用libstdc ++,在Mac OS X上一般使用libc ++

二者使用取决于编译器优先集成哪个,一般libstdc++兼容性好

libstdc++跟clang++一起用的时候可能会有各种bug,比如前段时间出过libstdc++的variant不能在clang++上编译,以及最近libstdc++的filesystem::directory_iterator在clang++编译后会提示找不到符号

补充LLVM和Clang 的关系:

 Clang 是一个 C、C++、Objective-C 和 Objective-C++ 编程语言的编译器前端,采用底层虚拟机(LLVM)作为后端。

传统编译器

传统编译器的工作原理,基本上都是三段式的,可以分为前端、优化器和后端。前端负责解析源代码,检查语法错误,并将其翻译为抽象的语法树;优化器对这一中间代码进行优化,试图使代码更高效;后端则负责将优化器优化后的中间代码转换为目标机器的代码,这一过程后端会最大化的利用目标机器的特殊指令,以提高代码的性能

LLVM

广义的 LLVM 指的是一个完整的 LLVM 编译器框架系统,包括了前端、优化器、后端、众多的库函数以及很多的模块;而狭义的 LLVM则是聚焦于编译器后端功能的一系列模块和库,包括代码优化、代码生成、JIT 等。

LLVM与clong
在这里插入图片描述

参考链接
链接:
link1.
link2.
link3.
link4.
link5.
link6.
link7.

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值