I have two concerns here, one technical and one philosophical.
The first one is, as Peter has said, is implicit conversions. Specifically, introducing ambiguity.
We had to resolve ~10 ambiguous cases when we added string_view to the standard library.
Adding a 3rd type into the mix will almost certainly add new ones.
The second one is about the long term use of boost::string_view.
My opinion is that it is a transitional library, useful for people on older systems which do not have std::string_view.
All things being equal, we (boost) should be encouraging people to transition away from boost::string_view to std::string_view when they can.
[ Note that I don’t think that’s necessarily true for boost::shared_ptr, which has additional functionality above and beyond std::shared_ptr. ]
On the other hand, boost::string_view has additional functionality w.r.t std::string_view, because of some odd (to me) choices by the standard committee (removing comparisons, for example).
— Marshall
> This is not what we want. We want a converting string_view that is
> called
> boost::string_view. The way to get it is to make a converting
> string_view so
> that we demonstrate the viability of the idea, then make
> boost::string_view
> be it.
From the discussion, what i understand is what you want ideally is an
std::string_view for c++11/14.
> (Before you suggest that the library should use boost::string_view
> under C++14 and std::string_view under C++17, note that this makes it
> impossible to build the library under C++14 and use it from C++17,
> which is a common scenario when using the system-provided Boost
> package.)
I'm sorry but i'm gonna raise that suggestion. While I agree it does
not solve the general use case, it looks to me like an acceptable
solution for boost.json. IIRC boost.json is advertised as a nearly
header-only library, where you have to include an hpp file once in your
project. So ABI mismatch should not hold there (by the way, i just
checked that it is the way it is packaged on my debian stable, no
binary for json is included).
Maybe some #defines could be added to avoid some mismatches, or to
force an ABI independantly from language level.
Regards,
Julien
I'm certainly biased toward our own use cases. Maintaining a medium
sized (<1Mloc) that needs to compile with compilers as old as gcc4.8 as
well as new compilers such as gcc11, and several boost versions, we
already have our base library filled with things such as:
#if __cplusplus >= 201703L
using filesystem = std::filesystem;
#else
using filesystem = boost::variant;
#endif
And then our code works on the compile time selected type. They're
usually close enough so that it works (and if it doesn't, we write the
required helper functions). The fact that we're always compiling the
whole target (embedded world) certainly helps avoiding ABI issues.
I don't know how widespread doing this is. But i would be surprised if
it were really uncommon.
> Preprocessor-conditioned overloads *do* work for arguments (though is
> an additional implementation burden since you would have to provide
> multiple implementations of the same thing), but doesn't help for
> return types, since sadly the return type does not participate in name
> mangling or overload resolution. While not impossible to have
> ABI-preserving alternative methods varying by return type, having one
> well-defined return type is really the best solution for that.
I'm not sure i was being clear here. I'm not talking about overloads,
but about replacements. Something like
#if __cplusplus >= 201703L
using string_view = std::string_view
#else
using string_view = boost::string_view
#endif
The interfaces are close enough so that there shouldn't be a need to
implement things twice in boost::json, which would always use
boost::json::string_view. You either got the c++14 boost::string_view
api, or the c++17 std::string_view api, but never both.
As a user, i fully supports what Vinnie said: users want to use stl
types as much as possible, not their boost equivalents. Every time a
boost library forces its users to use boostified versions for little or
no reasons, it is somewhat hostile to users. So a long term goal should
be to remove these, not add more...
Regards,
Julien
Looking at where the winds are blowing in this discussion, we'll have to
indeed add a string_view implementation to Json. Problems with other
approaches were already presented by Peter, so I won't repeat them. So, there
will be (at least one) new string_view in Boost, just not in Core.
ODR violations can be prevented with namespace aliases.
#if __cplusplus >= 201703L
#define BOOST_JSON_NAMESPACE json_with_std_string_view
namespace BOOST_JSON_NAMESPACE {
using string_view = std::string_view;
}
#else
#define BOOST_JSON_NAMESPACE json_with_boost_string_view
namespace BOOST_JSON_NAMESPACE {
using string_view = boost::string_view;
}
#endif
using json = BOOST_JSON_NAMESPACE;
namespace BOOST_JSON_NAMESPACE {
// Library contents go here.
}
That said, compiling parts of the same program with different C++
standards is asking for trouble. I would expect ODR violations from the
standard library in that case.
--
Rainer Deyke (rai...@eldwood.com)
Any time you're using a system-provided C++ library, you're doing this.
And that includes system-provided Boost.
It's fairly commonplace to compile an app with a higher C++ standard
than was used for the system libraries, because those tend to be built
with conservative defaults.
The reverse is also possible, but less common, since if a library
requires a higher C++ standard to build itself then this usually means
consumers of the library need at least that or higher too. Still, this
is not guaranteed; a library might be using some features only in its
source files and not its public headers.
And I've seen quite a few libraries where only a subset of source files
are compiled with a higher standard level than the others.
(Things are a bit different here between *nix and Windows. Most *nix
distros designate a One True C++ Compiler™ for their current release,
including a default C++ standard level and library, and most things are
built to this, so it's easier to rely on prebuilt system libraries.
Whereas on Windows there is no standard C++ compiler or library, so it's
more commonplace to either "build the world" and distribute private
binaries or to rely on the C or COM ABIs instead of the C++ ABI, since
those are more stable.)
I’m here; I’m reading.
I even responded to this thread yesterday; though I’m not sure anyone noticed.
— Marshall
Yeah and that's why we need this new string_view to go into core,
protestations against the lack of formal review notwithstanding. So we
can sort out these conversions.
> The second one is about the long term use of boost::string_view.
> My opinion is that it is a transitional library, useful for people on older systems which do not have std::string_view.
> All things being equal, we (boost) should be encouraging people to transition away from boost::string_view to std::string_view when they can.
Equivalent to saying that "we (boost) should be encouraging people to
transition away from C++11 in favor of C++17." Until C++11 has less
than 5% of the market, I certainly can't be requiring C++17 for any of
my libraries. And as of the latest language survey, C++11 is still
firmly ensconced as a Very Popular Version of the language.
This leaves us with a big problem. We are encountering headwinds
evolving a component that is vital for existing C++11 libraries.
> [ Note that I don’t think that’s necessarily true for boost::shared_ptr
> which has additional functionality above and beyond std::shared_ptr. ]
The set of use-cases for string_view is considerably larger than the
set of use-cases for shared_ptr in argument lists. Furthermore
std::shared_ptr is available in C++11 (which realistically is the
minimum that new libraries should target).
Thanks
Also, it's not quite this straightforward -- since both of these are
also implicitly constructable from char pointers and std::strings, you
have to add constructor overloads for these conversions too, otherwise
it's more annoying for the consumer.
(Even if the compiler permitted two consecutive implicit conversions,
which it doesn't, that would be ambiguous in this case -- which is *why*
it doesn't.)
It's relatively trivial to add these extra constructors as well,
although the end result is that you've almost entirely duplicated the
implementation of string_view anyway, just without the string-like
convenience methods (which may also be annoying for the consumer).
Having the wrapper inherit from one of the two (naturally, this has to
be boost::string_view) fixes both of these problems and reduces the
amount of code required (as I think Peter suggested himself at one
point). It also more clearly indicates which operations are "missing"
from boost::string_view. I don't recall why he didn't like this option
either, other than "I already wrote the full replacement" (implied, not
quoted).