android导入本地图书_美丽的本地图书馆

android导入本地图书

I’m obsessed with nice APIs. Not just APIs however, also in making the overall experience of using a library as good as possible. For Python there are quite a few best practices around by now but it feels like there is not really a lot of information available about how to properly structure a native library. What do I mean by native library? Essentially a dylib/DLL/so.

我迷上了不错的API。 但是,不仅要使用API​​,还应尽可能提高使用库的整体体验。 对于Python来说,目前有很多最佳实践,但是似乎没有足够的有关如何正确构建本机库的信息。 我的原生库是什么意思? 本质上是dylib / DLL / so。

Since I’m currently spending more time on C and C++ than Python at work I figured I might take the opportunity and collect my thoughts on how to write proper shared libraries that do not annoy your users.

由于我目前在C和C ++上花费的时间比在工作上花费的时间多,所以我想我可以借此机会并收集有关如何编写适当的共享库的想法,这些共享库不会惹恼用户。

共享还是静态? (Shared or Static?)

This post almost entirely assumes that you are building a DLL or shared library and not something you link statically. While it sounds like a statically and dynamically linked library are essentially the same thing where the only difference is how you link against it, there is much more to it.

这篇文章几乎完全假设您正在构建DLL或共享库,而不是静态链接的东西。 听起来静态和动态链接的库本质上是相同的东西,唯一的区别是链接的方式不同,但还有更多。

With a dynamically linked library you have much better control over your symbols. Dynamically linked libraries also work much better between different programming languages. Nothing stops you from writing a library in C++ and then using it in Python. In fact, that’s exactly how I recommend doing unittests against such libraries. More about that later.

使用动态链接库,您可以更好地控制符号。 动态链接库在不同的编程语言之间也可以更好地工作。 没有什么可以阻止您使用C ++编写库,然后在Python中使用它。 实际上,这正是我建议对此类库进行单元测试的方式。 以后再说。

哪种语言? (Which Language?)

So you want to write a library that compiles into a DLL or something of that sort and it should be somewhat platform independent. Which languages can you actually use there? Right now you can pick between C and C++ and soon you might also be able to add Rust to that list. Why not others? C is easy: because that’s the only language that actually defines a somewhat stable ABI. Strictly speaking it’s not the language that defines it, it’s the operating system, but in one way or another, C is the language of choice for libraries and the C calling conventions is the lingua franca of shared libraries.

因此,您想编写一个可编译为DLL或类似文件的库,并且该库应在某种程度上与平台无关。 您在那里实际上可以使用哪些语言? 现在,您可以在C和C ++之间进行选择,并且很快您也可以将Rust添加到该列表中。 为什么不别人呢? C很容易:因为这是唯一定义某种程度上稳定的ABI的语言。 严格说来,它不是定义它的语言,它是操作系统,但是从某种程度上来说,C是库选择的语言,而C调用约定是共享库的通用语言。

“The greatest trick that C ever pulled was convince the world that it does not have a runtime”. I’m not sure where I heard the quote first, but it’s incredibly appropriate when talking about libraries. Essentially C is so commonplace that everything can assume that some basic functionality is provided by the C standard library. That’s the one thing that everybody agreed on that exists. For C++ the situation is more complicated. C++ needs a bunch of extra functionality that is not provided by the C standard library. Primarily it needs support for exception handling. C++ however degrades otherwise nicely to C calling conventions so it’s very easy to still write libraries in it, that completely hide the fact that there is C++ behind the scenes.

“ C曾经拉过的最大诀窍就是让世界相信它没有运行时”。 我不确定首先在哪里听到报价,但这在谈论库时非常合适。 本质上,C非常普遍,以至于所有东西都可以假定C标准库提供了一些基本功能。 那是所有人都同意存在的一件事。 对于C ++,情况更加复杂。 C ++需要一堆C标准库未提供的额外功能。 首先,它需要支持异常处理。 但是,C ++可以很好地降级为C调用约定,因此仍然很容易在其中编写库,这完全掩盖了C ++在幕后的事实。

For other languages that’s not so easy however. Why for instance is it not a good idea to write a library in Go? The reason for this is that Go for needs quite a heavy runtime that does garbage collection and provides a scheduler for it’s coroutines. Rust is getting closer to not having any runtime requirements besides the C standard library which will make it possible to write libraries in it.

对于其他语言,这并不是那么容易。 例如,为什么在Go中编写一个库不是一个好主意? 原因是Go for需要大量的运行时来执行垃圾回收并为其协同程序提供调度程序。 除了C标准库之外,Rust几乎没有任何运行时要求,这使得可以在其中编写库。

Right now however, C++ is most likely the language you want to use. Why not C? The reason for this is that Microsoft’s C compiler is notoriously bad at receiving language updates and you would otherwise be stuck with C89. Obviously you could just use a different compiler on Windows but that causes a whole bunch of problems for the users of your library if they want to compile it themselves. Requiring a tool chain that is not native to the operating system is an easy way to alienate your developer audience.

但是,现在,C ++很可能是您要使用的语言。 为什么不C? 原因是众所周知,Microsoft的C编译器在接收语言更新方面很差,否则您将被C89所困扰。 显然,您可以在Windows上使用其他编译器,但是如果库用户希望自己编译它,则会给您带来很多问题。 要求不是操作系统本身的工具链是疏远开发人员受众的一种简便方法。

I would however generally recommend to a very C like subset of C++: don’t use exceptions, don’t use RTTI, don’t build crazy constructors. The rest of the post assumes that C++ is indeed the language of choice.

但是,我通常会向非常像C的C ++子集推荐:不要使用异常,不要使用RTTI,不要构建疯狂的构造函数。 文章的其余部分假定C ++确实是首选语言。

公共标题 (Public Headers)

The library you’re building should ideally have exactly one public header file. Internally go nuts and create as many headers as you want. You want that one public header file to exist, even if you think your library is only ever going to be linked against something that is not C. For instance Python’s CFFI library can parse header files and build bindings out of that. People of all languages know how headers work, they will have a look at them to build their own bindings.

理想情况下,您正在构建的库应该只有一个公共头文件。 内部发疯,并根据需要创建任意数量的标题。 您希望存在一个公共头文件,即使您认为您的库只会与非C的东西链接。例如,Python的CFFI库可以解析头文件并从中建立绑定。 各种语言的人都知道标题是如何工作的,他们将研究标题以建立自己的绑定。

What rules are there to follow in headers?

标头中应遵循哪些规则?

标头护卫 (Header Guards)

Each public header that other people use should have sufficiently unique header guards to make sure they can be included multiple times safely. Don’t get too creative with the guards, but also don’t be too generic with them. It’s no fun including a header that has a super generic include guard at the top (like UTILS_H and nothing else). You also want to make sure that there are extern "C" markers for C++.

他人使用的每个公共标头应具有足够独特的标头防护,以确保可以安全地多次包含它们。 不要对守卫太有创造力,但也不要对他们太宽泛。 包含在顶部具有超级通用包含保护的标头(例如UTILS_H等)没什么好玩的 。 您还想确保C ++有外部“ C”标记。

This would be your minimal header:

这将是您的最小标头:

#ifndef YOURLIB_H_INCLUDED
#ifndef YOURLIB_H_INCLUDED
#define YOURLIB_H_INCLUDED
#define YOURLIB_H_INCLUDED
#ifdef __cplusplus
#ifdef __cplusplus
extern extern "C" "C" {
       
{
       
#endif

#endif

/* code goes here */

/* code goes here */

#ifdef __cplusplus
#ifdef __cplusplus
}
}
#endif
#endif
#endif
#endif

出口标记 (Export Markers)

Because you yourself will probably include your header file as well you will need to make sure that there are macros defined that export your functions. This is necessary on Windows and it’s a really good idea on other platforms as well. Essentially it can be used to change the visibility of symbols. I will go into that later, for the time being just add something that looks like this:

因为您自己也可能包含头文件,所以需要确保定义了一些宏来导出您的函数。 在Windows上这是必需的,在其他平台上也是一个好主意。 本质上,它可用于更改符号的可见性。 我将在稍后讨论,暂时添加如下所示的内容:

On Windows it will set YL_API (I used YL as short version for “Your Library” here, pick a prefix that fits you) for DLLs appropriately depending on what flag is set. Whoever includes the header without doing anything fancy before will automatically get __declspec(dllimport) in its place. This is a really good default behavior on Windows. For other platforms nothing is set unless a somewhat recent GCC/clang version is used in which case the default visibility marker is added. As you can see some macros can be defined to change which branch is taken. For instance when you build the library you would tell the compiler to also defined YL_BUILD_SHARED.

在Windows上,它将根据设置的标志适当地为DLL设置YL_API (我在这里将YL用作“ Your Library”的简称,选择适合您的前缀)。 谁在不做任何花哨的情况下包含标头的人都会自动获得__declspec(dllimport)的位置。 这是Windows上非常好的默认行为。 对于其他平台,除非使用了较新的GCC / clang版本,否则不会进行任何设置,在这种情况下,将添加默认可见性标记。 如您所见,可以定义一些宏来更改采用哪个分支。 例如,当您构建库时,您将告诉编译器也定义了YL_BUILD_SHARED

On Windows the default behavior for DLLs has always been: all symbols are not exported default unless marked with __declspec(dllexport). On other platforms unfortunately the behavior has always been to export everything. There are multiple ways to fix that, one is the visibility control of GCC 4. This works okay, but there are some extra things that need to be considered.

在Windows上,DLL的默认行为始终是:默认情况下不会导出所有符号,除非标记为__declspec(dllexport) 。 不幸的是,在其他平台上,行为始终是导出所有内容。 有多种解决方法,一种是GCC 4的可见性控制。这可以正常工作,但是还需要考虑一些其他事项。

The first is that the in-source visibility control is not the silver bullet. For a start the marker will do nothing unless the library is compiled with -fvisibility=hidden. More important than that however is that this will only affect your own library. If you statically link anything against your library, that library might expose symbols you do not want to expose. Imagine for instance you write a library that depends on another library you want to statically link in. This library’s symbols will also be exported from your library unless you prevent that.

首先是源内可见性控件不是灵丹妙药。 首先,除非使用-fvisibility = hidden编译库,否则标记将不执行任何操作。 比这更重要的是,这只会影响您自己的库。 如果您将任何内容静态链接到您的库,则该库可能会公开您不想公开的符号。 例如,假设您编写了一个库,该库依赖于要静态链接的另一个库。该库的符号也将从库中导出,除非您禁止这样做。

This works differently on different platforms. On Linux you can pass --exclude-libs ALL to ld and the linker will remove those symbols automatically. On OS X it’s tricker because there is no such functionality in the linker. The easiest solution is to have a common prefix for all functions. For instance if all your functions start with yl_ it’s easy to tell the linker to hide everything else. You do this by creating a symbols file and then pointing the linker to it with -exported_symbols_list symbols.txt. The contents of this file can be the single line _yl_*. Windows we can ignore as DLLs need explicit export markers.

这在不同平台上的工作方式有所不同。 在Linux上,您可以将--exclude-libs ALL传递给ld ,链接器将自动删除这些符号。 在OS X上,这很麻烦,因为链接器中没有这样的功能。 最简单的解决方案是为所有功能使用通用前缀。 例如,如果您所有的函数都以yl_开头,则很容易告诉链接器隐藏其他所有内容。 为此,您可以创建一个符号文件,然后使用-exported_symbols_list symbol.txt将链接器指向该文件。 该文件的内容可以是_yl_ *单行 。 由于DLL需要明确的导出标记,因此我们可以忽略Windows。

小心包含和定义 (Careful with Includes and Defines)

One thing to be careful about is that your headers should not include too many things. Generally I believe it’s fine for a header to include things like stdint.h to get some common integer types. However what you should not do is being clever and defining types yourself. For instance msgpack had the brilliant idea to define int32_t and a few other types for Visual Studio 2008 because it lacks the stdint.h header. This is problematic as only one library can define those types then. Instead the better solution is to ask the user to provide a replacement stdint.h header for older Visual Studio versions.

要注意的一件事是标题不应该包含太多内容。 通常,我相信标头包含stdint.h之类的东西来获取一些常见的整数类型就可以了。 但是,您不应该做的是聪明并自己定义类型。 例如,msgpack有一个绝妙的主意,因为它缺少stdint.h标头,所以为Visual Studio 2008定义了int32_t和其他一些类型。 这是有问题的,因为那时只有一个库可以定义这些类型。 相反,更好的解决方案是要求用户为旧版本的Visual Studio提供替换的stdint.h标头。

Especially do not ever include windows.h in a library header. That header pulls in so much stuff that Microsoft added extra defines to make it leaner (WINDOWS_LEAN_AND_MEAN, WINDOWS_EXTRA_LEAN and NOMINMAX). If you need windows.h included, have a private header file that’s only included for your .cpp files.

特别是永远不要在库头文件中包含windows.h 。 该标头引入了很多东西,Microsoft添加了额外的定义以使其更精简( WINDOWS_LEAN_AND_MEANWINDOWS_EXTRA_LEANNOMINMAX )。 如果需要包含windows.h ,请使用仅包含.cpp文件的专用头文件。

稳定的ABI (Stable ABI)

Do not put any structs into public headers unless you are 100% sure that you will never change them. If you do want to expose structs and you do want to add extra members later, make sure that the user does not have to allocate that header. If the user does have to allocate that header, add a version or size information as first member into the struct.

除非您100%确保不会更改它们,否则请勿将任何结构放入公共头文件中。 如果确实要公开结构,并且以后确实要添加额外的成员,请确保用户不必分配该标头。 如果用户必须分配该标头,则将版本或大小信息作为第一个成员添加到结构中。

Microsoft generally puts the size of structs into the structs to allow adding members later, but this leads to APIs that are just not fun to use. If you can try to avoid having too many structs in the headers, if you can’t at least try to come up with alternative methods to make the API suck less.

Microsoft通常将结构的大小放入结构中,以允许以后添加成员,但这会导致API使用起来很不好玩。 如果可以避免在标头中包含太多结构,则至少不能尝试提出其他方法来减少API的负担。

With structs you also run into the issue that alignments might differ between different compile

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值