[Libpqxx-general] operator== for fields
Carlos Moreno
moreno_pg at mochima.com
Tue Sep 25 19:55:53 UTC 2007
Hi Jeroen, thanks for your reply!
>> I would probably go as far as to suggest that class result::field
>> should have implicit conversion to std::string --- as much as
>> implicit conversion operators are widely seen as an almost
>> unconditionally bad idea, I feel that in this case, result::field
>> should almost be a typedef for std::string (of course, that would
>> mean sacrificing a lot of interesting functionality, so it is good
>> as it is, being a class of its own).
>>
>> But intuitively, the data that we get from a database is a string;
>> in the docs, I see some discussion about the issues of "intuition"
>> often telling us that things would behave as they behave in the
>> SQL realm (since after all, what we're getting is a representation
>> of data that lives in the database).
>
> You raise an interesting issue. I can see the usefulness of a conversion
> to string, but I'm not sure I have a solid grasp of the risks yet:
Yes, as I said, I'm kind of "borderline" on this one --- I'm tempted to
go in favor of the implicit conversion, but instinct immediately kicks in
and flags the potential risks of irreversibly screwing up something...
> i. Performance risks: we may be tempting the user to make repeated
> implicit conversions when it might be better to keep a stringified field
> value in a variable. OTOH I've learned over time that initially libpqxx
> went too far in protecting programmers from themselves. Maybe it's not
> something I should worry about.
I would agree on not worrying about this one --- if the programmer is
sufficiently "non-competent" to do gratuitous conversions, that's their
problem, not the library's; after all, a sufficiently non-competent
programmer could loop through the results to get a maximum value of
the field and then loop again to find the lowest value for the same
field, and then loop again counting how many values are between those
two (between lowest and highest)... And I'm betting that you don't
feel bad because your library allows them to do that! :-)
> ii. Choice of string types: there are other standard string types besides
> std::string, which I was hoping to support better in the future, and I
> wonder if things might not get a bit messy if we start picking defaults.
I have to disagree with this point --- are you talking about different
instantiations of basic_string? (wchar_t, Unicode/UTF sorts of things?)
Either way, I don't see why would it be a problem to pick *a default*...
The C++ language does have a default; your library aims at compatibility
with C++, its library, and its native idioms --- I think picking a default
for conversion to string makes sense provided that that default is the
same C++ default --- std::string.
The user can still do an explicit conversion with the template method
field::as() --- db[row]["fullname"].as<other_string_type>() ... ).
> iii. Mistaken conversions. Are there any cases where you might
> accidentally get conversions you don't want?
Yep --- the usual monster, I have to agree. Though notice that being
std::string *a class*, the risk is greatly reduced --- I mean, implicit
conversion to char * would be catastrophic (and I mean putting aside
any memory management issues):
if (db[i][0] - 10 != 0)
The above expression is catastrophic if db[i][0] implicitly converts to
"char *" --- pointer minus offset, then compared against NULL (the value
0 as a pointer) ...
However, being db[i][0] implicitly converted to std::string, the above
line would fail to compile --- class std::string already limits what
you can do with it; so it would be almost impossible that a conversion
is chosen by the compiler as the only way to get around an expression
that is otherwise invalid and make it valid; if it is not valid for
class pqxx::field, it wouldn't be valid for class std::string.
We could ask for opinions/ideas in comp.lang.c++.moderated --- I'm sure
if there is a way that things can go wrong, someone will point it out
in there --- and then you can decide if such situation is applicable
in this context (it might not be!).
> It'd be a bit odd to
> have some string operators work but not others, or to have comparisons to
> some types but not to others.
Why?? The type std::string has comparisons to some types but not to
others;
so does the type char * --- it all comes down to the issue of "what does
a pqxx::field object conceptually represent?" --- I would be tempted to
answer that with: "it represents the data from a field that is part of a
recordset returned by a query, expressed in the form of a string, but
convertible to other types depending on what the data type is in the
database realm" ...
I know, you're the one most qualified to answer this question, as you are
the one that came up with the design and the design criteria --- but still,
the above seems fairly consistent with what you have, and IMHO reasonable
intuitive --- the data on the client side *is* received in the form of
strings (it is "serialized" or "flattened" data by nature) ... But then,
class pqxx::field does offer conversion to other data types, which will or
will not work depending on the nature of that data --- if we're reading
a field "counter", it makes sense that we could do f.as<int>() if we need
to (e.g., to do greater-than or less-than)
Perhaps the one delicate issue would be the same issue with operator==
for two fields: handling on NULLs --- though I'd say there's a very easy
way out: comparison of a field that is null against *any* string value
results in "not equal" (that is, operator== returns false, operator!=
returns true) --- this seems to violate a rule from the SQL realm in
which comparison of a NULL against anything results in a value of NULL;
seems a bit pointless to try to respect a rule that is fundamentally
incompatible with C++ itself --- a comparison yields a boolean value,
and it can not yield anything else, as C++ is a statically- and somewhat
strictly-typed language. And again, class pqxx::field does not represent
the SQL-realm data as it is in the database: it represents the "flattened"
data that was received *in the form of string* as the result of a query
to a database backend.
> I'll take an easy way out and ask: would there be anything actually wrong
> with something like
>
> std::string s(const pqxx::result::field &f)
> {
> return f.as<std::string>();
> }
>
> and then accessing fields as
>
> if (s(data[i][0]) == "foo")
Well, the problem I see is that no, there is nothing actually wrong, but
there is nothing actually interesting either :-(
That is, between the above and data[i][0].as<string>() == "foo" , I would
choose the solution with as<string>(). But it does feel frustrating --- it
kind of feels that the abstraction (having the field return an object of
class field instead of a string) is being respected only for the sake of
the abstraction, and that it is only adding limitations; I know that this
is not the case --- far from it; what I mean is that when we need to
compare against a given literal text value (which is, IMHO, often enough),
it does feel that way; the data, in the context that we're handling it,
is naturally that: a fragment of text, and class field gets in the way and
does not let us compare it easily and naturally.
Regards,
Carlos
--
More information about the Libpqxx-general
mailing list