Skip to content

Commit

Permalink
String conversion to string_view. More binary types.
Browse files Browse the repository at this point in the history
Fixes: #694
Fixes: #827

Making much broader use of concepts.  String conversions now accept any
contiguous range of `std::byte` as binary data.  Traits specialisations
for integer and floating-point types are simpler now.  And you can now
just convert from string to `std::string_view` (or `char const *`), so
long as you don't access it after the original string's lifetime ends.
jtv committed Jan 13, 2025

Verified

This commit was signed with the committer’s verified signature.
oddgrd Oddbjørn Grødem
1 parent a9685ef commit 40b360c
Showing 9 changed files with 213 additions and 340 deletions.
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
8.0.0
- C++20 is now the oldest C++ version that libpqxx supports.
- "String conversion" to `std::string_view` is now supported. (#694)
- **Beware lifetime** when "converting" a string to `std::string_view`!
- Binary data can be any `std::contiguous_range` of `std::byte`. (#925)
- Retired `binarystring` and its headers. Use `blob` instead.
- Retired `connection_base` type alias. Use `connection`.
- Retired `pqxx::encrypt_password()`. Use the ones in `pqxx::connection`.
27 changes: 17 additions & 10 deletions include/pqxx/doc/datatypes.md
Original file line number Diff line number Diff line change
@@ -12,8 +12,8 @@ You can "teach" libpqxx (in the scope of your own application) to convert
additional types of values to and from PostgreSQL's string format.

This is massively useful, but it's not for the faint of heart. You'll need to
specialise some templates. And, **the API for doing this can change with any
major libpqxx release.**
specialise several templates. And, **the API for doing this can change with
any major libpqxx release.**

If that happens, your code may fail to compile with the newer libpqxx version,
and you'll have to go through the `NEWS` file to find the API changes. Usually
@@ -107,13 +107,16 @@ namespace near the top of your translation unit, and pass the type as an
argument.

The library also provides specialisations for `std::optional<T>`,
`std::shared_ptr<T>`, and `std::unique_ptr<T>`. If you have conversions for
`T`, you'll also automatically have conversions for those.
`std::shared_ptr<T>`, and `std::unique_ptr<T>` (for any given `T`). If you
have conversions for `T`, you'll also automatically have conversions for those.


Specialise `type_name`
----------------------

(This is a feature that should disappear once we have introspection in the C++
language.)

When errors happen during conversion, libpqxx will compose error messages for
the user. Sometimes these will include the name of the type that's being
converted.
@@ -142,12 +145,16 @@ Specialise `nullness`
---------------------

A struct template `pqxx::nullness` defines whether your type has a natural
"null value" built in. If so, it also provides member functions for producing
and recognising null values.
"null value" built in. For example, a `std::optional` instantiation has a
value that neatly maps to an SQL null: the un-initialised state.

If your type has a value like that, its `pqxx::nullness` specialisation also
provides member functions for producing and recognising null values.

The simplest scenario is also the most common: most types don't have a null
value built in. There is no "null `int`" in C++. In that kind of case, just
derive your nullness traits from `pqxx::no_null` as a shorthand:
derive your nullness traits from `pqxx::no_null` as a shorthand: This tells
libpqxx that your type has no null value of its own.

```cxx
// T is your type.
@@ -196,9 +203,9 @@ where `NULL <> NULL`). Or `T` may have multiple different null values. Or `T`
may override the comparison operator to behave in some unusual way.
As a third case, your type may be one that _always_ represents a null value.
This is the case for `std::nullptr_t` and `std::nullopt_t`. In that case, you
set `nullness<TYPE>::always_null` to `true` (as well as `has_null` of course),
and you won't need to define any actual conversions.
This is the case for `std::nullptr_t` and `std::nullopt_t`. In a case like
that, you set `nullness<TYPE>::always_null` to `true` (as well as `has_null`
of course), and you won't need to define any actual conversions.
Specialise `string_traits`
45 changes: 7 additions & 38 deletions include/pqxx/field.hxx
Original file line number Diff line number Diff line change
@@ -344,44 +344,13 @@ template<> inline bool field::to<char const *>(char const *&obj) const
}


template<> inline bool field::to<std::string_view>(std::string_view &obj) const
{
bool const null{is_null()};
if (not null)
obj = view();
return not null;
}


template<>
inline bool field::to<std::string_view>(
std::string_view &obj, std::string_view const &default_value) const
{
bool const null{is_null()};
if (null)
obj = default_value;
else
obj = view();
return not null;
}


template<> inline std::string_view field::as<std::string_view>() const
{
if (is_null())
internal::throw_null_conversion(type_name<std::string_view>);
return view();
}


template<>
inline std::string_view
field::as<std::string_view>(std::string_view const &default_value) const
{
return is_null() ? default_value : view();
}


/// Specialization: `to(zview &)`.
/** This conversion is not generally available, since the general conversion
* would not know whether there was indeed a terminating zero at the end of
* the string. (It could check, but it would have no way of knowing that a
* zero occurring after the string in memory was actually part of the same
* allocation.)
*/
template<> inline bool field::to<zview>(zview &obj) const
{
bool const null{is_null()};
Loading

0 comments on commit 40b360c

Please sign in to comment.