Skip to content

Decide the string parameter convention before the first tag #23

Description

@skipbit

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions