Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is going on with __vector_base_common?

Tags:

c++

libc++

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?

like image 649
HenryF1 Avatar asked Apr 26 '18 19:04

HenryF1


1 Answers

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.

like image 118
Sebastian Redl Avatar answered Nov 15 '22 06:11

Sebastian Redl