diff --git a/doc/internals/export.rst b/doc/internals/export.rst new file mode 100644 index 0000000000000000000000000000000000000000..7e997f364d43898098c23d2d8b5075ddd69d4971 --- /dev/null +++ b/doc/internals/export.rst @@ -0,0 +1,134 @@ +.. _export: + +Exporting Symbols +================= + +The Arbor libraries are compiled with `hidden visibility <https://gcc.gnu.org/wiki/Visibility>`_ by +default which strips a compiled library of all symbols not explicitly marked as visible. Note that +the term *visibility* here refers to the symbol visibility exposed to the linker and not to language +*access* specifier such as public/private. Arbor provides a couple of macros to make functions and +classes visible which are defined in the header file ``export.hpp`` in each library's include +directory, i.e. ``include/arbor/export.hpp``. These header files are generated at configure time +based on the build variant, compiler and platform. + +By default, hidden/visible symbols will affect **shared** libraries directly. Since the linker is +responsible for making symbols hidden, the visibility settings are not applied to **static** +libraries immediately, as static libraries are generated by bundling object files with the archiver +(standard in cmake for ``STATIC`` libraries). In principle, the linker would be able to generate +relocatable output instead (``ld -r``) by incrementally linking object files into one output file +(sometimes called partial linking) which would apply proper visibility to static libraries. +However, we currently do not handle this case in our build scripts as it is not not nativley +`supported by cmake yet <https://gitlab.kitware.com/cmake/cmake/-/issues/16977>`_. + +.. Note:: + When linking an application with **static** Arbor libraries the linker may issue warnings (particularly on macos). Thus, if you encounter problems, try building shared Arbor libraries (cmake option ``-DBUILD_SHARED_LIBS=ON``) instead. + +Macro Descripiton +----------------- + +.. c:macro:: ARB_LIBNAME_API + + Here "``LIBNAME``" is a placeholder for the library's name: ``ARB_ARBOR_API`` for the core Arbor + library, ``ARB_ARBORIO_API`` for Arborio, etc. This macro is intended to annotate functions, + classes and structs which need to be accessible when interfacing with the library. Note that it + expands to different values when Arbor is being built vs. when Arbor is being used by an + application. + + Below we list the places where the macro needs to be added or can be safely omitted (we assume + all of the symbols below are part of the public/user-facing API). In general, annotation is + required + + * for **declarations in header files** which are not definitions + * for **definitions** of functions, friend functions and (extern) variables **in source files** + + Members and member functions of already visible classes will be visible, as well, without + further annotation, except for friend function (see below). + + Implementation details and internal APIs may not need annotation as long as they do not require + visibility across the library boundary (though some annotations are required for unit test + purposes). Exception classes and type-erased objects need special annotation, see + :c:macro:`ARB_SYMBOL_VISIBLE`. + + .. code-block:: cpp + :caption: header.hpp + + #include <arbor/export.hpp> + + // free function declaration + ARB_ARBOR_API void foo(); + + // free function definition + inline void bar(int i) { /* ... */ } + + // function template definition + template<typename T> + void baz(T i) { /* ... */ } + + // class definition + // class member declaration + class ARB_ARBOR_API A { + A(); + friend std::ostream& operator<<(std::ostream& o, A const & a); + }; + + // class definition + // class member definition + class B { + B() { /* ... */ } + }; + + // template class definition + // class member definition + template<typename T> + class C { + C() { /* ... */ } + }; + + // (extern) variable declarations + ARB_ARBOR_API int g; + ARB_ARBOR_API extern int h; + + + .. code-block:: cpp + :caption: source.cpp + + // free function definition + ARB_ARBOR_API void foo() { /* ... */ } + + // class member definition (will be visible since A is visible) + A::A() { /* ... */ } + + // friend function definition + ARB_ARBOR_API std::ostream& operator<<(std::ostream& o, A const& a) { /* ... */ } + + // (extern) variable definitions + ARB_ARBOR_API int g = 10; + ARB_ARBOR_API int h = 11; + + +.. c:macro:: ARB_SYMBOL_VISIBLE + + Objects which are type-erased and passed across the library boundaries sometimes need runtime + type information (rtti). In particular, exception classes and classes stored in ``std::any`` or + similar need to have the correct runtime information attached. Hidden visibility strips away + this information which leads to all kind of unexpected behaviour. Therefore, all such classes + must be annotated with this macro which guarantees that the symbol is always visible. This also + applies for classes (or structs) which are entirely defined inline. Note, one must not use + :c:macro:`ARB_LIBNAME_API` for these cases. + + .. code-block:: cpp + :caption: header.hpp + + #include <arbor/export.hpp> + + // exception class defintion and class member definition + class ARB_SYMBOL_VISIBLE some_error : public std::runtime_error { + some_error() { /* ... */ } + }; + + // class defintion and member defintion + // class D will be type-erased and restored by an any_cast or similar + class ARB_SYMBOL_VISIBLE D { + D() { /* ... */ } + }; + diff --git a/doc/internals/index.rst b/doc/internals/index.rst index cb7a844720465c16c951c226144a08c2263439e2..dbbfdbdf5ab72d026b6dffefc14604bd28affa92 100644 --- a/doc/internals/index.rst +++ b/doc/internals/index.rst @@ -9,6 +9,7 @@ Here we document internal components of Arbor. These pages can be useful if you' :caption: Arbor Internals: :maxdepth: 2 + export util simd_api extending_catalogues