{"action":"created","issue":{"url":"https://api.github.com/repos/openmicroscopy/bioformats/issues/1814","labels_url":"https://api.github.com/repos/openmicroscopy/bioformats/issues/1814/labels{/name}","comments_url":"https://api.github.com/repos/openmicroscopy/bioformats/issues/1814/comments","events_url":"https://api.github.com/repos/openmicroscopy/bioformats/issues/1814/events","html_url":"https://github.com/openmicroscopy/bioformats/pull/1814","id":79539203,"number":1814,"title":"cpp: common: Add units of measurement support","user":{"login":"rleigh-dundee","id":1528953,"avatar_url":"https://avatars.githubusercontent.com/u/1528953?v=3","gravatar_id":"","url":"https://api.github.com/users/rleigh-dundee","html_url":"https://github.com/rleigh-dundee","followers_url":"https://api.github.com/users/rleigh-dundee/followers","following_url":"https://api.github.com/users/rleigh-dundee/following{/other_user}","gists_url":"https://api.github.com/users/rleigh-dundee/gists{/gist_id}","starred_url":"https://api.github.com/users/rleigh-dundee/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rleigh-dundee/subscriptions","organizations_url":"https://api.github.com/users/rleigh-dundee/orgs","repos_url":"https://api.github.com/users/rleigh-dundee/repos","events_url":"https://api.github.com/users/rleigh-dundee/events{/privacy}","received_events_url":"https://api.github.com/users/rleigh-dundee/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/openmicroscopy/bioformats/labels/develop","name":"develop","color":"02e10c"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-05-22T17:27:06Z","updated_at":"2015-05-29T14:28:48Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/openmicroscopy/bioformats/pulls/1814","html_url":"https://github.com/openmicroscopy/bioformats/pull/1814","diff_url":"https://github.com/openmicroscopy/bioformats/pull/1814.diff","patch_url":"https://github.com/openmicroscopy/bioformats/pull/1814.patch"},"body":"Base unit definition types, constants and quantity types for units of measurement support.  Note: this is not model units support; this is completely generic for use in any code, and will be used by the model units support in the future.\r\n\r\nUses Boost.Units to provide all the base unit system and unit definitions, from which we create a complete set of types for each unit prefix needed.  While these aren't strictly necessary for basic unit conversion, providing these allows the programmer to control conversions between units and unit prefixes so that no implicit conversions can occur.  That is to say, a metre_quantity and micrometre_quantity can both be assigned a value in micrometres upon construction, but the metre_quantity will force a conversion to metres while the micrometre_quantity does not.  The separate types can also be used by the model units code and individual readers/writers as needed for their requirements.\r\n\r\nMinor issues:\r\n\r\n- pound-force per square inch (psi) has not been defined in terms of lbf/in^2 but uses a simple conversion from pascal; this means you can't multiply by in^2 to get the lbf, which didn't seem too important\r\n- mmHg conversion in Boost.Units uses a low precision 6dp constant which isn't as correct as it could be; see https://svn.boost.org/trac/boost/ticket/11335\r\n- angle conversion constants also have lower precision than expected; https://svn.boost.org/trac/boost/ticket/6893\r\n\r\n--------\r\n\r\nTesting: covered by unit tests.  Run `./bf-test ./cpp/test/ome-common/units` by hand to see them run.\r\n\r\nThe unit tests:\r\n\r\n- Instantiate every type defined in the headers; since this is templated code, verifying it builds is sufficient to check that the types are instantiable\r\n- Default constructs every instantiated type\r\n- Checks conversion to and from every SI prefixed type to the unprefixed form to ensure the static_rational powers are correct\r\n- Checks conversion of every non-SI type to and from the default (SI) unit system types; testing every combination would be quite burdensome, so we test that we can convert each to and from a prefixed or unprefixed SI type\r\n- Checks the unit name; if the name is invalid it won't compile and if incorrect it won't find the associated test data and will fail the test\r\n- Checks the unit symbol and stream output of numerical values.  We output to 4 s.f. and compare with expected output; note accuracy is tested by the conversion code; this is a sanity check that we're using the correct unit type\r\n\r\n--------\r\n\r\nFuture work:\r\n\r\n- Raise test precision threshold after conversion constant precision is improved (longer term); This is fairly simple, so I'll probably push a few one-liners upstream to use more precise constants"},"comment":{"url":"https://api.github.com/repos/openmicroscopy/bioformats/issues/comments/106827987","html_url":"https://github.com/openmicroscopy/bioformats/pull/1814#issuecomment-106827987","issue_url":"https://api.github.com/repos/openmicroscopy/bioformats/issues/1814","id":106827987,"user":{"login":"rleigh-dundee","id":1528953,"avatar_url":"https://avatars.githubusercontent.com/u/1528953?v=3","gravatar_id":"","url":"https://api.github.com/users/rleigh-dundee","html_url":"https://github.com/rleigh-dundee","followers_url":"https://api.github.com/users/rleigh-dundee/followers","following_url":"https://api.github.com/users/rleigh-dundee/following{/other_user}","gists_url":"https://api.github.com/users/rleigh-dundee/gists{/gist_id}","starred_url":"https://api.github.com/users/rleigh-dundee/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rleigh-dundee/subscriptions","organizations_url":"https://api.github.com/users/rleigh-dundee/orgs","repos_url":"https://api.github.com/users/rleigh-dundee/repos","events_url":"https://api.github.com/users/rleigh-dundee/events{/privacy}","received_events_url":"https://api.github.com/users/rleigh-dundee/received_events","type":"User","site_admin":false},"created_at":"2015-05-29T14:28:48Z","updated_at":"2015-05-29T14:28:48Z","body":"This is an outline of my thoughts so far looking through the Java code.  They may at this stage be incomplete.\r\n\r\n----------\r\n\r\nJava:\r\n\r\n- `UnitsFoo` enumerations from xsd-fu\r\n- Unit: `Unit<Quantity>`\r\n- Quantity: `Quantity`; contains number, `Unit<Q>`\r\n- Instance: by symbol, are `Unit<Q>` *even though they are quantities!*\r\n\r\nWhy (WHY?) is `Unit` specialised by `Quantity` when it is *not* a quantity and *not related to a quantity*.  A `Quantity` should be specialised by a `Unit`, not the other way around.  All the actual units, like `Length`, `Power` are `Quantities`.  **No**, they are actually `Units`...  This looks like a design flaw.\r\n\r\nAlso, in C++, the short symbol names are inevitably going to clash with preprocessor macros, either in the system headers, or the application headers of the application using our headers.  We already\r\nsaw this for one case, and it's likely there will be more in real use.\r\n\r\n----------\r\n\r\nC++, ome-common basic unit types (this PR):\r\n\r\n- Unit: base_unit::unit_type\r\n- Quantity: quantity<unit_type>\r\n- Constant: base_unit::unit_type()\r\n- Creation of a quantity with unit: number * Constant\r\n\r\nClear and clean separation of concerns, matching Boost.Units' design.  The only part of the existing Java implementation this provides is the conversion constants.  Everything else here is unique to C++ and not part of the model units classes provided by the Java implementation.\r\n\r\n----------\r\n\r\nC++, ome-xml model unit types (proposed design):\r\n\r\n- `UnitsFoo` enumerations from xsd-fu\r\n- Unit: `Unit<UnitFoo>` - specialised for a given dimension\r\n- Quantity: `Quantity<Unit>`; contains value and unit enum value of the specified dimension, and is not capable of any unit conversions\r\n- Creation of a quantity with unit: quantity constructor from value+enum (model serialisation) or direct from base quantity.\r\n\r\n`Quantity<Unit>` constructor can allow direct construction from a base level `quantity<unit_type>`; the type will map directly to the corresponding enum.  Conversely, it can also be converted back to a base type either via a templated class method or templated free function.\r\n\r\n`Quantity<Unit>` does not intrinsically do conversions; it's simply a container for the model value and associated unit.  Free functions or helper methods can do conversions via use of the base types.\r\n\r\nThe Java code is mixing up these separate concerns as well as getting some of them completely backward."}}