Document number: P0276R0
Project: Programming Language C++
Audience: Core Evolution Working Group
 
Antony Polukhin <antoshkka@gmail.com>, <antoshkka@yandex-team.ru>
 
Date: 2016-03-20

A Proposal to add Attribute [[visible]]

I. Introduction and Motivation

Consider the following shared library source code:

    // libgrph.so

    // Public interface
    bool grph_is_tree(graph* g);
    bool grph_is_directed(graph* g);

    // For internal usage
    void in_depth_visitor(graph* g, function<void(graph::node*)>);
    void in_breadth_visitor(graph* g, function<void(graph::node*)>);

Current C++ Standard lacks an ability to specify, which entities will be exported by shared libraries. Different compilers add extensions to do so, which forces the user to write macro:

    // libgrph.so

    #if MSVC
    #   define EXPORT __declspec(dllexport)
    #else
    #   define EXPORT __attribute__((visibility("default")))
    #endif

    // Public interface
    EXPORT bool grph_is_tree(graph* g);
    EXPORT bool grph_is_directed(graph* g);

    // For internal usage
    void in_depth_visitor(graph* g, function<void(graph::node*)>);
    void in_breadth_visitor(graph* g, function<void(graph::node*)>);

Things are getting even worse if the same header file is used during library compilation and as the header that declares user interface:

    // grph.h

    #if EXPORTING
    #   if MSVC
    #       define API __declspec(dllexport)
    #   else
    #       define API __attribute__((visibility("default")))
    #   endif
    #else
    #   if MSVC
    #       define API __declspec(dllimport)
    #   else
    #       define API
    #   endif
    #endif

    // Public interface
    API bool grph_is_tree(graph* g);
    API bool grph_is_directed(graph* g);

This wording attempts to fix issues mentioned above by providing a single [[visible]] attribute:

    // grph.h

    // Public interface
    [[visible]] bool grph_is_tree(graph* g);
    [[visible]] bool grph_is_directed(graph* g);

II. Impact on the Standard

This proposal does not propose changes to existing headers. It does require change in the core language by adding a new [[visible]] attribute.

III. Design Decisions

A. Single attribute for import and export.

Having a single attribute will simplify user's code, all the import and export related macro will be gone.

It has been publicly mentioned that with the modules feature, having two attributes (__declspec(dllexport) and __declspec(dllimport)) is no longer the case for MSVC. Entities need to be marked as being available for DLL linkage using single attribute and then the linker automatically exports entitiess in modules linked into the library and imports entities otherwise.

Two attributes must not be the case for GCC too, where it is allowed to use __attribute__((visibility("default"))) on import and export.

B. Attribute name.

Attribute name in this proposal is nether [[import]] nor [[export]]. Such names may cause confusion with keywords from modules proposal. Name of the attribute in this proposal is based on GCC's attribute name visibility.

C. Analysis of existing practices.

According to A. Visibility of entities depending on __visible__, linkage and inline existing practice is following:

This proposal attempts to standardize existing practice which is reasonable and well thought trough:

D. Silently ignore visibility attribute on platforms that do not support it.

Silent ignorance of visibility attribute does not cause hard detectable errors on the platforms that do not support obtaining external references at program startup from library file:

Also consider the case, when we have a source file that is used for producing shared and static libraries. If platform has no support for shared libraries we must not break the build of static libraries, so the visibility attribute must be silently ignored in that case.

E. Minimal modifications for the N4567 wording.

There have been a lot of shared library related proposals:

The proposal you're reading attempts to avoid issues of the proposals from above, minifying the changes to the N4567 wording. The proposal you're reading:

IV. Proposed wording relative to N4567

7.6.6 Visible attribute [dcl.attr.visible]

The attribute-token visible specifies that an entity with external linkage [basic.link] may be obtained:

On the platforms that do not support obtaining external references at program startup from library files the visible attribute may be silently ignored. [Note: Program that relies on that attribute shall fail to link because of unsatisfied external references. - end note]

visible shall appear at most once in each attribute-list and no attribute-argument-clause shall be present. The attribute may be applied to following entities with external linkage:

If namespace has the visible attribute, then all the entities with external linkage (except inline functions) of the nemaspace shall be implicitly declared with visible attribute.

If class is declared or defined with visible attribute, then the all the static class data members, non-inline member functions, nested classes and implementation generated type information of the class shall be implicitly declared with visible attribute.

The first declaration shall specify the visible attribute if any other declaration specifies the visible attribute. If an entity is declared with the visible attribute in one translation unit and the same entity is declared without the visible attribute in another translation unit, the program is ill-formed; no diagnostic required.

V. Feature-testing macro

For the purposes of SG10 it is sufficient to check for attribute [[visible]] using __has_cpp_attribute.

V. Revision History

Revision 0:

VI. References

[DISCUSSION] "[std-proposals] Attributes useful for shared objects (import, export, symbol locations)" discussion. Available online at https://groups.google.com/a/isocpp.org/forum/?fromgroups#!topic/std-proposals/jzc7d_MQUZg

[N4567] Working Draft, Standard for Programming Language C++. Available online at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf

[N1400] Toward standardization of dynamic libraries. Available online at http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2002/n1400.html

[N1418] Dynamic Libraries in C++. Available online at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1418.html

[N1428] Draft Proposal for Dynamic Libraries in C++. Available online at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1428.html

[N1496] Draft Proposal for Dynamic Libraries in C++ (Revision 1). Available online at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1496.html

[N1976] Dynamic Shared Objects: Survey and Issues. Available online at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1976.html

[N2015] Plugins in C++. Available online at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2015.pdf

[N2407] C++ Dynamic Library Support. Available online at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2407.html

 

 

VII. Acknowledgements

Sean Middleditch gave very useful comments and suggestions about using a single attribute name for import and export.

VIII. Appendix

A. Visibility of entities depending on __visible__, linkage and inline

Table was generated by program from C. Program to generate visibility table, from B. Library source file

Entity Name GCC-4.6 Import Visibility GCC-4.8 Import Visibility GCC-5.3 Import Visibility Clang-3.5 Import Visibility
    
Calculated Import Visibility = External Linkage && Not Inline function && __visible__ or in __visible__ class
<anonymous>::function False False False False False = False && True && True
<anonymous>::function_inline False False False False False = False && False && True
<anonymous>::function_static False False False False False = False && True && True
<anonymous>::some_class::private_member_function False False False False False = False && True && True
<anonymous>::some_class::private_member_function_inline False False False False False = False && False && True
<anonymous>::some_class::private_member_variable False False False False False = False && True && True
<anonymous>::some_class::private_static_member_function False False False False False = False && True && True
<anonymous>::some_class::private_static_member_function_inline False False False False False = False && False && True
<anonymous>::some_class::private_static_member_variable False False False False False = False && True && True
<anonymous>::some_class::public_member_function False False False False False = False && True && True
<anonymous>::some_class::public_member_function_inline False False False False False = False && False && True
<anonymous>::some_class::public_member_function_inline_not_in_body False False False False False = False && False && True
<anonymous>::some_class::public_member_variable False False False False False = False && True && True
<anonymous>::some_class::public_static_member_function False False False False False = False && True && True
<anonymous>::some_class::public_static_member_function_inline False False False False False = False && False && True
<anonymous>::some_class::public_static_member_variable False False False False False = False && True && True
<anonymous>::variable_extern False False False False False = False && True && True
<anonymous>::variable_noextern False False False False False = False && True && True
<anonymous>::variable_noextern_const False False False False False = False && True && True
<anonymous>::variable_static False False False False False = False && True && True
::base_nonvis_class::private_member_function False False False False False = True && True && False
::base_nonvis_class::private_member_function_inline False False False False False = True && False && False
::base_nonvis_class::private_member_variable False False False False False = False && True && False
::base_nonvis_class::private_static_member_function False False False False False = True && True && False
::base_nonvis_class::private_static_member_function_inline False False False False False = True && False && False
::base_nonvis_class::private_static_member_variable False False False False False = True && True && False
::base_nonvis_class::public_member_function False False False False False = True && True && False
::base_nonvis_class::public_member_function_inline False False False False False = True && False && False
::base_nonvis_class::public_member_function_inline_not_in_body False False False False False = True && False && False
::base_nonvis_class::public_member_variable False False False False False = False && True && False
::base_nonvis_class::public_static_member_function False False False False False = True && True && False
::base_nonvis_class::public_static_member_function_inline False False False False False = True && False && False
::base_nonvis_class::public_static_member_variable False False False False False = True && True && False
::derived_nonvis_class::private_member_function False False False False False = True && True && False
::derived_nonvis_class::private_member_function_inline False False False False False = True && False && False
::derived_nonvis_class::private_member_variable False False False False False = False && True && False
::derived_nonvis_class::private_static_member_function False False False False False = True && True && False
::derived_nonvis_class::private_static_member_function_inline False False False False False = True && False && False
::derived_nonvis_class::private_static_member_variable False False False False False = True && True && False
::derived_nonvis_class::public_member_function False False False False False = True && True && False
::derived_nonvis_class::public_member_function_inline False False False False False = True && False && False
::derived_nonvis_class::public_member_function_inline_not_in_body False False False False False = True && False && False
::derived_nonvis_class::public_member_variable False False False False False = False && True && False
::derived_nonvis_class::public_static_member_function False False False False False = True && True && False
::derived_nonvis_class::public_static_member_function_inline False False False False False = True && False && False
::derived_nonvis_class::public_static_member_variable False False False False False = True && True && False
::function True True True True True = True && True && True
::function_inline False False False False False = True && False && True
::function_static False False False False False = False && True && True
::some_class::private_member_function True True True True True = True && True && True
::some_class::private_member_function_inline False False False False False = True && False && True
::some_class::private_member_variable False False False False False = False && True && True
::some_class::private_nested_class::private_member_function True True True True True = True && True && True
::some_class::private_nested_class::private_member_function_inline False False False False False = True && False && True
::some_class::private_nested_class::private_member_variable False False False False False = False && True && True
::some_class::private_nested_class::private_static_member_function True True True True True = True && True && True
::some_class::private_nested_class::private_static_member_function_inline False False False False False = True && False && True
::some_class::private_nested_class::private_static_member_variable True True True True True = True && True && True
::some_class::private_nested_class::public_member_function True True True True True = True && True && True
::some_class::private_nested_class::public_member_function_inline False False False False False = True && False && True
::some_class::private_nested_class::public_member_function_inline_not_in_body False False False False False = True && False && True
::some_class::private_nested_class::public_member_variable False False False False False = False && True && True
::some_class::private_nested_class::public_static_member_function True True True True True = True && True && True
::some_class::private_nested_class::public_static_member_function_inline False False False False False = True && False && True
::some_class::private_nested_class::public_static_member_variable True True True True True = True && True && True
::some_class::private_static_member_function True True True True True = True && True && True
::some_class::private_static_member_function_inline False False False False False = True && False && True
::some_class::private_static_member_variable True True True True True = True && True && True
::some_class::public_member_function True True True True True = True && True && True
::some_class::public_member_function_inline False False False False False = True && False && True
::some_class::public_member_function_inline_not_in_body False False False False False = True && False && True
::some_class::public_member_variable False False False False False = False && True && True
::some_class::public_nested_class::private_member_function True True True True True = True && True && True
::some_class::public_nested_class::private_member_function_inline False False False False False = True && False && True
::some_class::public_nested_class::private_member_variable False False False False False = False && True && True
::some_class::public_nested_class::private_static_member_function True True True True True = True && True && True
::some_class::public_nested_class::private_static_member_function_inline False False False False False = True && False && True
::some_class::public_nested_class::private_static_member_variable True True True True True = True && True && True
::some_class::public_nested_class::public_member_function True True True True True = True && True && True
::some_class::public_nested_class::public_member_function_inline False False False False False = True && False && True
::some_class::public_nested_class::public_member_function_inline_not_in_body False False False False False = True && False && True
::some_class::public_nested_class::public_member_variable False False False False False = False && True && True
::some_class::public_nested_class::public_static_member_function True True True True True = True && True && True
::some_class::public_nested_class::public_static_member_function_inline False False False False False = True && False && True
::some_class::public_nested_class::public_static_member_variable True True True True True = True && True && True
::some_class::public_nested_class_not_inside::private_member_function True True True True True = True && True && True
::some_class::public_nested_class_not_inside::private_member_function_inline False False False False False = True && False && True
::some_class::public_nested_class_not_inside::private_member_variable False False False False False = False && True && True
::some_class::public_nested_class_not_inside::private_static_member_function True True True True True = True && True && True
::some_class::public_nested_class_not_inside::private_static_member_function_inline False False False False False = True && False && True
::some_class::public_nested_class_not_inside::private_static_member_variable True True True True True = True && True && True
::some_class::public_nested_class_not_inside::public_member_function True True True True True = True && True && True
::some_class::public_nested_class_not_inside::public_member_function_inline False False False False False = True && False && True
::some_class::public_nested_class_not_inside::public_member_function_inline_not_in_body False False False False False = True && False && True
::some_class::public_nested_class_not_inside::public_member_variable False False False False False = False && True && True
::some_class::public_nested_class_not_inside::public_static_member_function True True True True True = True && True && True
::some_class::public_nested_class_not_inside::public_static_member_function_inline False False False False False = True && False && True
::some_class::public_nested_class_not_inside::public_static_member_variable True True True True True = True && True && True
::some_class::public_static_member_function True True True True True = True && True && True
::some_class::public_static_member_function_inline False False False False False = True && False && True
::some_class::public_static_member_variable True True True True True = True && True && True
::variable_extern True True True True True = True && True && True
::variable_noextern True True True True True = True && True && True
::variable_noextern_const False False False False False = False && True && True
::variable_static False False False False False = False && True && True
visible_ns::function True True True True True = True && True && True
visible_ns::function_inline False False False False False = True && False && True
visible_ns::function_static False False False False False = False && True && True
visible_ns::nested_ns::function True True True True True = True && True && True
visible_ns::nested_ns::function_inline False False False False False = True && False && True
visible_ns::nested_ns::function_static False False False False False = False && True && True
visible_ns::nested_ns::some_class::private_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::private_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::private_member_variable False False False False False = False && True && True
visible_ns::nested_ns::some_class::private_static_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::private_static_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::private_static_member_variable True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_member_function_inline_not_in_body False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_member_variable False False False False False = False && True && True
visible_ns::nested_ns::some_class::public_nested_class::private_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class::private_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class::private_member_variable False False False False False = False && True && True
visible_ns::nested_ns::some_class::public_nested_class::private_static_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class::private_static_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class::private_static_member_variable True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class::public_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class::public_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class::public_member_function_inline_not_in_body False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class::public_member_variable False False False False False = False && True && True
visible_ns::nested_ns::some_class::public_nested_class::public_static_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class::public_static_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class::public_static_member_variable True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::private_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::private_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::private_member_variable False False False False False = False && True && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::private_static_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::private_static_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::private_static_member_variable True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::public_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::public_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::public_member_function_inline_not_in_body False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::public_member_variable False False False False False = False && True && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::public_static_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::public_static_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_nested_class_not_inside::public_static_member_variable True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_static_member_function True True True True True = True && True && True
visible_ns::nested_ns::some_class::public_static_member_function_inline False False False False False = True && False && True
visible_ns::nested_ns::some_class::public_static_member_variable True True True True True = True && True && True
visible_ns::nested_ns::variable_extern True True True True True = True && True && True
visible_ns::nested_ns::variable_noextern True True True True True = True && True && True
visible_ns::nested_ns::variable_noextern_const False False False False False = False && True && True
visible_ns::nested_ns::variable_static False False False False False = False && True && True
visible_ns::some_class::private_member_function True True True True True = True && True && True
visible_ns::some_class::private_member_function_inline False False False False False = True && False && True
visible_ns::some_class::private_member_variable False False False False False = False && True && True
visible_ns::some_class::private_static_member_function True True True True True = True && True && True
visible_ns::some_class::private_static_member_function_inline False False False False False = True && False && True
visible_ns::some_class::private_static_member_variable True True True True True = True && True && True
visible_ns::some_class::public_member_function True True True True True = True && True && True
visible_ns::some_class::public_member_function_inline False False False False False = True && False && True
visible_ns::some_class::public_member_function_inline_not_in_body False False False False False = True && False && True
visible_ns::some_class::public_member_variable False False False False False = False && True && True
visible_ns::some_class::public_static_member_function True True True True True = True && True && True
visible_ns::some_class::public_static_member_function_inline False False False False False = True && False && True
visible_ns::some_class::public_static_member_variable True True True True True = True && True && True
visible_ns::variable_extern True True True True True = True && True && True
visible_ns::variable_noextern True True True True True = True && True && True
visible_ns::variable_noextern_const False False False False False = False && True && True
visible_ns::variable_static False False False False False = False && True && True

B. Library source file

// main.cpp
#define __visible__ __attribute__((visibility("default")))

#define NONCLASS_ENTITIES(prefix)                                           \
    /*variables*/                                                           \
    static  __visible__ int prefix ## __variable_static = 0;                \
            __visible__ int prefix ## __variable_noextern = 0;              \
            __visible__ const int prefix ## __variable_noextern_const = 0;  \
    extern  __visible__ int prefix ## __variable_extern;                    \
    int prefix ## __variable_extern = 0;                                    \
                                                                            \
    /*functions*/                                                           \
    __visible__ inline  int prefix ## __function_inline() { return 0; };    \
    __visible__ static  int prefix ## __function_static() { return 0; };    \
    __visible__         int prefix ## __function() { return 0; };           \
    /**/

#define CLASS_MEMBER_ENTITIES(prefix)                                                   \
    public:                                                                             \
        static  int prefix ##   __public_static_member_function_inline() { return 0; }  \
        static  int prefix ##   __public_static_member_function();                      \
        static  int prefix ##   __public_static_member_variable;                        \
                int prefix ##   __public_member_function_inline() { return 0; }         \
        inline  int prefix ##   __public_member_function_inline_not_in_body();          \
                int prefix ##   __public_member_function();                             \
                int prefix ##   __public_member_variable;                               \
    private:                                                                            \
        static  int prefix ##   __private_static_member_function_inline() { return 0; } \
        static  int prefix ##   __private_static_member_function();                     \
        static  int prefix ##   __private_static_member_variable;                       \
                int prefix ##   __private_member_function_inline() { return 0; }        \
                int prefix ##   __private_member_function();                            \
                int prefix ##   __private_member_variable;                              \
    /**/

#define CLASS_MEMBER_ENTITIES_DEFINITION(prefix)                                \
    int prefix ## __public_static_member_function() { return 0; }               \
    int prefix ## __public_static_member_variable = 0;                          \
    int prefix ## __public_member_function() { return 0; }                      \
    int prefix ## __public_member_function_inline_not_in_body() { return 0; }   \
    int prefix ## __private_static_member_function() { return 0; }              \
    int prefix ## __private_static_member_variable = 0;                         \
    int prefix ## __private_member_function() { return 0; }                     \
    /**/

NONCLASS_ENTITIES(global_ns)

struct base_nonvis_class_impl {
    virtual ~base_nonvis_class_impl(){}
    CLASS_MEMBER_ENTITIES(global_ns__base_nonvis_class)
};
CLASS_MEMBER_ENTITIES_DEFINITION(base_nonvis_class_impl::global_ns__base_nonvis_class)


struct __visible__ some_class: base_nonvis_class_impl {
    CLASS_MEMBER_ENTITIES(global_ns__some_class)

    int foo() {
        return base_nonvis_class_impl::global_ns__base_nonvis_class__public_member_function();
    }
public:
    struct public_nested_class { 
        CLASS_MEMBER_ENTITIES(global_ns__some_class__public_nested_class)
    };

    struct public_nested_class_not_inside;
private:
    struct private_nested_class {
        CLASS_MEMBER_ENTITIES(global_ns__some_class__private_nested_class)
    };
};

struct  some_class::public_nested_class_not_inside {
    CLASS_MEMBER_ENTITIES(global_ns__some_class__public_nested_class_not_inside)
};

CLASS_MEMBER_ENTITIES_DEFINITION(some_class::global_ns__some_class)
CLASS_MEMBER_ENTITIES_DEFINITION(some_class::public_nested_class::global_ns__some_class__public_nested_class)
CLASS_MEMBER_ENTITIES_DEFINITION(some_class::private_nested_class::global_ns__some_class__private_nested_class)
CLASS_MEMBER_ENTITIES_DEFINITION(some_class::public_nested_class_not_inside::global_ns__some_class__public_nested_class_not_inside)


struct derived_nonvis_class_impl: some_class {
    CLASS_MEMBER_ENTITIES(global_ns__derived_nonvis_class)
};
CLASS_MEMBER_ENTITIES_DEFINITION(derived_nonvis_class_impl::global_ns__derived_nonvis_class)


namespace /*anonymous_ns*/ {
    NONCLASS_ENTITIES(anonymous_ns)
    struct __visible__ anonymous_ns_class {
        CLASS_MEMBER_ENTITIES(anonymous_ns__some_class)
    };
    CLASS_MEMBER_ENTITIES_DEFINITION(anonymous_ns_class::anonymous_ns__some_class)
}

namespace visible_ns __visible__ {
    NONCLASS_ENTITIES(visible_ns)
    struct __visible__ visible_ns_class {
        CLASS_MEMBER_ENTITIES(visible_ns__some_class)
    };
    CLASS_MEMBER_ENTITIES_DEFINITION(visible_ns_class::visible_ns__some_class)

    namespace nested {
        NONCLASS_ENTITIES(visible_ns__nested_ns)
        struct __visible__ visible_ns_nested_ns_class {
            CLASS_MEMBER_ENTITIES(visible_ns__nested_ns__some_class)
        public:
            struct public_nested_class { 
                CLASS_MEMBER_ENTITIES(visible_ns__nested_ns__some_class__public_nested_class)
            };

            struct public_nested_class_not_inside;
        };

        struct visible_ns_nested_ns_class::public_nested_class_not_inside {
                CLASS_MEMBER_ENTITIES(visible_ns__nested_ns__some_class__public_nested_class_not_inside)
        };

        CLASS_MEMBER_ENTITIES_DEFINITION(visible_ns_nested_ns_class::visible_ns__nested_ns__some_class)
        CLASS_MEMBER_ENTITIES_DEFINITION(visible_ns_nested_ns_class::public_nested_class::visible_ns__nested_ns__some_class__public_nested_class)
        CLASS_MEMBER_ENTITIES_DEFINITION(visible_ns_nested_ns_class::public_nested_class_not_inside::visible_ns__nested_ns__some_class__public_nested_class_not_inside)
    }
}

C. Program to generate visibility table

#!/usr/bin/python

import re
import subprocess

# Compiles the main.cpp, extracts all the exported entities using readelf
def get_exported_entities(compiler, all_entities):
    popen_result = subprocess.Popen(compiler + ' -g -fvisibility=hidden -shared main.cpp -o libmain.so && readelf -sW libmain.so',
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)
    popen_result.wait()

    if popen_result.returncode != 0:
        print popen_result.stderr.read()
        exit(popen_result.returncode)

    output = popen_result.stdout
    exported = set()
    while True:
        line = output.readline()
        if not line:
            break

        if not " GLOBAL " in line:
            continue

        exported.update([e for e in all_entities if e in line])

    return exported


def extend_with_colors(items):
    ext = {}
    for key, value in items.iteritems():
        ext[key + "_color"] = 'green' if value else 'red'
        
    items.update(ext)


# Preprocesses the main.cpp, extracts all the entities with two underlines in the middle
def get_all_entities():
    popen_result = subprocess.Popen('g++ -E main.cpp',
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)
    popen_result.wait()

    if popen_result.returncode != 0:
        print popen_result.stderr.read()
        exit(popen_result.returncode)

    output = popen_result.stdout
    all_entities = []
    while True:
        line = output.readline()
        if not line:
            break

        all_entities += re.findall("([_a-z]+__[_a-z]+)", line)

    return list(set(all_entities))


def get_extern_linkage_entities():
    vis_suffix = [
        '__function',
        '__function_inline',
        '__variable_noextern',
        '__variable_extern',

        '__public_member_function',
        '__public_member_function_inline',
        '__public_member_function_inline_not_in_body',
        '__public_static_member_function',
        '__public_static_member_function_inline',

        '__private_member_function',
        '__private_member_function_inline',
        '__private_static_member_function',
        '__private_static_member_function_inline',

        '__public_static_member_variable',
        '__private_static_member_variable',
    ]

    extern_linkage = set()
    for i in vis_suffix:
        if "member" in i:
            extern_linkage.add('global_ns__base_nonvis_class' + i)
            extern_linkage.add('global_ns__some_class' + i)
            extern_linkage.add('global_ns__derived_nonvis_class' + i)
            extern_linkage.add('global_ns__some_class__public_nested_class' + i)
            extern_linkage.add('global_ns__some_class__public_nested_class_not_inside' + i)
            extern_linkage.add('global_ns__some_class__private_nested_class' + i)
            extern_linkage.add('visible_ns__some_class' + i)
            extern_linkage.add('visible_ns__nested_ns__some_class' + i)
            extern_linkage.add('visible_ns__nested_ns__some_class__public_nested_class' + i)
            extern_linkage.add('visible_ns__nested_ns__some_class__public_nested_class_not_inside' + i)
        else:
            extern_linkage.add('global_ns' + i)
            extern_linkage.add('visible_ns' + i)
            extern_linkage.add('visible_ns__nested_ns' + i)

    return extern_linkage



def main():
    all_entities = get_all_entities()
    extern_linkage = get_extern_linkage_entities()

    gcc46_exports = get_exported_entities('g++-4.6', all_entities)
    gcc48_exports = get_exported_entities('g++-4.8', all_entities)
    gcc53_exports = get_exported_entities('g++-5', all_entities)
    clang34_exports = get_exported_entities('clang++', all_entities)

    result = []

    pattern = (
        "<tr> "
            "<td>{name}</td> "
            "<td bgcolor='{gcc46_color}'>{gcc46}</td> "
            "<td bgcolor='{gcc48_color}'>{gcc48}</td> "
            "<td bgcolor='{gcc53_color}'>{gcc53}</td> "
            "<td bgcolor='{clang34_color}'>{clang34}</td> "
            "<td style='border:none'> </td> "
            "<td bgcolor='{computed_color}'>{computed}</td> "
            "<td>=</td> "
            "<td bgcolor='{linkage_color}'>{linkage}</td> "
            "<td>&amp;&amp;</td> "
            "<td bgcolor='{noinline_color}'>{noinline}</td> "
            "<td>&amp;&amp;</td> "
            "<td bgcolor='{visible_color}'>{visible}</td> "
        "</tr>"
    )

    for entity in all_entities:
        not_base_or_derived = not "derived" in entity and not "base" in entity
        is_extern = entity in extern_linkage
        not_inline = not "inline" in entity
        computed_vis = not_base_or_derived and not_inline and is_extern
        
        s = {
            'name': entity,
            'gcc46': entity in gcc46_exports,
            'gcc48': entity in gcc48_exports,
            'gcc53': entity in gcc53_exports,
            'clang34': entity in clang34_exports,
            'computed': computed_vis,
            'linkage': is_extern,
            'noinline': not_inline,
            'visible': not_base_or_derived,
        }
        extend_with_colors(s) 
        result.append( pattern.format(**s) )

    for l in sorted(result):
        print l.replace('__','::').replace('global_ns', '').replace('anonymous_ns', '&lt;anonymous&gt;')


if __name__ == "__main__":
    main()