This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Generic questions on inline functions and linking


On 21/07/15 22:16 -0700, Tim Shen wrote:
If a user compiled two object files, one with exception (or rtti,
doesn't matter) turned on, but another without, and links them
together, such a function in libstdc++ may be problematic:

inline void __foo()
{
 __try
   {
     // ...
   }
 __catch(...)
   {
     // ...
   }
}

since it's an odr violation.

Yes, technically it's an ODR violation. But it's harmless. The
function will either throw or will abort, but it doesn't really matter
which.

Should the user be responsible for this, or we should avoid writing
such a thing? I guess it applies to template functions as well?

Yes, it applies to template code too, but it works, so there's no
problem.

If the function gets inlined then the code that gets inlined into the
calling function will either use exceptions or not, depending on the
value of __cpp_exceptions when the caller is being compiled (which is
exactly what the user asked for).

If the function doesn't get inlined then the function definition will
be emitted in the object file, either using exceptions or not,
according to the value of __cpp_exceptions when compiling that object.
In that case different object files might contain different
definitions of the function (some with exceptions some without) and
the linker will pick one and discard the others. Whether the function
uses exceptions or not depends which definition the linker keeps (and
that can be controlled to some extent by adjusting the order the
objects are seen by the linker). This isn't really much different from
file1 calling a non-inline function defined in file2 where file1 uses
exceptions and file2 doesn't.

In short, nothing bad is going to happen. If the function "fails" then
it will throw or it will abort, and both are apparently acceptable to
the user (or they wouldn't be compiling one object with -fexceptions
and another with -fno-exceptions). If users don't like the
inconsistency then they shouldn't use inconsistent compiler options,
it's not the library's problem.

The kind of ODR violations we must avoid are where the function does
something completely different e.g.

inline T* something_or_other(T* p)
{
 if (p && p->something())
   return p;
 // else give them some other T object
#if __cpp_rtti
 static T t;
 return &t;
#else
 delete p;
 return new T;
#endif
}

Here the ODR violation is dangerous, because one caller might get a
pointer to a static object returned, and another caller might pass
that pointer back to the function causing it to be deleted.  It's not
safe to combine objects that use this function with inconsistent
values of __cpp_rtti. We must not write code like this in the library.

Similarly, we must avoid changing object size or layout according to
compiler options e.g. attempting to define std::future in terms of a
type like this would cause a very bad ODR violation:

template<typename T>
struct __future_shared_state
{
 T* _M_stored_value;
#if __cpp_exceptions
 std::exception_ptr _M_stored_exception;
#else
 // Exception support turned off so no need to store one
#endif
 bool _M_ready;
};

This would make it impossible to share std::future variables between
files with different values of __cpp_exceptions because they would not
agree on the layout of the type.

I raise this because recently I'm refactoring regex library, and found
that marking the giant regex library inlined (including template
function) improves >10% time efficiency and reduces binary size. It's

Nice. Does it have any effect on the compile times?

really good to keep them inlined since they are anyway in the header,
and there is no real reason should we let use to keep multiple copies
in an executable.

I don't think I understand this last sentence, because you won't get
multiple copies kept in the executable anyway, the linker will only
keep one copy of each symbol in the final executable.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]