Context
split() took std::string_view parameters for a short period and was reverted in #22. The reason was that dross cannot pass its own types to such a parameter: string, number, boolean, data, timezone, timestamp (plus its two nested classes) and path declare operator std::string() but no conversion to std::string_view. Reaching a string_view parameter from them would take two user-defined conversions, which the language does not perform.
The revert fixed the breakage, but it did not settle the wider question. include/ and src/ currently declare 82 const std::string& parameters (28 in headers, 54 in sources), and whether they should move to std::string_view is still open.
No version has been tagged and no release has been published, so the ABI can still change freely. Once a tag exists, the present signatures become part of the contract. This should be decided before the first tag.
Options
1. Migrate to std::string_view
The signatures cannot be designed in isolation from the types. Only string and number hold a stable std::string and could hand out a view. timezone, timestamp, date_part and time_part build their representation on every call, and path::operator std::string() returns _path.string() — a copy of the underlying std::filesystem::path. Handing out a view from these requires adding storage and defining when it is invalidated.
Worth measuring first: path holds _path alone and exposes no mutator, so a representation computed at construction time may need no invalidation rule at all. The other types have not been checked for this.
2. Keep const std::string&
Record the reason and close the question. Every caller that passes a dross type already pays for one std::string construction today, and that does not change.
3. Introduce a parameter wrapper
A wrapper used as split(const string_arg&, const string_arg&) lets callers pass dross types directly with no explicit conversion at the call site, while std::string, string literals and std::string_view still travel without a copy.
This compiles and behaves as intended on GCC 13.3.0 (libstdc++ 13) and Clang 22.1.8, both with -std=c++23, across six call shapes. Three conditions turned out to be necessary — the naive version does not compile at all:
- One
string_view constructor is not enough. Building the wrapper from a literal or from std::string would need two user-defined conversions — the very mechanism that broke the string_view signature in the first place. Dedicated const char* and const std::string& constructors are required.
- The copy constructor must be written, not deleted. The view member points into the owned string, so deleting the copy looks like the safe choice, but it leaves reference binding unable to materialise the temporary and kills the implicit conversion entirely.
- The template constructor needs a
requires clause excluding types already convertible to std::string_view; otherwise literals and std::string fall into the owning branch and construct a std::string for nothing.
What this option does not buy: passing a dross type still costs one std::string construction, because operator std::string() returns by value. It buys the accuracy of the signature, not the removal of the copy. The wrapper is also an argument-only type — holding one in a variable built from a temporary std::string dangles, the same hazard std::string_view already carries.
Related
join() builds its result with a + delimiter + b. If the delimiter becomes a view, that expression needs operator+ between std::string and std::string_view, which arrived in C++26 (P2591) while this project requires C++23. The implementation has to be rewritten under option 1 or 3.
Context
split()tookstd::string_viewparameters for a short period and was reverted in #22. The reason was that dross cannot pass its own types to such a parameter:string,number,boolean,data,timezone,timestamp(plus its two nested classes) andpathdeclareoperator std::string()but no conversion tostd::string_view. Reaching astring_viewparameter from them would take two user-defined conversions, which the language does not perform.The revert fixed the breakage, but it did not settle the wider question.
include/andsrc/currently declare 82const std::string¶meters (28 in headers, 54 in sources), and whether they should move tostd::string_viewis still open.No version has been tagged and no release has been published, so the ABI can still change freely. Once a tag exists, the present signatures become part of the contract. This should be decided before the first tag.
Options
1. Migrate to
std::string_viewThe signatures cannot be designed in isolation from the types. Only
stringandnumberhold a stablestd::stringand could hand out a view.timezone,timestamp,date_partandtime_partbuild their representation on every call, andpath::operator std::string()returns_path.string()— a copy of the underlyingstd::filesystem::path. Handing out a view from these requires adding storage and defining when it is invalidated.Worth measuring first:
pathholds_pathalone and exposes no mutator, so a representation computed at construction time may need no invalidation rule at all. The other types have not been checked for this.2. Keep
const std::string&Record the reason and close the question. Every caller that passes a dross type already pays for one
std::stringconstruction today, and that does not change.3. Introduce a parameter wrapper
A wrapper used as
split(const string_arg&, const string_arg&)lets callers pass dross types directly with no explicit conversion at the call site, whilestd::string, string literals andstd::string_viewstill travel without a copy.This compiles and behaves as intended on GCC 13.3.0 (libstdc++ 13) and Clang 22.1.8, both with
-std=c++23, across six call shapes. Three conditions turned out to be necessary — the naive version does not compile at all:string_viewconstructor is not enough. Building the wrapper from a literal or fromstd::stringwould need two user-defined conversions — the very mechanism that broke thestring_viewsignature in the first place. Dedicatedconst char*andconst std::string&constructors are required.requiresclause excluding types already convertible tostd::string_view; otherwise literals andstd::stringfall into the owning branch and construct astd::stringfor nothing.What this option does not buy: passing a dross type still costs one
std::stringconstruction, becauseoperator std::string()returns by value. It buys the accuracy of the signature, not the removal of the copy. The wrapper is also an argument-only type — holding one in a variable built from a temporarystd::stringdangles, the same hazardstd::string_viewalready carries.Related
join()builds its result witha + delimiter + b. If the delimiter becomes a view, that expression needsoperator+betweenstd::stringandstd::string_view, which arrived in C++26 (P2591) while this project requires C++23. The implementation has to be rewritten under option 1 or 3.