.. _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 Description
-----------------

.. 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() { /* ... */ }
        };

