[facets] strict_num_put design problem

Robert Ramey has evicted my facets for non-finite numbers from the serialization namespace :-) They clearly don't belong there. So I've moved them to a new facets namespace. I have moved the code in the vault from the Serialization folder to a new Facets folder. And I have started a new thread. ------------------------------------------------------------------------- There is a serious problem with the design of the facets strict_num_put and strict_num_get. These facets are supposed to set the failbit of the stream when they encounter non-finite numbers. This may not be a good idea, because Facets derived from std::num_put and std::num_get should do formatting and parsing, not data validation. The designers of the standard library made the following assumptions: 1. Facets derived from std::num_put do formatting. 2. Facets derived from std::num_get do parsing. 3. Formatting always succeeds. 4. Parsing may fail. This led them to the following design decisions: 5. Facets derived from std::num_put do not have access to the stream state. 6. Facets derived from std::num_get do have access to the stream state. The facets strict_num_put and and strict_num_get violate assumptions 1 and 2. The facet strict_num_put does validation and formatting. The facet strict_num_get does parsing and validation. This makes it difficult to implement strict_num_put. What should it do when validation fails? It can not set the failbit of the stream. The only thing it can do, is to throw an exception. The stream will catch that exception and set the badbit! At least with VC++ 7.1. Why? The stream probably assumes that formatting facets do not throw exceptions, so it concludes that the exception was thrown by the stream buffer. When the stream buffer is corrupted, the stream sets the badbit. However, setting the badbit when data validation fails, is a bad idea. Suggested solution: Throw out the facets strict_num_put and strict_num_get. Comments? The documentation of the library has been updated to reflect this problem. --Johan Råde

Johan Råde wrote:
Robert Ramey has evicted my facets for non-finite numbers from the serialization namespace :-) They clearly don't belong there. So I've moved them to a new facets namespace. I have moved the code in the vault from the Serialization folder to a new Facets folder.
Probably should have spoken up earlier, but I'm wondering if boost::io wouldn't be a better choice. We already have some things in this namespace... Jeff

Jeff Garland wrote:
Johan Råde wrote:
Robert Ramey has evicted my facets for non-finite numbers from the serialization namespace :-) They clearly don't belong there. So I've moved them to a new facets namespace. I have moved the code in the vault from the Serialization folder to a new Facets folder.
Probably should have spoken up earlier, but I'm wondering if boost::io wouldn't be a better choice. We already have some things in this namespace....
Jeff
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Excellent idea. --Johan

Jeff Garland wrote:
Johan Råde wrote:
Robert Ramey has evicted my facets for non-finite numbers from the serialization namespace :-) They clearly don't belong there. So I've moved them to a new facets namespace. I have moved the code in the vault from the Serialization folder to a new Facets folder.
Probably should have spoken up earlier, but I'm wondering if boost::io wouldn't be a better choice. We already have some things in this namespace....
Jeff
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
I have updated the code and the documentation accordingly. I have moved the zip file to the Input - Output folder in the vault. Now, how do I remove the Facets folder from the vault? --Johan

Johan Råde wrote:
3. Formatting always succeeds.
Hmmm - formatting a NaN results in something like "+inf". Personally, I wouldn't characterize that as a success. <snip>
This makes it difficult to implement strict_num_put. What should it do when validation fails? It can not set the failbit of the stream. The only thing it can do, is to throw an exception. The stream will catch that exception and set the badbit! At least with VC++ 7.1.
Its better than nothing. Personally I would be fine with this.
Why? The stream probably assumes that formatting facets do not throw exceptions, so it concludes that the exception was thrown by the stream buffer. When the stream buffer is corrupted, the stream sets the badbit.
However, setting the badbit when data validation fails, is a bad idea.
How come?
Suggested solution: Throw out the facets strict_num_put and strict_num_get.
Comments?
You've already investigated the issue more than I have so I'm happy to defer to your judgement. On the other hand, the attempt to serialize a NaN is an indication of a broken program I would like to see it trapable. On the other hand, it seems that I'm the only one who thinks this. Oh well.
The documentation of the library has been updated to reflect this problem.
thanks for taking this on. Robert Ramey

Robert Ramey wrote:
Johan Råde wrote:
Hmmm - formatting a NaN results in something like "+inf". Personally, I wouldn't characterize that as a success.
Formatting positive infinity results in the string "inf". Parsing the string "inf" results in positive infinity. Formatting negative infinity results in the string "-inf". Parsing the string "-inf" results in negative infinity. Formatting a positive NaN results in the string "nan". Parsing the string "nan results in a positive NaN. Formatting a negative NaN results in the string "-nan". Parsing the string "-nan" results in a negative NaN. I would characterize that as success.
<snip>
This makes it difficult to implement strict_num_put. What should it do when validation fails? It can not set the failbit of the stream. The only thing it can do, is to throw an exception. The stream will catch that exception and set the badbit! At least with VC++ 7.1.
Its better than nothing. Personally I would be fine with this.
You get platform dependent behavior. VC++ 7.1 sets the badbit. Other platforms may set the failbit. (Hopefully no platform will propagate the exception.) Personally I would also be fine with that. But maybe it is not up to Boost standards.
Why? The stream probably assumes that formatting facets do not throw exceptions, so it concludes that the exception was thrown by the stream buffer. When the stream buffer is corrupted, the stream sets the badbit.
However, setting the badbit when data validation fails, is a bad idea.
How come?
The failbit should be set. Setting the badbit should mean that transporting bytes to the external device has failed.
Suggested solution: Throw out the facets strict_num_put and strict_num_get.
Comments?
You've already investigated the issue more than I have so I'm happy to defer to your judgement.
Maybe I'm too rash.
On the other hand, the attempt to serialize a NaN is an indication of a broken program I would like to see it trapable. On the other hand, it seems that I'm the only one who thinks this. Oh well.
Is serializing infinity a sign of a broken program? Is serializing a NaN a sign of a broken program? It depends on the context. (In the program I'm working on now, serializing a NaN is a sign that something is broken, while serializing infinity is not.) What is needed is maybe not different classes, but two boolean flags: trap_infinity and trap_nan. And maybe we have to accept that the exact behavior when trapping is platform dependent. There does not seem to be any way around that.
thanks for taking this on.
That is the least I can do. Your serialization library has been of immense value to me. --Johan Råde
participants (3)
-
Jeff Garland
-
Johan Råde
-
Robert Ramey