Le 10.02.18 à 19:09, Steven Watanabe via Boost a écrit :
AMDG
On 02/10/2018 03:32 AM, Raffi Enficiaud via Boost wrote:
Le 09.02.18 à 20:08, Steven Watanabe via Boost a écrit :
<snip> In general, I think that: - interfaces that take boost/std::xxx as a parameter should accept both.> - interfaces that return boost/std::xxx are hard and I don't know how to handle them.
Yes. You are starting/proposing a recipe for ABI compatibility, which is the target of my concerns. Now:
* do you think we can elaborate more on this? For instance, same type of problem should apply to template parameters as well, exceptions, new keywords...
template parameters are not a major issue, as you can just accept whatever the user gives you. If you're explicitly instantiating the template in the separately compiled library, it should be safe to instantiate both versions, and the one that isn't used will just be ignored template <class some_struct> class my_template;
If some_struct is a class, it should be the same in every translation units, isn't it. Same more or less for my_template. By induction, everything that is pulled by those 2 entities should have the same implementation if they have exposed/public symbol and they land in different compilation units. In the case where this template + template parameter is seen in two different shared or static libraries, I believe they share the same symbol, while maybe implementing something different. That means * enforcing the use of the same language variant for the compilation of a library and all its dependencies. I believe this is what is done in the Debian community (please correct me if needed) * or enforce the ABI compatibility, which is difficult for developers, as the language does not provide enough semantics on this, and a lot of know-how seems to be compiler or platform specific. * or carefully crafting the visibility of each of those symbols, to lower the risk of having the same symbol with different definitions, which let the problem of static libraries the same
It's probably okay to throw different exceptions as the default assumption in C++ is that any function can throw any exception.
Exceptions or return type, the problem isn't it the same? the exception thrown should be ABI compatible, isn't it? Moreover, exceptions should have public visibility I believe.
A function that has the same mangled name in different standards and is defined in multiple translation units (i.e. inline functions or function templates) must be identical regardless of the standard.
Do you know if at least the linker emits an error or a warning if, same symbol exists but with different definition? I really have not a single clue, but it seems that this is a grey area where people just "hope" as you said and have little control.
Note that to be strictly correct it is not enough for the function to have the same effective behavior, as the optimizer is permitted to make assumptions based on the actual implementation. Also note that this is the hardest thing to get right and most code that tries to be ABI compatible across multiple standards tends to fudge it and hope for the best.
...
If a function takes different parameters depending on the language standard, then it is a different function and both versions can coexist.
* do you think we can set up mechanisms that checks we are doing it right? (apart from trial and error, which has an exponential complexity in the number of types). I am just unaware of any tool or methodology that may help me doing it right.
Just build the library and test case with different standards. If it works, it's probably good enough.
Well, the second best to hope for is a compiler or a linker error. The worse is exactly something that presumably "works" with silent problems that, for instance, corrupt a stack. I was hoping for the second best.
- The internal implementation can pick one or the other. If you want ABI compatibility between C++03 and C++11, and the object crosses an ABI boundary, that means always using boost::xxx.
I was hoping for another solution ... To me, that means that long run, boost is implementing a side, non-compatible, C++ standard.
Pick your poison. It's impossible to use only std::xxx in the ABI while still being link-compatible with C++03. Personally, I can live with requiring a consistent std version, in which case, this is not a problem.
From all this conversation, I learned at least
* that there is a real problem that is not just between the keyboard and the chair (that is good for my ego ...) * cross dialect APIs should be avoided, which was not obvious * having API on different dialect is ok as long as it is a partition of the API, * that static or shared libraries naming conventions might help the user avoiding mistakes, but means that all dialect variant should appear in the name This is what I think is a partition: /////// class helper_cpp03 { void somemethod(boost::function< void () > f); }; #if CPP11 class cpp11 { template <class T> void somemethod2(T&& v) { helper_cpp03 inst; // bind can talk to boost::function inst.somemethod( std::bind(&X::C, std::forward(v)) ); } }; #endif /////// The two APIs can talk together, no symbol is modified depending on the dialect. The API in the C++11 case is a superset of the one in C++03, I can only add symbols, not modify C++03 existing ones. This is not a partition: /////// class helper { #if CPP11 void somemethod(std::function< void () > f); #else void somemethod(boost::function< void () > f); #endif }; class myclass { template <class T> #if CPP11 void somemethod2(T && v) { inst.somemethod( std::bind(&X::C, std::forward(v)) ); } #else void somemethod2(T const & v) { inst.somemethod( boost::bind(&X::C, v) ); } #endif }; /////// While this is a bit exaggerated in the example, I think this type of code might happen: code is changed depending on the dialect: * the interface is changed * the way those two entities exchange data is changed (boost::bind vs std::bind) Does this make sense?
For Boost.Test my overall recommendation is: always use boost::function, but use std::bind when it is available.
The little story behind boost::bind was that, I believe around ~1.60, I encountered issues between std::bind and boost::function for moveable parameters. Maybe I was doing it wrong at that time, now it seems to be fixed (I tested). But I think the problem remains.
In the worst case, you can use std::bind -> std::function -> boost::function.
This initial problem is fine now, it works as many other mentioned, std::bind being fully compatible with boost::function (which I was not aware of).