I wanted to see at how C++ vector is made. I found this, Implementation is LLVM compiler https://llvm.org/svn/llvm-project/libcxx/trunk/src/vector.cpp appleclang
src/vector.cpp:
#include "vector"
_LIBCPP_BEGIN_NAMESPACE_STD
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS __vector_base_common<true>;
_LIBCPP_END_NAMESPACE_STD
Implementation https://llvm.org/svn/llvm-project/libcxx/trunk/include/vector appleclang LLVM.
include/vector:
// .. deleted code
template <bool>
class __vector_base_common
{
protected:
_LIBCPP_ALWAYS_INLINE __vector_base_common() {}
_LIBCPP_NORETURN void __throw_length_error() const;
_LIBCPP_NORETURN void __throw_out_of_range() const;
};
template <bool __b>
void
__vector_base_common<__b>::__throw_length_error() const
{
_VSTD::__throw_length_error("vector");
}
template <bool __b>
void
__vector_base_common<__b>::__throw_out_of_range() const
{
_VSTD::__throw_out_of_range("vector");
}
_LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>)
// .. deleted code
template <class _Tp, class _Allocator>
class __vector_base
: protected __vector_base_common<true>
// .. deleted code
class _LIBCPP_TEMPLATE_VIS vector
: private __vector_base<_Tp, _Allocator>
// .. deleted code
I have many questions how a vector is made.. it feel embarrassing to ask even one.. but.. why is __vector_base_common taking bool template parameter? It doesn't seem to use it, and I verified only use in code is __vector_base_common<true>
, the false
value is not used.
Edit: There have been multiple suggestion relating vector<bool>
to this. Only one specialisation of the above bool parameter is used (true
). This is what vector special looks like
template <class _Allocator>
class _LIBCPP_TEMPLATE_VIS vector<bool, _Allocator>
: private __vector_base_common<true>
The difference looks between private versus protected... Is this a space optimisation for vector not needing those throwing member functions? Still I have question why __vector_base_common
needs a template parameter. Is this C++ pattern with a name?
This is an implementation trick so that the library can be either used only as headers, or have a precompiled part.
Some of vector
's member functions do not depend on the template arguments at all; specifically, the helper functions that throw exceptions. It is therefore possible (unlike the parts that depend on the template parameters) to compile them once and put them in a shared library. This is what happens on MacOS, for example.
On the other hand, on platforms where the library is not distributed with the OS, it is more convenient for the user if he doesn't have to distribute the shared library, but can instead use the library as header-only, i.e. include <vector>
and be done with it, without having to add flags to the linker invocation in the build.
This means that you need the code for these functions to be available in the headers, but if you use the shared library variant, it should not actually get compiled when using the header.
The trick presented here is a way to achieve that. First, the implementation is put into a template, so that it may live in the header without generating multiple definition errors. The template in question only has a dummy parameter; the important thing is that it is a template, not that it has any particular parameters. This is a common technique used by header-only libraries.
Now you can use the library header-only. But if you want to use the shared library variant, you actually need to compile the code ahead of time and suppress code generation for the library user. Explicit template instantiation can be used for that.
So you put an extern template declaration in the header:
extern template class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>;
So the header now contains an explicit specialization declaration, suppressing code generation for the template members.
And then you take a source file, put in the explicit instantiation, and compile it to a shared library.
template class _LIBCPP_CLASS_TEMPLATE_INSTANTIATION_VIS __vector_base_common<true>;
Now you have the shared library usage covered, but you destroyed the ability to use the library header-only. To get that back, you need to make the extern template
declaration optional, depending on the usage mode of the library. So you wrap the declaration in a macro whose definition depends on the mode:
_LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>)
This macro is conditionally defined:
#ifdef _LIBCPP_DISABLE_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...)
#endif
#ifndef _LIBCPP_EXTERN_TEMPLATE
#define _LIBCPP_EXTERN_TEMPLATE(...) extern template __VA_ARGS__;
#endif
So if you're in header-only mode (_LIBCPP_DISABLE_EXTERN_TEMPLATE
is defined), the declaration vanishes. If you're in shared library mode, the declaration is there, preventing code generation.
The reason vector<bool>
derives privately from __vector_base_common
is because it doesn't have any derived classes itself that need access to the throw helpers. vector<T>
derives from __vector_base<T>
, and __vector_base<T>
in turn derives from __vector_base_common
; so for vector<T>
to have access to the __vector_base_common
members, __vector_base<T>
must derive from __vector_base_common
as protected
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With