As you know from Xcode10, apple dropped support for the libstdc++ library in favor of libc++ library. Both libraries have been available and available to developers since Xcode9 and earlier, although apple has announced that libstdc++ will be deprecated since the Xcode9 era.

C + + standard library

An app that uses C++ related code and class libraries needs to link to the C++ standard library. The C++ standard library is a set of functions and classes based on the C++ language. Its early code is defined in the STD namespace. Most classes are implemented using the template template, which consists of IO streams, string classes, and STLS. The implementation of the standard library is distributed in header files without suffixes (such as vector and most template classes), and some code is stored in the corresponding dynamic library, namely libstdc++. Dylib or libc++. Dylib. Why one standard library is implemented by two dynamic libraries is explained in more detail later.

Version of the C++ specification

A language is never static, and C++ is no different. It will need to be upgraded and improved over time. But C++ is not as irresponsible as Swift, and its standards and specifications are relatively rigorous. Personally, I think the reason is that it is already so large and well-developed that the upgrades are mostly minor tweaks. You may find that many other languages are tailored versions of C++. So it can be said that learning C++, all over the world is not afraid! The following table lists the various versions of C++ :

Year C++ Standard Informal name
1998 ISO/IEC 14882:1998[20] C++98
2003 ISO/IEC 14882:2003[21] C++03
2011 ISO/IEC 14882:2011[22] C++11, C++0x
2014 ISO/IEC 14882:2014[23] C++14, C++1y
2017 ISO/IEC 14882:2017[8] C++17, C++1z
2020 to be determined C++20

Before the C++11 standard came out, the compilers on the market basically supported the C++98 version. Most of the grammar and rules in books or knowledge are based on C++98. C++11 mainly adds: automatic type derivation, threading API support, smart pointer memory management, lamda expressions, STL extensions, etc. (if you want to learn more about these new specifications, see C++11 new features introduction). Each major compiler vendor customizes the specification for its own purposes **(the standard syntax and vendor customization are called dialects)**. At present, the more popular C++ compiler has Microsoft VC++, GNU organization GCC (g++), apple LLVM(clang++) and so on. These vendors more or less tailor or extend the C++ specification and support different versions of C++. Currently, almost all major compilers have full support for the C++11 standard.

Libstdc++. Dylib and libc++. Dylib

As mentioned earlier, there are different versions of C++. Libstdc++. Dylib represents C++98 and libc++. Dylib represents C++11. Libc++ is a new C++ implementation that fully supports the C++11 standard. Xcode10 will no longer support the libstdc++ implementation, but will only support the new libc++ implementation. A static library that previously relied on libstdc++ will be linked in Xcode10 with an unfound link error :Undefined symbols for architecture XXX, as shown below:

Undefined symbols for architecture x86_64:
  "std::__throw_length_error(char const*)", referenced from:
      std::vector<int, std::allocator<int> >::_M_insert_aux(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&) in libcpplib.a(cpplib.o)
  "std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
  "std::allocator<char>::allocator()", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
  "std::string::c_str() const", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
  "std::allocator<char>::~allocator()", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
  "std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
ld: symbol(s) not found for architecture x86_64

Copy the code

You might be thinking that the libstdc++ implementation is supposed to be an upgrade of the libstdc++ implementation, so why are there undefined errors? I will elaborate on the answer later.

libc++abi.dylib

When you look at all the C++ dynamic libraries loaded while a program is running, you’ll find one called libc++abi.dylib. This library mainly supports the implementation of C++ keywords: new/delete, try/catch/throw, typeid, etc. These keywords are not simple keywords, they also carry a certain function. In fact, in order to simplify the use of some languages, some capabilities are often refined into a special keyword, so that the use of these capabilities often do not need to write any code, as long as the corresponding keywords can simplify the implementation of these functions. A typical example besides C++ is the chan keyword in GO. In the case of C++, the system stores the code for these keywords in a library called libc++abi.dylib. Here is a brief overview of what functions are available in libc++abi.dylib:

  1. In C++, heap memory is allocated and destroyed by the new/delete operator, so when new/delete is used to allocate and destroy objects in source code, the compilation phase is converted to two global function calls without overloading the operator:
  void * operator new(size_t size);
  void operator delete(void *p);
Copy the code

The implementation code for these two functions is stored in a dynamic library called libc++abi.

  1. In C++, exceptions are caught and thrown using the try/catch/throw keywords. Therefore, when these keywords are used in source code, they are converted to calls to the following functions at compile time:
extern _LIBCXXABI_FUNC_VIS _LIBCXXABI_NORETURN void __cxa_throw(void *thrown_exception, std::type_info *tinfo, void (*dest)(void *)); // 2.5.3 Exception extern _LIBCXXABI_FUNC_VIS void * __cxa_get_exception_ptr(void *exceptionObject) throw(); extern _LIBCXXABI_FUNC_VIS void * __cxa_begin_catch(void *exceptionObject) throw(); extern _LIBCXXABI_FUNC_VIS void __cxa_end_catch();Copy the code

The implementation code for these functions is also stored in the libc++abi, a dynamic library.

  1. In C++, the class description (RTTI) of an object can be obtained by using the keyword typeid. The class description class of C++ is a type_info class. You can see the name, data members, and function layout of a C++ Class from this Class. The information in type_info is similar to the Class type referred to by OC’s isa. The defined implementation of the type_info class is also stored in the libc++abi, a dynamic library.

As you can see, the dynamic library libc++abi is a core library that supports C++ syntax.

Xcode support and Settings for C++

Projects created in Xcode can choose to use the C++ dialect and the C++ standard library version, Select the type of C++ Dialect to use in the C++ Language Dialect in the Apple clang-language-c ++ group in the project’s Build Settings. The version of the C++ Standard Library you choose to use in the C++ Standard Library.

We can verify the C++ dialect support option with the following code, because support for lamda expressions was introduced in C++11, so you can write a lamda expression in the implementation of a function in a.mm file in your project:

//test.mm
void foo()
{
   auto f = []{ NSLog(@"test"); };
   f();   
}

Copy the code

By default, Xcode supports c++14, so the above code can be compiled. If you change the c++ Language Dialect option to c++98 [-std=c++98], you will find a compilation error:

xxxxxxx\test.mm:52:16: error: expected identifier
    auto f = [] { NSLog(@"test"); };
               ^
1 error generated.
Copy the code

The choice of Dialect and Language type is reflected in the -std= compilation option, which can be seen by looking at the Xcode compilation message details: if the file suffix is.m, then -std= is the option in C Language Dialect. If the file suffix is.mm, then the -std= value is the option in the C++ Language Dialect.

The selection of the C++ Stadard Library option in Xcode affects the version of the linked dynamic Library and the search path for the corresponding header file.

  • If you choose the standard library is libc++. The search path for the header file will be: / Applications/Xcode. App/Contents/Developer/Toolchains/XcodeDefault xctoolchain/usr/include/c + + / v1, And the linked dynamic library is libc++. Dylib.

  • If you choose libstdc++, then the search path for the header file will be: / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneOS platform/Developer/SDKs/iPhoneOS SDK/usr/include/c + + / 2 And the linked dynamic library is libstdc++. Dylib.

The selection of the Standard Library is shown in the -libstd= compilation option. A look at the Xcode compilation message details shows that if a file has a.mm suffix, then the -libstd= values are the C++ Standard Library options.

TBD and libstdc++. TBD are added to the Link Binary With Libraries in the Build Phases of the project. Why is it possible to introduce two libraries that define the same thing in one project? Isn’t there a symbol conflict or a duplicate name error during compilation? However, the fact that there are no symbol collisions is due to a new feature introduced in C++11: the inline namespace.

Inline Namespace

Suppose you define and export the same function or class in two different dynamic libraries, and add dependencies to both dynamic libraries. Once the function of the same name is called in the program, the function is duplicated or an undefined link error is introduced. This problem does not occur with different versions of the C++ standard library :libstdc++ and libc++. You can rely on both libraries in your programs without generating compile link errors. We know that the contents of libc++ are supersets of libstdc++, why do we not report function or class name conflicts when we introduce two libraries together? The answer is that C++11 provides support for inline namespaces. As mentioned earlier, all classes in older versions of the C++ standard library are defined in the STD namespace. When you select libstdc++, you will define the contents of all header files between the _GLIBCXX_BEGIN_NAMESPACE and _GLIBCXX_END_NAMESPACE macros, such as the standard input and output stream object definition fragments in:

_GLIBCXX_BEGIN_NAMESPACE(std)

  extern istream cin;		///< Linked to standard input
  extern ostream cout;		///< Linked to standard output
  extern ostream cerr;		///< Linked to standard error (unbuffered)
  extern ostream clog;		///< Linked to standard error (buffered)

#ifdef _GLIBCXX_USE_WCHAR_T
  extern wistream wcin;		///< Linked to standard input
  extern wostream wcout;	///< Linked to standard output
  extern wostream wcerr;	///< Linked to standard error (unbuffered)
  extern wostream wclog;	///< Linked to standard error (buffered)
#endif
  
_GLIBCXX_END_NAMESPACE

Copy the code

<bits/c++config.h> <bits/c++config.h>

# define _GLIBCXX_BEGIN_NAMESPACE(X) namespace X { 
# define _GLIBCXX_END_NAMESPACE } 
namespace std {
  }
Copy the code

Therefore, it is clear that all classes and functions and variables in the early C++ standard library were defined in the STD namespace.

When you use the libc++ library, you’ll find that all the classes and methods in the header files are defined in _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD. For example, the standard input and output stream objects in the definition fragment:

LIBCPP_BEGIN_NAMESPACE_STD

#ifndef _LIBCPP_HAS_NO_STDIN
extern _LIBCPP_FUNC_VIS istream cin;
extern _LIBCPP_FUNC_VIS wistream wcin;
#endif
#ifndef _LIBCPP_HAS_NO_STDOUT
extern _LIBCPP_FUNC_VIS ostream cout;
extern _LIBCPP_FUNC_VIS wostream wcout;
#endif
extern _LIBCPP_FUNC_VIS ostream cerr;
extern _LIBCPP_FUNC_VIS wostream wcerr;
extern _LIBCPP_FUNC_VIS ostream clog;
extern _LIBCPP_FUNC_VIS wostream wclog;
Copy the code

The definitions of the above two macros can be seen in <__config>, which is expanded as follows:

// For better understanding, I have simplified the following macro and command space definitions#define _LIBCPP_BEGIN_NAMESPACE_STD namespace std {inline namespace __1 {
#define _LIBCPP_END_NAMESPACE_STD } }

namespace std {
  inline namespace __1 {
  }
}

Copy the code

In libc++, all classes, methods, and variables are not defined directly in the STD namespace, but in its subnamespace, STD ::__1. The inline keyword in the subnamespace is a new keyword added to the namespace in C++11: ** Inline Child namespaces can be defined in the parent namespace. Inline Child namespaces can import their names into the parent namespace, so that the names defined in the Child namespaces can be accessed directly from the parent namespace, rather than using the domain qualifier Child::name. ** Here’s an example:

#include <iostream>

void main()
{
     std::__1::cout << "hello1" << std::__1::endl;
     std::cout << "hello2" << std::endl;
}
Copy the code

The standard output stream object cout in C++11 is really defined in the STD ::__1 namespace, but because STD ::__1:: is an inlined child namespace, it can be accessed through the parent namespace STD ::. Because of the use of inline namespaces, it is possible to switch between different versions of the C++ standard library and link to two different versions of the C++ standard library libstdc++. Dylib and libc++. Dylib. Therefore, there will be no symbol duplication and conflict errors! In fact, in C++ namespaces inline keyword is introduced to resolve version compatibility and conflicts. This explains why we get the following error when we introduce a static library that relies on libstdc++. Dylib into Xcode10:

Undefined symbols for architecture x86_64:
  "std::__throw_length_error(char const*)", referenced from:
      std::vector<int, std::allocator<int> >::_M_insert_aux(__gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, int const&) in libcpplib.a(cpplib.o)
  "std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
  "std::allocator<char>::allocator()", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
  "std::string::c_str() const", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
  "std::allocator<char>::~allocator()", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
  "std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()", referenced from:
      -[cpplib testfn] in libcpplib.a(cpplib.o)
ld: symbol(s) not found for architecture x86_64

Copy the code

The reason is that the C++ class in question is defined in the STD :: namespace (because of C++ naming and decorating rules, the name of a method or function is modified to include the namespace in which it resides). However, all symbols in the new version of the C++ standard library are in the STD ::__1 namespace, so the linker will not be able to find the symbol. For example, the standard input stream cin is defined differently in libc++ and libstdc++ :

__ZNSt3__13cinE // this is the modified real name of cin in the libstdc++. Dylib libraryCopy the code

One problem: the inlined child namespaces mentioned earlier are accessible directly through the parent namespace. Why not here? Inline namespaces are only accessible at compile time, but inline namespaces are not recognized at the linking stage, where only modified symbols are recognized. In other words, there is no concept of inline namespaces at the linking stage.

Now that Xcode10 is reporting link errors, how do you solve this problem? There are two ways to do this:

  • One is to recompile the static library you imported, upgrading the standard library that the static library depends on to libc++. Dylib. (Recommended method)
  • One is to copy the old libstdc++. Dylib library to Xcode10.

Xcode10 support for libstdc++

Libstdc++. Dylib is not found in Xcode10, and if the project depends on libstdc++ or the C++ Stadard Library option is set to libstdc++, the following error will be reported:

clang: warning: libstdc++ is deprecated; move to libc++ [-Wdeprecated]
ld: library not found for -lstdc++
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Copy the code

Xcode10 has been analyzed on the two standard library support context, and also a simple introduction as long as the old version of libstdc++. Dylib copy to the new version of the IDE environment can be, the specific method and process you can refer to the following two articles:

Blog.csdn.net/box_kun/art… Blog.csdn.net/u010960265/…

However, this is risky, because Xcode10’s header files for the C++ standard library are based on C++11. Therefore, when you introduce an older version of the C++ standard library, it will compile without error, but it will crash at runtime. In particular, if your static library exposes objects from an old C++ standard library class as interface or function arguments to the outside world, it may cause runtime crashes due to differences in data structures and internal implementations between the old and new versions. In order to completely solve these problems, it is still the best solution to require that your static library code be recompiled in Xcode10.

The reference list

En.wikipedia.org/wiki/C++ blog.csdn.net/ftell/artic… Blog.csdn.net/fengbingchu… Blog.csdn.net/Jxianxu/art…


Welcome to myMaking the addressandJane’s address book