Roland Bock wrote:
On 2014-08-19 09:39, Gavin Lambert wrote:
I think we've had a terminology clash. By "backend" I thought you meant "the sqlpp11 class that knows how to talk to the native driver", not the native driver itself. Of course the native driver probably won't know how to drive an optional, nor should it be expected to.
There are several layers, I assume:
1. User code 2. sqlpp11 database-independent frontend 3. sqlpp11 database-specific connector 4. native database library
Perfectly correct :-)
Between layers 3 & 4 obviously you have to use whatever the native library supports, which is unlikely to be boost::optional (but still possible in some cases). So you might have to provide a raw int64_t pointer to the database up front, and translate from a int64_t pointer and an "is this null" method call (or a bool*) to a boost::optional when it calls you back saying the complete row is ready. (I'm assuming this is asynchronous, otherwise it's easier.) As of today, it is synchronous.
But between layers 1 & 2 and 2 & 3 you'd only have boost::optionals. I can see the reasoning for 1&2. And I understand that it can be done with 2&3 of course, but I am not sure there is a benefit.
As I see it, the benefit would be the use of the standard-approved way of handling values that could be invalid instead of using a library-specific handling. But I think optionals wouldn't be safe. Correct me if I'm wrong. If optionals were used without a check for validity and the unexpected NULL value was set in the DB (maybe by mistake), it could result in segmentation fault. Of course assuming that the macro BOOST_ASSERT() wouldn't be expanded to some exception throw. This could lead to some security vulnerabilities in apps using this library.
Otherwise how does the backend return a NULL value? The backend is called with two parameters, one pointer for the value, the other for the is_null information. So once you know that those have been filled in, you can translate it into a boost::optional to be returned to the higher layer. It does mean the value has to be copied (unless boost::optional has had move assignment added since I last looked), but you'd be doing that anyway for std::string so this doesn't seem any worse than that. I can see that for the 1&2 interface, but not necessarily for 2&3 since then I would have two copies of the string: once when obtaining the value from 4 and copying it into 2&3, one for copying it from 2->1. The above assuming that RVO can't be applied, rvalue refs aren't supported by the compiler, move semantics isn't supported by boost::optional and swap() isn't explicitly used. As of now, I create a temporary string when the value is requested in the 1&2 interface.
So there always must be some temporary object. You could directly create boost::optionalstd::string using InPlaceFactory (http://www.boost.org/doc/libs/1_56_0/libs/optional/doc/html/boost_optional/i...) or as I mentioned earlier create default-constructed string within optional and assign to it (assuming it's a valid way of modifying an optional). But as I wrote above, I'm not sure if supporting "raw" optionals would be safe. Regards, Adam