List:MySQL++« Previous MessageNext Message »
From:Warren Young Date:February 15 2008 5:16pm
Subject:Re: minor compile-time issue with mysqlpp::null
View as plain text  
Andrej van der Zee wrote:
> But yes, declaring a constant:
> const mysqlpp::Null<mysqlpp::sql_int_unsigned>
> msr_null(mysqlpp::null);

No, I meant change the operator method to be const:

  #if !defined(DOXYGEN_IGNORE)
  // Doxygen will not generate documentation for this section.
-	template <class Type> operator Type()
+	template <class Type> operator Type() const
  		throw BadNullConversion();
  		return Type();

This change will appear in the next release.  I just wanted you to know
you could easily change your copy to test it before then.

> Still don't really understand why it doesn't work in a
> "a?a:b" embedded statement 

Due to the data type conversions required in that statement, it was
trying to call null_type::operator Type(), but mysqlpp::null is a const
object, so it can only call it if the method is marked as const.  It was
an error for it to not be so marked before because it doesn't actually
modify the null_type object.

This bug has existed since at least 1.7.9, for what it's worth.

I just realized something, however: if you apply this change and try to
use the code you showed, it'll throw BadNullConversion.  (Study the diff
above.)  This is really funny, actually.  This operator exists to detect
incorrect library use, but it was impossible to trigger it through the
mysqlpp::null instance.  Sigh.... :)

The problem is that for this:

     unsigned msr_id = ...; // some value
     ssqls.nullable_member = msr_id ? msr_id : mysqlpp::null;

to be a consistent statement in C++, the compiler has to coerce all the
data types to the same type.  Having taken care of the constness issue,
your compiler is now allowed to convert mysqlpp::null to unsigned int,
but having done it, you will get an exception.

Bottom line, the code looks like it should work, but it's not right.
The more verbose way of writing it is correct because it uses the two
rvalues in different contexts, so they don't have to be the same type:

     if (msr_id) {
         ssqls.nullable_member = msr_id;
     else {
         ssqls.nullable_member = mysqlpp::null;

Perhaps the most compact, legal way to say this is:

     ssqls.nullable_member = msr_id;
     ssqls.nullable_member.is_null = !msr_id;

Legal it is, but I don't know about sensible.  It says that SQL null in
this context is the same as zero or Boolean false.  The point of SQL
null is that it is equal to nothing else, so why use it here?  Are you
sure you aren't just using new features because they're there?

As a result of this realization, I'm changing null_type so trying to do
this results in a different compile time error than the one you got
that's clearer.  I get this with g++ 4.1.2:

lib/null.h:55: error: ‘template<class
CannotConvertNullToAnyOtherDataType> mysqlpp::null_type::operator
CannotConvertNullToAnyOtherDataType() const’ is private

(Amusingly, the 'const' fix is still needed.)

Also, I'm documenting this anti-pattern in the reference manual.

minor compile-time issue with mysqlpp::nullAndrej van der Zee15 Feb
  • Re: minor compile-time issue with mysqlpp::nullWarren Young15 Feb
    • Re: minor compile-time issue with mysqlpp::nullAndrej van der Zee15 Feb
      • Re: minor compile-time issue with mysqlpp::nullWarren Young15 Feb
        • Re: minor compile-time issue with mysqlpp::nullAndrej van der Zee17 Feb
          • Re: minor compile-time issue with mysqlpp::nullWarren Young19 Feb