On 2/2/21 2:51 PM, Andrey Semashev wrote:
On 2/2/21 11:35 AM, Alexander Grund via Boost wrote:
Hi Boost maintainers,
TLDR: Check your classes if they use BOOST_*_DECL and change that to BOOST_SYMBOL_VISIBLE
In Boost.ProgramOptions I discovered an issue as a user, where I was unable to catch an exception thrown from the shared Boost library in an executable when both are build with hidden visibility.
It turns out the exception class was marked with BOOST_PROGRAM_OPTIONS_DECL which evaluates to "visible" when building the DLL but to nothing when using it. This makes the linker (in theory) mark the typeinfo as hidden which means that the class thrown by Boost and the one tried to catch are unrelated.
This means that BOOST_PROGRAM_OPTIONS_DECL is incorrectly defined.
The basic gist is as follows:
1. On Windows, there is no visibility support. gcc and clang still support visibility attributes (including BOOST_SYMBOL_VISIBLE), but I don't think they have any effect. On Windows, you should be using BOOST_SYMBOL_EXPORT/BOOST_SYMBOL_IMPORT, depending on when your library is compiled or when user's code is compiled. 2. Again, on Windows, there is no need to export/import RTTI for it to work, so you don't need to mark exceptions just to make them catchable. You may still want to export/import a class (including an exception) if you want to export/import all its methods, *including the implicitly generated ones*. In particular, the compiler will probably not inline any methods of the class, so it is more preferable to mark individual methods rather than the whole class. 3. On other systems, where visibility is supported, BOOST_SYMBOL_VISIBLE must be used both when compiling the library and user's code, and it should be used to mark types whose RTTI is required to work across library boundaries. This includes exceptions and types that participate in dynamic_cast. Also, BOOST_SYMBOL_VISIBLE must be used to export/import methods from the library.
So, the bottom line, BOOST_PROGRAM_OPTIONS_DECL must be defined to:
- BOOST_SYMBOL_EXPORT - on Windows, when the library is being built; - BOOST_SYMBOL_IMPORT - on Windows, when the library is being consumed by user; - BOOST_SYMBOL_VISIBLE - in any other case.
Then you can use BOOST_PROGRAM_OPTIONS_DECL to mark methods and classes that you want to be exported/imported from the library. If a particular type does not need to be exported/imported, but whose RTTI must still be accessible across library boundaries, mark that type with BOOST_SYMBOL_VISIBLE, not BOOST_PROGRAM_OPTIONS_DECL. You can still use BOOST_PROGRAM_OPTIONS_DECL to export/import select methods of that type, if needed.
PS: I remember someone saying that on recent Windows dllexport/dllimport attributes are no longer necessary. I don't quite know how that works, but for backward compatibility we should probably keep using BOOST_SYMBOL_EXPORT/BOOST_SYMBOL_IMPORT as described above.
PPS: I forgot to add that recent gcc versions don't need RTTI to be publicly visible to be able to catch exceptions or compare type_info, as they now perform deep string comparison instead of address comparison. However, older compiler versions are still susceptible to this, and I'm not sure if the deep comparison behavior is used on all platforms.