[Units] help with type-safe unit-specific conversions
I have been playing with Boost.Units as part of a work project recently, and I am not sure what the best approach to "demarshalling" values in a specific unit. For example, I would like to do something like: using namespace boost::units; struct A { enum { feet, kilometers }; quantitysi::length some_length; double foo(int type) { switch(type) { case feet: return quantity<feet>(some_length).value(); case kilometers: return quantity<kilometers>(some_length).value(); } } }; This approach is necessary for passing values to external libraries which do not (and probably will never) use Boost.Units. Is conversion_factor the best approach, or is there a way to achieve this implicitly using the type system? Thanks Justin
Presumably these external libraries expect their input to be in a specific unit. For maximal safety, you should use explicit construction of that unit in a wrapper. So, for example, you have some external function (not tested) : void doSomethingWithLength(double length /* expects meters */); create a wrapper function that expects a quantity (you could improve this by only overloading on units with length dimensionality) namespace wrap { template<Unit> void doSomethingWithLength(const quantity<Unit>& q) { return doSomethingWithLength((quantitysi::length(q)).value()); } } // namespace wrap Then you can call wrap::doSomethingWithLength with any unit having length dimension and the argument will be converted to meters and "demarshalled"... Matthias
I have been playing with Boost.Units as part of a work project recently, and I am not sure what the best approach to "demarshalling" values in a specific unit. For example, I would like to do something like:
using namespace boost::units;
struct A { enum { feet, kilometers };
quantitysi::length some_length;
double foo(int type) { switch(type) { case feet: return quantity<feet>(some_length).value(); case kilometers: return quantity<kilometers>(some_length).value(); } } };
This approach is necessary for passing values to external libraries which do not (and probably will never) use Boost.Units. Is conversion_factor the best approach, or is there a way to achieve this implicitly using the type system?
Thanks Justin _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
namespace wrap {
template<Unit> void doSomethingWithLength(const quantity<Unit>& q) { return doSomethingWithLength((quantitysi::length(q)).value()); }
} // namespace wrap
Then you can call wrap::doSomethingWithLength with any unit having length dimension and the argument will be converted to meters and "demarshalled"...
If I understand the way quantity works correctly, this will take a quantity on any length dimension and convert it to a quantity on si::length. What I am attempting to do is manage quantities in units. What I mean by marshalling and demarshalling is this: quantitysi::length marshall(double value) { return value * us::foot_base_unit(); } double demarshall(const quantitysi::length& value) { return quantity*feet*/>(value).value(); } If possible this is the approach I'd like to take, Thanks Justin
If I understand the way quantity works correctly, this will take a quantity on any length dimension and convert it to a quantity on si::length. What I am attempting to do is manage quantities in units. What I mean by marshalling and demarshalling is this:
quantitysi::length marshall(double value) { return value * us::foot_base_unit(); }
double demarshall(const quantitysi::length& value) { return quantity*feet*/>(value).value(); }
If possible this is the approach I'd like to take,
If you just want to do "unsafe" conversions back and forth like you've written, why don't you just do it directly? quantitysi::length::from_value(value) quantityus::length(value).value() The advantage of using wrappers like I suggested earlier is that you then are insulated from the units-incompatible libraries while preserving their interface and guaranteeing (assuming you've written the wrappers correctly) that you maintain dimensional correctness... Maybe I'm just not understanding your use case. Matthias
Maybe I'm just not understanding your use case.
We have a GUI which allows users to choose units (from a fixed set) and input values. The enumerations represent the unit set the user can choose. There are two cases to handle: 1) User changes a value directly - "marshall" their input into the type system, then push the unit-safe values into the model (as in model-view-control). 2) The model issues an update and the interface must update - take the unit-safe values from the model, "demarshall" then to doubles whose value corresponds to the users current type, and update the interface. I just didn't recognize how to coerce a specific unit type. This problem was solved via Mr. Watanabe's assistance, I just wanted to explain the case sufficiently. Thanks Justin
Is there a good way to do this? My server is a basic HTTP server, and if the client has disconnected while I'm still processing their request I'd like to abort the processing (e.g. no point doing work if the client is not there waiting for the data). I've read I can try to read from the socket, but in HTTP the client sends the request, and doesn't write any more to the socket. - Alex
My server is a basic HTTP server, and if the client has disconnected while I'm still processing their request I'd like to abort the processing (e.g. no point doing work if the client is not there waiting for the data).
How would you do this without ASIO, with plain sockets?
I've read I can try to read from the socket, but in HTTP the client sends the request, and doesn't write any more to the socket.
Probably you can try to write back to the socket, and check if this operation fails?
On Sat, Jul 25, 2009 at 11:27 AM, Igor R
I've read I can try to read from the socket, but in HTTP the client sends the request, and doesn't write any more to the socket.
Probably you can try to write back to the socket, and check if this operation fails?
You would pretty much need to fill up the socket send buffer before it even thinks about failing. You might consider doing a test to see if attempting a non-blocking read on the socket yields a pending read. For blocking reads, this would result in an EOF if the client has dropped the connection, but I'm not sure what will happen w/ the non-blocking reads on your system without running a little test. Jon
My server is a basic HTTP server, and if the client has disconnected while I'm still processing their request I'd like to abort the processing (e.g. no point doing work if the client is not there waiting for the data).
How would you do this without ASIO, with plain sockets?
I don't know the answer to that either. As you are implying, this question isn't necessarily ASIO specific.
I've read I can try to read from the socket, but in HTTP the client sends the request, and doesn't write any more to the socket.
Probably you can try to write back to the socket, and check if this operation fails?
I'm writing back using async_write and they seem to succeed. I might try writing back synchronously...
"Alex Black"
Is there a good way to do this?
My server is a basic HTTP server, and if the client has disconnected while I'm still processing their request I'd like to abort the processing (e.g. no point doing work if the client is not there waiting for the data).
If the client closes the connection cleanly, you will get an end-of-file if you try to read from the socket. You should be able to get a notification of that if you have an asynchronous read outstanding. If the client just disappears, you won't get any notification until you try to send data (including the OS sending TCP Keepalives, if they are enabled); then when that data send times out, you will get an error, probably on your read or the next time you try to write.
I've read I can try to read from the socket, but in HTTP the client sends the request, and doesn't write any more to the socket.
You can still try to do an asynchronous read from the socket; it will just never fire the event as long as there is no data to read. Hope this helps, ----Scott.
On Sat, Jul 25, 2009 at 1:42 PM, Scott Gifford
If the client just disappears, you won't get any notification until you try to send data (including the OS sending TCP Keepalives, if they are enabled); then when that data send times out, you will get an error, probably on your read or the next time you try to write.
If the client silently drops off, I wouldn't count on getting an error from send until your TCP send buffer fills up. Depending on how large the request file/resource is, you may never know that the client dropped off. Writes basically always succeed if there is space in the buffer. If the response fits in the buffer, you'll write, close the socket, and never get an error. If the response is larger than the buffer, you'll fill up the buffer, block (assuming blocking io) until the send times out, and then possibly get an error (dependent on your TCP stack impl). Jon
On Sat, Jul 25, 2009 at 1:42 PM, Scott Gifford
wrote: If the client just disappears, you won't get any notification until you try to send data (including the OS sending TCP Keepalives, if they are enabled); then when that data send times out, you will get an error, probably on your read or the next time you try to write.
If the client silently drops off, I wouldn't count on getting an error from send until your TCP send buffer fills up. Depending on how large the request file/resource is, you may never know that the client dropped off. Writes basically always succeed if there is space in the buffer. If the response fits in the buffer, you'll write, close the socket, and never get an error. If the response is larger than the buffer, you'll fill up the buffer, block (assuming blocking io) until the send times out, and then possibly get an error (dependent on your TCP stack impl).
Thanks for the info. It sounds like there isn't any way to detect this then. Would doing synchronous writes (instead of asycnchronous writes) make any difference?
Alex Black wrote:
On Sat, Jul 25, 2009 at 1:42 PM, Scott Gifford
wrote: If the client just disappears, you won't get any notification until you try to send data (including the OS sending TCP
Keepalives, if they
are enabled); then when that data send times out, you will get an error, probably on your read or the next time you try to write.
If the client silently drops off, I wouldn't count on getting an error from send until your TCP send buffer fills up. Depending on how large the request file/resource is, you may never know that the client dropped off. Writes basically always succeed if there is space in the buffer. If the response fits in the buffer, you'll write, close the socket, and never get an error. If the response is larger than the buffer, you'll fill up the buffer, block (assuming blocking io) until the send times out, and then possibly get an error (dependent on your TCP stack impl).
Thanks for the info. It sounds like there isn't any way to detect this then.
Would doing synchronous writes (instead of asycnchronous writes) make any difference?
SO_KEEPALIVE?
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On Mon, Jul 27, 2009 at 10:17 AM, Etienne Philip
Pretorius
SO_KEEPALIVE?
SO_KEEPALIVE just tries to prevent one end from closing a connection that has been idle for too long under "normal" operation. The problem we're trying to detect is when the other side of the connection drops off silently so we don't waste time *writing* data to the connection. This is an error condition, SO_KEEPALIVE won't help AFAICT. Jon
Jonathan Franklin wrote:
On Mon, Jul 27, 2009 at 10:17 AM, Etienne Philip Pretorius
wrote: SO_KEEPALIVE?
SO_KEEPALIVE just tries to prevent one end from closing a connection that has been idle for too long under "normal" operation.
The problem we're trying to detect is when the other side of the connection drops off silently so we don't waste time *writing* data to the connection. This is an error condition, SO_KEEPALIVE won't help AFAICT.
Then the others have answered the question. Whether you use async/blocking/non-blocking (dispatch method) you will only get an error if the underlying Network OS thinks that there is an error. In TCP, there should be a FIN state that the Network OS should pick up as the close call on the socket. Anyway - there is no 100% way to establish that this was because the client disconnected or the link between the two went down. There is a possibility that a shutdown on the read side from the server could cause the Network OS to detect if there is no ACK from the client when the half close is called. Also at that stage if the shutdown was ACK'ed you will get a read call with NO ERROR and 0 bytes read.
Jon _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On Mon, Jul 27, 2009 at 10:14 AM, Alex Black
Would doing synchronous writes (instead of asycnchronous writes) make any difference?
No. It still needs to fill up the TCP send buffer before it even considers raising any sort of error. If one end of the connection silently drops off, the only reliable way to find out is to attempt a read. Jon
On Mon, Jul 27, 2009 at 10:14 AM, Alex Black
wrote: Would doing synchronous writes (instead of asycnchronous writes) make any difference?
No. It still needs to fill up the TCP send buffer before it even considers raising any sort of error.
If one end of the connection silently drops off, the only reliable way to find out is to attempt a read.
So, if I attempt a read, will I be able to distinguish between these two scenarios: A) the client is no longer connected B) the client is connected but hasn't send anything since I last read (which is the case for HTTP clients I believe)
Jon _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
So, if I attempt a read, will I be able to distinguish between these two scenarios: A) the client is no longer connected B) the client is connected but hasn't send anything since I last read (which is the case for HTTP clients I believe)
Yes, if you call async_read(), then in case of (A) your handler will be called with some error_code (eg. eof), while in (B) nothing would happen - async.read is pending until there's some data or error.
AMDG Justin Leonard wrote:
I have been playing with Boost.Units as part of a work project recently, and I am not sure what the best approach to "demarshalling" values in a specific unit. For example, I would like to do something like:
using namespace boost::units;
struct A { enum { feet, kilometers };
quantitysi::length some_length;
double foo(int type) { switch(type) { case feet: return quantity<feet>(some_length).value(); case kilometers: return quantity<kilometers>(some_length).value(); } } };
This approach is necessary for passing values to external libraries which do not (and probably will never) use Boost.Units. Is conversion_factor the best approach, or is there a way to achieve this implicitly using the type system?
If you substitute the correct unit types in place of the enum values in quantity<xxx>(some_value).value(), then the code above should work fine. I'm not quite sure what you mean by achieving this implicitly using the type system. You have define the mapping from enum values to unit types somehow. There is no way for it to be deduced automatically. In Christ, Steven Watanabe
Steven Watanabe
If you substitute the correct unit types in place of the enum values in quantity<xxx>(some_value).value(), then the code above should work fine.
So I should be able to do something like: double length_in_feet = quantityus::foot_base_unit(10.0*si::meters).value(); to explicitly convert from meters to feet? Right now this gives 'boost::mpl::assertion_failed' while compiling quantity.hpp.
I'm not quite sure what you mean by achieving this implicitly using the type system. You have define the mapping from enum values to unit types somehow. There is no way for it to be deduced automatically.
My mistake, I meant to use a boost::unit instead of the enumeration inside of quantity... The enumerations just allow for run-time user input to control the type. Thanks, Justin
AMDG Justin Leonard wrote:
Steven Watanabe
writes: If you substitute the correct unit types in place of the enum values in quantity<xxx>(some_value).value(), then the code above should work fine.
So I should be able to do something like:
double length_in_feet = quantityus::foot_base_unit(10.0*si::meters).value();
to explicitly convert from meters to feet? Right now this gives 'boost::mpl::assertion_failed' while compiling quantity.hpp.
It asserts because a base_unit is just a tag type. Try us::foot_base_unit::unit_type. In Christ, Steven Watanabe
It asserts because a base_unit is just a tag type. Try us::foot_base_unit::unit_type.
Ah, that works as intended - do you think you could make it more explicit
in the documentation how to do this? E.g., as part of "runtime conversions",
...
typedef us_dollar_base_unit::unit_type us_dollar;
typedef euro_base_unit::unit_type euro;
void foo() {
quantity
I have conversions working fine for built-in types, but for both
scaled types and user-defined types I don't know how to get indirect
conversions to compile.
Here is an example:
#include
AMDG justinleona@gmail.com wrote:
I have conversions working fine for built-in types, but for both scaled types and user-defined types I don't know how to get indirect conversions to compile.
Here is an example:
#include
#include #include #include void f() { typedef metric::bar_base_unit::unit_type bars;
typedef boost::units::scale<10,static_rational<3> > kilo_type; typedef make_scaled_unit
::type kbars; const kbars kilobars; typedef boost::units::scale<10,static_rational<6> > mega_type; typedef make_scaled_unitsi::pressure,mega_type::type mpa; const mpa megapascals;
typedef boost::units::quantitysi::pressure Pascals; typedef boost::units::quantity<mpa> Megapascals; typedef boost::units::quantity<kbars> Kilobars;
Megapascals p(.5*kilobars); std::cout << Kilobars(p) << std::endl; }
If I directly convert it works - e.g., Kilobars(Pascals(p)). Do I need to use BOOST_UNITS_DEFAULT_CONVERSION to set a default conversion? If so - which part(s) of the chain need it?
This ought to just work. Fixed in the trunk. In Christ, Steven Watanabe
participants (9)
-
Alex Black
-
Etienne Philip Pretorius
-
Igor R
-
Jonathan Franklin
-
Justin Leonard
-
justinleona@gmail.com
-
Matthias Schabel
-
Scott Gifford
-
Steven Watanabe