Re: Inline symbols visibility
Re: Inline symbols visibility
- Subject: Re: Inline symbols visibility
- From: Andreas Grosam <email@hidden>
- Date: Thu, 8 Apr 2010 12:14:01 +0200
On Apr 7, 2010, at 3:23 PM, Guillaume Billard wrote:
> Le 2 avr. 2010 à 19:10, Andreas Grosam a écrit :
>
>> You should be carefully, though with static declared member variables which may be accessed in inline member functions. The static member variable shall be visible, and all copies of an inline member function shall access this single instance. Usually, the compiler and linker takes care about all of this - but not always (see one of my previous post).
>
> I checked, the inline function indeed accesses a static member variable (through a static member function). I would say that this static member variable is indeed visible, but how do I check that all copies of the inline member function do access this single instance? I'm not sure I understand.
You should normally not worry about this - the compiler and linker shall ensure that this works as expected. I only mentioned this, because there can be subtle issues, occasionally.
Anyway, if you suspect your static variable does not behave correctly, use the nm tool to verify symbol visibility and the debugger ;)
> Actually I'm not even sure what a "out-of-line copy of an inline member function" is, sorry. I've read the term many times but I'm still confused.
This is just an inlined declared member function, which exists inlined in one or more occurrences, but also has one ore more non-inlined instances in some modules. This may happen, when you for instance use the address of this inline function, which forces the compiler to generate a non-inlined version in this case. But otherwise the call to this function can and will be inlined.
>
>>> I found this thread where it seems that the same behavior occurred:
>>> http://lists.apple.com/archives/xcode-users/2005/Jul/msg00609.html.
>>>
>>> Adding the "default" visibility attribute to the inline method solved the problem.
>>> If I'm not mistaken, the compiler gets to decide wether to inline methods or not, even when they're defined out of their class definition.
>>> It appears that it can inline methods for classes that use __attribute__((visibility("default"))), but I don't know if it can do the same for out-of-class methods. I'm hoping not.
>>> I'd like to know if adding the attribute to a method of a visible class whenever it doesn't get exported is the right approach.
>> Actually, it will be *exported* , the function is in your header. Unless its declared private in the class, clients have "access" to it. Do you mismatch interfaces and visibility?
>
> I don't mismatch the two but it's true, now that you make me think about it, how come this function would not be available? I want to believe you that it's not about visibility, but still adding the visibility attribute fixes my problem.
> Below is a simplification of the code I'm working with. I'm sorry I don't have a reproducible sample.
Thank you, your example is perfect :)
The culprit seems to be the *virtual* *inline* functions. Usually, you can declare virtual functions also inline. The compiler may choose to inline a virtual function if it (he) knows the concrete type. For instance, when the function is explicitly qualified (ClassX::foo()).
In any way, the compiler must guarantee the *expected behavior* (polymorphism), though. So your code should always work, (should!).
In your case, though, I suspect the two classes are in different dynamic shared objects (DSOs) - or libraries - where the "visibility" of symbols is relevant. It seems, the compiler and linker will hide the vtables and virtual functions when these are inlined, since inline functions will be hidden by default (compiler flag).
> If that helps, using nm on the mid-level library I see this line:
> 001a9c30 t __ZNK36gnReplicationObjectsCreationDatagram12GetMetaClassEv
This tells, that the function is in fact there, but hidden.
So, although it makes sense that non-virtual out-of-line copies of inline declared member functions should be hidden, it makes not so much sense for virtual functions. Honestly, I would have expected, the compiler would know this as well ;) Virtual inlined destructors!?
Virtual functions will rarely be inlined anyway (that is, will be mostly non-inlined, and there should exist at least one out-of-line copy, anyway). And due to polymorphism the actual function that will be called may reside in a different DSO. This of course requires the function to be visible in the DSO where it resides. So, IMHO, unless explicitly declared otherwise, virtual inline functions shall be always visible.
Is it a bug in the compiler? I would say, yes. At least, the current behavior is pretty unfortunate.
The workaround is to declare virtual inline functions explicitly visible, as you did.
A similar subtle issue is with exceptions and RTTI in cases where you deal with several DSOs. Ensure that all classes (and base classes) which may be thrown in exceptions or classes which are arguments in a dynamic_cast are visible.
Regards
Andreas
>
> ************************************************************************************
>
> // ---------------------------------------------------------------------------------------------------------------------
> // gnReplicationDatagram.h, part of a mid-level dynamic library
>
> #include "<path_to_low_level_library>/gnMetaClass.h"
>
> class __attribute__((visibility("default"))) gnReplicationDatagram
> {
> private:
> static gnMetaClass const* ms_pClass_gnReplicationDatagram;
> public:
> static gnMetaClass const& MetaClass()
> {
> return *ms_pClass_gnReplicationDatagram;
> }
>
> /*__attribute__((visibility("default")))*/ virtual gnMetaClass const& GetMetaClass() const
> {
> return gnReplicationDatagram::MetaClass();
> }
>
> public:
> gnReplicationDatagram() {}
> virtual ~gnReplicationDatagram() {}
>
> gnReplicationDatagram(gnReplicationDatagramId a_eId): m_eId(a_eId) {}
>
> private:
> gnReplicationDatagramId m_eId;
> };
>
>
> // ---------------------------------------------------------------------------------------------------------------------
> // gnReplicationObjectsCreationDatagram.h, part of the same mid-level dynamic library as above
>
> #include "<path_to_mid_level_library>/gnReplicationDatagram.h"
>
> class __attribute__((visibility("default"))) gnReplicationObjectsCreationDatagram: public gnReplicationDatagram, gnNoCopy<gnReplicationObjectsCreationDatagram>
> {
> private:
> static gnMetaClass const* ms_pClass_gnReplicationObjectsCreationDatagram;
>
> public:
> static gnMetaClass const& MetaClass()
> {
> return *ms_pClass_gnReplicationObjectsCreationDatagram;
> }
>
> /*__attribute__((visibility("default")))*/ virtual gnMetaClass const& GetMetaClass() const
> {
> return gnReplicationObjectsCreationDatagram::MetaClass();
> }
>
> public:
> gnReplicationObjectsCreationDatagram(): gnReplicationDatagram(gnReplicationDatagramId::eObjectCreation) {}
> virtual ~gnReplicationObjectsCreationDatagram();
>
> private:
> // some non-static variable
> };
>
>
> // ---------------------------------------------------------------------------------------------------------------------
> // gnReplicationDatagramUTestSuite.cpp, part of a Unit Test dynamic library
>
> #include "<path_to_mid_level_library>/gnReplicationObjectsCreationDatagram.h"
>
> class gnReplicationDatagramUTestSuite: public gnUTestSuite
> {
> private:
> void ObjectsCreationDestructionDatagram()
> {
> gnReplicationObjectsCreationDatagram oCreationDatagram;
> }
> };
>
> ************************************************************************************
>
> Enabling the 2 commented attributes fixes the link error that I have when I build the Unit Test library with -03 instead of -O0 or -Os:
> Undefined symbols:
> "gnReplicationObjectsCreationDatagram::GetMetaClass() const", referenced from:
> gnReplicationDatagramUTestSuite::ObjectsCreationDestructionDatagram() in gnReplicationDatagramUTestSuite.o
>
> If that helps, using nm on the mid-level library I see this line:
> 001a9c30 t __ZNK36gnReplicationObjectsCreationDatagram12GetMetaClassEv
> Which c++filt sees as:
> gnReplicationObjectsCreationDatagram::GetMetaClass() const
>
>
> To put things into context, the pieces of code about gnMetaClass that are almost identical in the 2 classes actually are an expanded macro defined in the low-level library, used to implement reflexivity.
>
> Thanks a lot for your detailed answer, I hope this piece of code can help you helping me understand this! :)
>
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Xcode-users mailing list (email@hidden)
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Xcode-users mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden