At some point, we'll need to acknowledge the problem which results from building Boost with one -std and user code with another, and deal with it somehow, if just by documenting. Most of our libraries don't suffer from it at the moment but Boost.System does, because of my changes; its ABI changes between 03/11/14. So I'm going to take the initial hit. :-) POSIX platforms have long been able to ignore ABI issues so we don't mangle the library names there at all, but now whether one builds Boost for C++03, C++11 or C++14 matters, and there's nothing to prevent linking C++xx to C++yy. Even with --layout=versioned, the cxxstd level is not encoded. The fact that the default C++ standard differs between g++ and clang++ doesn't help; when following the instructions of doing "b2 install", with g++6 and above you get C++14, but with g++5 or clang++ you get C++03. The latter is very rarely what one wants today.
On 8/19/2018 11:17 AM, Peter Dimov via Boost wrote:
At some point, we'll need to acknowledge the problem which results from building Boost with one -std and user code with another, and deal with it somehow, if just by documenting.
Most of our libraries don't suffer from it at the moment but Boost.System does, because of my changes; its ABI changes between 03/11/14. So I'm going to take the initial hit. :-)
POSIX platforms have long been able to ignore ABI issues so we don't mangle the library names there at all, but now whether one builds Boost for C++03, C++11 or C++14 matters, and there's nothing to prevent linking C++xx to C++yy. Even with --layout=versioned, the cxxstd level is not encoded.
The fact that the default C++ standard differs between g++ and clang++ doesn't help; when following the instructions of doing "b2 install", with g++6 and above you get C++14, but with g++5 or clang++ you get C++03. The latter is very rarely what one wants today.
Are not ABI changes a compiler issue ? I do not see that there is any inherent reason why building code with some -std, and using it with code built to another -std, should cause ABI issues when the compiler remains the same. I admit I do not know whether ABI compatibility is addressed by the C++ standard itself, although I expect not.
Edward Diener wrote:
Are not ABI changes a compiler issue ? I do not see that there is any inherent reason why building code with some -std, and using it with code built to another -std, should cause ABI issues when the compiler remains the same.
It happens when the library interface, or the layout of its classes, changes depending on a Config macro, which is set in f.ex. C++03 mode and not set in C++11.
On Sun, Aug 19, 2018 at 6:17 PM, Peter Dimov via Boost
At some point, we'll need to acknowledge the problem which results from building Boost with one -std and user code with another, and deal with it somehow, if just by documenting.
Most of our libraries don't suffer from it at the moment but Boost.System does, because of my changes; its ABI changes between 03/11/14. So I'm going to take the initial hit. :-)
POSIX platforms have long been able to ignore ABI issues so we don't mangle the library names there at all, but now whether one builds Boost for C++03, C++11 or C++14 matters, and there's nothing to prevent linking C++xx to C++yy. Even with --layout=versioned, the cxxstd level is not encoded.
The fact that the default C++ standard differs between g++ and clang++ doesn't help; when following the instructions of doing "b2 install", with g++6 and above you get C++14, but with g++5 or clang++ you get C++03. The latter is very rarely what one wants today.
Our libraries should build into binaries that are suitable for linking with user's code in any C++ version, otherwise standard Boost binaries in Linux distributions will be compatible only with one C++ version, which is not practical. Could you expand on what causes the ABI difference and why it can't be unified? I know it may be ugly, but libstdc++ does maintain ABI stability somehow, we could follow its example. If we can't or don't want to make Boost.System ABI-portable then I'd rather revert the changes to Boost.System and maybe create Boost.System2 for C++11 and up.
Andrey Semashev wrote:
Our libraries should build into binaries that are suitable for linking with user's code in any C++ version, otherwise standard Boost binaries in Linux distributions will be compatible only with one C++ version, which is not practical.
I don't think that it's possible in general to support linking any C++ version to any other. Suppose for instance that you have struct X; struct Y { void f( X const& x ); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) void f( X&& x ); #endif }; If you compile everything with C++03, it works. If you compile everything with C++11, it works. If you compile the library with C++11 and the user code with C++03, it works (barring other issues.) But if you compile the library with C++03, and the user code with C++11, it doesn't. The user code tries to call Y::f(X&&), and it's not there. And if you're saying that fine, we need to build the library with C++11 then, well that's part of my point, that "b2 install" builds with C++03 by default on Clang. Now the above is a trivial case, there are many other examples of code that could change depending on whether a feature is supported or not; and even when it 'works', it's still technically an ODR violation because the definition of Y is not the same. Being "ABI-portable" under a strict interpretation of the standard means that nothing in the user-facing headers is allowed to depend on whether a feature is supported or not. That's impractical. The only supported configuration, under said strict interpretation, is when everything is compiled with the same -std level. This applies even to libraries that do nothing differently by themselves, if they include a header-only library that does something differently. In practice, the present situation with Boost.System is that you can link 03 to 11, 11 to 03, 03 and 11 to 14, but not 14 to 03 or 11. This was the best I could do.
On Sun, Aug 19, 2018 at 11:37 PM, Peter Dimov via Boost
Andrey Semashev wrote:
Our libraries should build into binaries that are suitable for linking with user's code in any C++ version, otherwise standard Boost binaries in Linux distributions will be compatible only with one C++ version, which is not practical.
I don't think that it's possible in general to support linking any C++ version to any other.
Suppose for instance that you have
struct X;
struct Y { void f( X const& x );
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
void f( X&& x );
#endif };
If you compile everything with C++03, it works. If you compile everything with C++11, it works. If you compile the library with C++11 and the user code with C++03, it works (barring other issues.)
But if you compile the library with C++03, and the user code with C++11, it doesn't. The user code tries to call Y::f(X&&), and it's not there.
I solved this in Boost.Log this way: struct Y { void f( X const& x ); #if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) void f( X&& x ) { f_move(x); } #endif private: void f_move(X& x); };
Now the above is a trivial case, there are many other examples of code that could change depending on whether a feature is supported or not; and even when it 'works', it's still technically an ODR violation because the definition of Y is not the same.
Yes, I understand that not every case is as simple as the one above. That's why I'm asking for more details. And we have to accept that as soon as we enter this ABI stuff territory we're no longer in standard C++ land. We will most likely have to do dirty stuff, much like compiler writers do, to achieve compatibility. ODR issues are no longer relevant, as long as we're playing by compiler rules.
Being "ABI-portable" under a strict interpretation of the standard means that nothing in the user-facing headers is allowed to depend on whether a feature is supported or not. That's impractical. The only supported configuration, under said strict interpretation, is when everything is compiled with the same -std level. This applies even to libraries that do nothing differently by themselves, if they include a header-only library that does something differently.
In practice, the present situation with Boost.System is that you can link 03 to 11, 11 to 03, 03 and 11 to 14, but not 14 to 03 or 11. This was the best I could do.
Sorry, but I don't think this is acceptable. It prevents C++03 users from linking with system Boost.System (provided that the system Boost is compiled with default gcc options, which enable C++14). And there are strictly C++03 users out there. As a slightly different variant of the Boost.System2 solution, I can suggest building multiple Boost.System binaries, one per ABI we can support.
Andrey Semashev wrote:
In practice, the present situation with Boost.System is that you can link 03 to 11, 11 to 03, 03 and 11 to 14, but not 14 to 03 or 11. This was the best I could do.
Sorry, but I don't think this is acceptable. It prevents C++03 users from linking with system Boost.System (provided that the system Boost is compiled with default gcc options, which enable C++14).
As I said, linking 03 (user code) to 14 (Boost.System) works. The reverse does not. As I also said, the reverse occurs when the compiler is g++ 5 or clang++, where the default is 03. But in general, there's no practical way to support mixed standards unless we change something in the way we do things, and I don't know what. If you compile library A with C++03 and library B with C++11 and both use shared_ptr, in a program that uses both there will be an ODR violation because shared_ptr will use std::atomic when present (in B) and will not use it when not (in A).
On Mon, 20 Aug 2018 at 03:42, Peter Dimov via Boost
... or clang++, where the default is 03.
https://clang.llvm.org/docs/UsersManual.html#basic-usage The truth doesn't matter though, as defaults will change as we go forward in time, for all compilers, so the problem will not go away. It's trying to fit a square peg in a round hole, it starts to look like a Hackaton to me. The thing that should work and the only thing that can be guaranteed to work is when your whole code base is compiled with the same compiler with the same standard (std::string ?). The rest is just a waste of time and resources, which could be used more fruitfully. degski -- *“If something cannot go on forever, it will stop" - Herbert Stein* *“No, it isn’t truth. Truth isn’t truth" - Rudolph W. L. Giuliani*
On 20/08/2018 12:42, Peter Dimov wrote:
But in general, there's no practical way to support mixed standards unless we change something in the way we do things, and I don't know what. If you compile library A with C++03 and library B with C++11 and both use shared_ptr, in a program that uses both there will be an ODR violation because shared_ptr will use std::atomic when present (in B) and will not use it when not (in A).
Pick one of: 1. Ignore the issue and let it be the user's problem. "Don't do that." This is basically how things are at the moment and it's fine for people who build Boost themselves, but not as good for people trying to build or use a system-provided precompiled Boost. ODR violations are everywhere. 2. Make everything header-only. This doesn't solve the problem but it pushes it to the user like #1 (and also increases their compile times). 3. Compile once in C++03, once in C++11, etc and again for any other ABI-affecting Boost.Config setting the library uses (and, importantly, for any such things any of your *ABI-visible dependencies* use). In the end, merge the result into a single compiled library (using different internal namespaces for each build), and have the headers select one implementation or the other based on the settings in effect from the caller. 4. Compile only once for whatever the user specifies but use library name mangling so that different library ABIs can coexist. 5. Wait for modules and hope they solve it. (This is otherwise #1) Some libraries are already doing a little of #3 for specific settings, but it probably needs to be more universal to handle things like move constructors, as already noted here. One feature of #3 (which some will like and others will hate) is that it would allow different "islands" of C++03 and C++11 (or later) code to coexist, as long as they don't try to pass Boost objects in their own ABI between each other. (And if they do try, they'll get a link error instead of an ODR violation.)
On 08/20/18 03:42, Peter Dimov via Boost wrote:
But in general, there's no practical way to support mixed standards unless we change something in the way we do things, and I don't know what. If you compile library A with C++03 and library B with C++11 and both use shared_ptr, in a program that uses both there will be an ODR violation because shared_ptr will use std::atomic when present (in B) and will not use it when not (in A).
Then the only solution I see is to build separate binaries for different C++ versions.
On Mon, 20 Aug 2018 at 11:26, Andrey Semashev via Boost < boost@lists.boost.org> wrote:
Then the only solution I see is to build separate binaries for different C++ versions.
Yes, or something will break, now or in the future. Adding functionality to Boost that depends on compiler implementations seems the wrong way to go, IMO, as those implementations are free to change as they please, provided they stick to the standard, which is of not much help in this context. degski -- *“If something cannot go on forever, it will stop" - Herbert Stein* *“No, it isn’t truth. Truth isn’t truth" - Rudolph W. L. Giuliani*
On Sun, Aug 19, 2018 at 10:57 PM, Andrey Semashev via Boost
I solved this in Boost.Log this way:
struct Y { void f( X const& x );
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
void f( X&& x ) { f_move(x); }
#endif
private: void f_move(X& x); };
Only works if void f( X&& x ) is inlined and even then it still violates ODR doesn't it?
As a slightly different variant of the Boost.System2 solution, I can suggest building multiple Boost.System binaries, one per ABI we can support.
That'd require library name mangling on Linux and adding the std level to the name.. -- Olaf
On 08/20/18 10:37, Olaf van der Spek wrote:
On Sun, Aug 19, 2018 at 10:57 PM, Andrey Semashev via Boost
wrote: I solved this in Boost.Log this way:
struct Y { void f( X const& x );
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
void f( X&& x ) { f_move(x); }
#endif
private: void f_move(X& x); };
Only works if void f( X&& x ) is inlined and even then it still violates ODR doesn't it?
If `f( X&& x )` is not inlined then it means it is ODR-used in the user's code and left there as a standalone function. I don't see the problem. ODR is violated but not in the harmful way. ABI is the same, API is 99.9% compatible (leaving 0.1% for weird cases where you try to exchange a pointer to Y::f between code bases for C++03 and C++11).
As a slightly different variant of the Boost.System2 solution, I can suggest building multiple Boost.System binaries, one per ABI we can support.
That'd require library name mangling on Linux and adding the std level to the name..
Not necessarilly. The base name of Boost.System with the new ABI could be different.
On Mon, Aug 20, 2018 at 10:23 AM, Andrey Semashev via Boost
As a slightly different variant of the Boost.System2 solution, I can suggest building multiple Boost.System binaries, one per ABI we can support.
That'd require library name mangling on Linux and adding the std level to the name..
Not necessarilly. The base name of Boost.System with the new ABI could be different.
Wouldn't that require all reverse dependencies to do the same? -- Olaf
On 08/20/18 11:32, Olaf van der Spek wrote:
On Mon, Aug 20, 2018 at 10:23 AM, Andrey Semashev via Boost
wrote: As a slightly different variant of the Boost.System2 solution, I can suggest building multiple Boost.System binaries, one per ABI we can support.
That'd require library name mangling on Linux and adding the std level to the name..
Not necessarilly. The base name of Boost.System with the new ABI could be different.
Wouldn't that require all reverse dependencies to do the same?
Yes, I suppose so. With Boost.System being a core library, I agree that automatic name mangling would make it easier.
On Mon, Aug 20, 2018 at 10:45 AM, Andrey Semashev via Boost
Yes, I suppose so. With Boost.System being a core library, I agree that automatic name mangling would make it easier.
Costs / complexity of having multiple versions of libraries (System1 / System2) shouldn't be underestimated either. When can we drop C++03 support? :D -- Olaf
On 8/19/2018 4:37 PM, Peter Dimov via Boost wrote:
Andrey Semashev wrote:
Our libraries should build into binaries that are suitable for linking with user's code in any C++ version, otherwise standard Boost binaries in Linux distributions will be compatible only with one C++ version, which is not practical.
I don't think that it's possible in general to support linking any C++ version to any other.
Suppose for instance that you have
struct X;
struct Y { void f( X const& x );
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
void f( X&& x );
#endif };
If you compile everything with C++03, it works. If you compile everything with C++11, it works. If you compile the library with C++11 and the user code with C++03, it works (barring other issues.)
But if you compile the library with C++03, and the user code with C++11, it doesn't. The user code tries to call Y::f(X&&), and it's not there.
I do not think this will occur. If the shared library is statically loaded the linker will resolve the issue and tell you that Y::f(X&&) is not there at link time, and if the shared library is dynamically loaded whatever functionality which you call at run-time to get the address of Y::f(X&&) will fail. I can see the case where you expect a call to resolve to Y::f(X&&) but because the library is built as c++03 it resolves to Y::f(X const &) instead, but that is to be expected unless the end-user does not know that he is using a c++03 shared library. Of course if your program is not rebuilt against a shared library whose ABI changes you probably get a run-time error as soon as you execute code, but I would view that as user error. I am not saying that it is not a problem when a library with the same name is rebuilt with different public functionality for an end-user of that library. This can theoretically occur in much more minute ways than just changing the level of C++ standard support. Are you arguing that the final name of a Boost shared library should be different depending on its C++ standard support level ?
And if you're saying that fine, we need to build the library with C++11 then, well that's part of my point, that "b2 install" builds with C++03 by default on Clang.
Now the above is a trivial case, there are many other examples of code that could change depending on whether a feature is supported or not; and even when it 'works', it's still technically an ODR violation because the definition of Y is not the same.
Being "ABI-portable" under a strict interpretation of the standard means that nothing in the user-facing headers is allowed to depend on whether a feature is supported or not. That's impractical. The only supported configuration, under said strict interpretation, is when everything is compiled with the same -std level. This applies even to libraries that do nothing differently by themselves, if they include a header-only library that does something differently.
In practice, the present situation with Boost.System is that you can link 03 to 11, 11 to 03, 03 and 11 to 14, but not 14 to 03 or 11. This was the best I could do.
On 20/08/2018 16:46, Edward Diener wrote:
On 8/19/2018 4:37 PM, Peter Dimov wrote:
But if you compile the library with C++03, and the user code with C++11, it doesn't. The user code tries to call Y::f(X&&), and it's not there.
I do not think this will occur. If the shared library is statically loaded the linker will resolve the issue and tell you that Y::f(X&&) is not there at link time, and if the shared library is dynamically loaded whatever functionality which you call at run-time to get the address of Y::f(X&&) will fail.
I can see the case where you expect a call to resolve to Y::f(X&&) but because the library is built as c++03 it resolves to Y::f(X const &) instead, but that is to be expected unless the end-user does not know that he is using a c++03 shared library.
The problem is that the headers are compiled as C++11 (because they're included by that translation unit) while the source was compiled as C++03. Thus this is a link error -- the header specified that the move constructor should be present, but the compiled library does not contain it.
I am not saying that it is not a problem when a library with the same name is rebuilt with different public functionality for an end-user of that library. This can theoretically occur in much more minute ways than just changing the level of C++ standard support. Are you arguing that the final name of a Boost shared library should be different depending on its C++ standard support level ?
Yes, there are a lot more things that can cause the exact same problem -- this is one of the reasons why the Windows builds include so many different "ABI altering" flags in their names. Historically Linux hasn't had to worry about this as much since "everything" is compiled from source using the same compilers and libraries -- except when they aren't.
On Mon, 20 Aug 2018 at 15:08, Gavin Lambert via Boost
Yes, there are a lot more things that can cause the exact same problem -- this is one of the reasons why the Windows builds include so many different "ABI altering" flags in their names.
Yes MS worries a lot more about breaking ABI's, as there are billions of computers running the thing, so you have to jump through hoops, to work around those issues (which is just another word for "hacking"). Historically Linux hasn't had to worry about this as much since
"everything" is compiled from source using the same compilers and libraries -- except when they aren't.
See above, and up till now there haven't been many std's either (C++03 is just some gloss over of C++98), so there was nothing to worry about. The implicit assumption is obviously that not only is all compiled from source, but everything is compiled with the same std and compiler, as no-one in his right mind would compile one bit with C++03 and another bit with C++17 (if you're compiling all from source). What does not help is that nothing until C++14 (maybe even in C++11) was ever removed from the std, so this is also a novelty, all of a sudden C++14 doesn't compile C++03 code (which I would say has always been another implicit assumption). degski -- *“If something cannot go on forever, it will stop" - Herbert Stein* *“No, it isn’t truth. Truth isn’t truth" - Rudolph W. L. Giuliani*
degski wrote:
as no-one in his right mind would compile one bit with C++03 and another bit with C++17 (if you're compiling all from source).
Unfortunately, that's exactly what people do, build Boost with the default "b2 install" and then use it from C++17. Admittedly, our build instructions don't mention the cxxstd=17 option.
On Mon, Aug 20, 2018 at 3:39 PM, Peter Dimov via Boost
degski wrote:
as no-one in his right mind would compile one bit with C++03 and another bit with C++17 (if you're compiling all from source).
Unfortunately, that's exactly what people do, build Boost with the default "b2 install" and then use it from C++17.
Admittedly, our build instructions don't mention the cxxstd=17 option.
Documentation is not a solution. ;) Would it make sense to default to the highest available level? -- Olaf
On Mon, 20 Aug 2018 at 16:50, Olaf van der Spek via Boost < boost@lists.boost.org> wrote:
On Mon, Aug 20, 2018 at 3:39 PM, Peter Dimov via Boost
wrote: degski wrote:
as no-one in his right mind would compile one bit with C++03 and another bit with C++17 (if you're compiling all from source).
Unfortunately, that's exactly what people do, build Boost with the default "b2 install" and then use it from C++17.
Admittedly, our build instructions don't mention the cxxstd=17 option.
Documentation is not a solution. ;)
Would it make sense to default to the highest available level?
The documentation should just say that compilers and std levels should match. In a way we have inadvertently wandered into unexplored territory (mainly because this problem never arose before). There is another gray area, which was touched upon on the list here, which I think should also be discussed more, and that's Robert's problem of linking static and dynamic libs into the same app. degski -- *“If something cannot go on forever, it will stop" - Herbert Stein* *“No, it isn’t truth. Truth isn’t truth" - Rudolph W. L. Giuliani*
Just for information: this issue has now (not by me, but someone else following this list) been raised (and has been recognized) as a problem at vcpkg. Some advice has been issued, resolution is pending more investigation. degski -- *“If something cannot go on forever, it will stop" - Herbert Stein* *“No, it isn’t truth. Truth isn’t truth" - Rudolph W. L. Giuliani*
On Mon, Aug 20, 2018 at 9:14 AM degski via Boost
Just for information: this issue has now (not by me, but someone else following this list) been raised (and has been recognized) as a problem at vcpkg. Some advice has been issued, resolution is pending more investigation.
For our curiosity.. What issue does vcpkg have in this realm? -- -- Rene Rivera -- Grafik - Don't Assume Anything -- Robot Dreams - http://robot-dreams.net
On Mon, 20 Aug 2018 at 17:23, Rene Rivera
For our curiosity.. What issue does vcpkg have in this realm?
Ok, I dug it up for you: https://github.com/Microsoft/vcpkg/issues/4004#issuecomment-414188408 . degski -- *“If something cannot go on forever, it will stop" - Herbert Stein* *“No, it isn’t truth. Truth isn’t truth" - Rudolph W. L. Giuliani*
On Mon, Aug 20, 2018 at 9:37 AM degski
On Mon, 20 Aug 2018 at 17:23, Rene Rivera
wrote: For our curiosity.. What issue does vcpkg have in this realm?
Ok, I dug it up for you: https://github.com/Microsoft/vcpkg/issues/4004#issuecomment-414188408 .
I see, thanks. Conan had a similar issue early on also. I brought it up as a problem. And now it considers different std versions ABI incompatible. As a general note.. It's becoming less common to rely on system installed C++ libraries because of these issues. Something I consider a good thing. As ABI is not the only problem with them. -- -- Rene Rivera -- Grafik - Don't Assume Anything -- Robot Dreams - http://robot-dreams.net
On Mon, 20 Aug 2018 at 17:43, Rene Rivera via Boost
And now it considers different std versions ABI incompatible.
I think this is the only answer, and so the only thing Boost needs to do is to state that in the docs. On to dynamic and static mixed linking. degski -- *“If something cannot go on forever, it will stop" - Herbert Stein* *“No, it isn’t truth. Truth isn’t truth" - Rudolph W. L. Giuliani*
participants (7)
-
Andrey Semashev
-
degski
-
Edward Diener
-
Gavin Lambert
-
Olaf van der Spek
-
Peter Dimov
-
Rene Rivera