GithubHelp home page GithubHelp logo

units's Introduction

Boost.Units

Boost.Units, part of collection of the Boost C++ Libraries, implements dimensional analysis in a general and extensible manner, treating it as a generic compile-time metaprogramming problem. With appropriate compiler optimization, no runtime execution cost is introduced, facilitating the use of this library to provide dimension checking in performance-critical code.

Directories

  • doc - QuickBook documentation sources
  • example - examples
  • images - images for documention
  • include - Interface headers
  • test - unit tests
  • test_headers - unit tests for self containment of headers
  • tutorial - tutorial

Test results

@ Travis AppVeyor
master Build Status Build Status
develop Build Status Build Status
Coverity Scan Build Status

More information

License

Distributed under the Boost Software License, Version 1.0.

units's People

Contributors

akumta avatar alfc avatar beman avatar brycelelbach avatar chrislesiak avatar d4n avatar danieljames avatar douggregor avatar eldiener avatar erikerlandson avatar glenfe avatar grafikrobot avatar imikejackson avatar jhunold avatar jwakely avatar jzmaddock avatar kojoley avatar lastique avatar mkurdej avatar muggenhor avatar pdimov avatar petamas avatar robin-zimmeck avatar steveire avatar straszheim avatar swatanabe avatar vprus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

units's Issues

Forward declarations for boost units

Hello dear boost developers,

as i am seeking to reduce the compile times of our code, i am currently trying to reduce heavy includes in header files. For this, i am forward declaring function parameters as in this example:

// Class A.h
class B;

class A
{
   ...
   void foo(B);
}

However, i did not find a way to forward declare our type definitions, that rely on boost units such as the definition of a length unit (Length.h) required as a function parameter in the file Object.h:

// Length.h
#include <boost/units/base_units/si/meter.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/static_constant.hpp>

#include <boost/units/make_scaled_unit.hpp>

using meter_unit = boost::units::si::meter_base_unit::unit_type;
using Length = boost::units::quantity<meter_unit, double>;
BOOST_UNITS_STATIC_CONSTANT(Meter, meter_unit);
BOOST_UNITS_STATIC_CONSTANT(Meters, meter_unit)
// Object.h
#include <Length.h> // This include shall be avoided

class Position; 
class Object {
 ... 

    bool isNearby(Position pos, Length distance); 
}

What I Tried:
I tried forward declaring the template boost::units::quantity<meter_unit, double>; but i struggeld to fwd-declare the meter_unit that contains both template class meter_base_unit and unit_type inside the namespace of meter_unit.

The meter_base_unit is defined inside boost units as below (simplified) and i guess the unit_type is defined inside the macro BOOST_TYPEOF_REGISTER_TYPE.


// Boost ... Units/Meter.hpp
namespace boost::units::si {
    struct meter_base_unit : public base_unit<meter_base_unit, length_dimension, -9>
    {
        static std::string name()   { return("meter"); }
        static std::string symbol() { return("m"); }
        };
    }
}

BOOST_TYPEOF_REGISTER_TYPE(boost::units::si::meter_base_unit)

Is such a constellation even possible to forward declare completely?
I managed to forward declare the meter unit to some degree as shown here:

// MyBoostForwardDeclarations/Length_fwd.h
#include "boost/units/units_fwd.hpp"
#include <boost/units/base_units/si/meter.hpp>

namespace boost::units
{
template<class Unit, class Y> class quantity;
}

using meter_unit = boost::units::si::meter_base_unit::unit_type;
using Length = boost::units::quantity<meter_unit, double>;

However, this "forward declaration" header is still an expensive include because it need both units_fwd.hpp and meter.hpp
So therefore, i would prefer to completely forward declare the Length_Unit without including any boost header. Are there any ideas on how i could achieve this?

I am happy for every contribution regarding my specific problem and every contribution leading to deeper understanding of template forward declarations.

Thanks in Advance

SegfaultCreator

[Feature Request]: unit_cast

When I try to cast a quantity to it's numerical representation I can use quantity_cast. That's great. What I think could also be very helpful is a shorthand for casting a quantity to a different unit. Now I know that this can be done like so:

boost::units::quantity<boost::units::si::length, double> siLength;
auto cgsLength = static_cast<boost::units::quantity<boost::units::cgs::length, double>>(siLength);

However this feels kinda more bulky than it'd need to be. It would be very neat if something like this would be possible:

boost::units::quantity<boost::units::si::length, double> siLength;
auto cgsLength = unit_cast<boost::units::cgs::length>(siLength);

So essentially I am asking for a way to cast to a different (compatible) unit without having to explicitly write that I want to cast it to a quantity again. That should be deduced. Thus when starting from a quantity of double, I'd expect this cast to automatically cast to a quantity of double as well. Same for int and all other types.

Would something like this be possible? 🤔

Support for deserialization

Hello,

Right now, only serialization is possible. Is there any reason deserialization is not implemented ?
I'm on a project where I want to extends the "safety" of units to the data in our database.
For example, I would like to be able to do that:

quantity <si::length> l0 = 3.14 * si::meter;
stream << l0; // "3.14 m"

quantity <si::area> a;
stream >> a; // throw an exception

quantity <si::length> l1;
stream >> l1; // OK

Thank you!

one/quantity support

While working on an adaptation of a generic vector class to support boost::units, I found boost::units::one to be quite helpful. It can act as a substitute for a unit_type when there is no quantity (and no system of units) involved, e.g. just a built-in floating-point type.

Using one got me quite far until I tried to prepare for instances of units, or one, divided by actual values. This brings divide_typeof_helper<one, T>::type into play. Which turns out to be T, even if T is a unit or a quantity type. (Such specializations are given in detail/one.hpp and quantity.hpp.) The expected result would be something with the reciprocated unit type, i.e. all base unit exponents sign-flipped.

I have looked for effects on officially documented functions, including pow<-1> (since static_int_power_sign_impl<N, true> literally uses divide_typeof_helper<one,), but I have found no misbehavior on the surface. Somehow the implementation manages to avoid this case, so I cannot label this as a bug.

Nevertheless, if you want boost::units::one() / x to work as intended for a quantity or unit constant x, you can use the attached boost-units-one-div.cc.txt as a basis for a test case.

subtraction of dimensionless quantities produce surprising result

In a generic environment one needs to write generic function involving integer integrals.

template<class A, class B> auto f(A a, B b){return 1 - a/b;}

Feeding f(1.,2.) gives 0.5.

However if one feeds f(1.*si::meter, 2.*si::meter) insted one gets 1 and of type int.

This is probably because the dimensionless quantities are implicitly convertible to their value type, which is a good feature. But for some reason in this case this double is converted to an int before doing the subtraction. The second implicit conversion in seems to be very strong in the language and I don't see a way to solve this in the library (please don't make the conversion explicit! that creates many other problems.).

The only solution I found was to define a family of functions specialized for int.

namespace boost{
namespace units{
template<class S, class Y>
inline
quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>
operator-(int i,
    const quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>& q2)
{
    typedef quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S),Y> quantity_type;
    return quantity_type::from_value(i - q2.value());
}
}}

The good news is that this is in principle only needed for int, the bad news is that one need to implement many functions, (int - dimlessQ), (dimlessQ - int), (int + dimlessQ), (dimlessQ + int), (int * dimlessQ), (dimlessQ * int), (int / dimlessQ), (dimlessQ / int).

I would say this is a bug, but that can be controversial. What it is not controversial is that it is surpring because even for totally dimensinless arguments it will not behave like a double.

Is there a workaround solution?

Gauss system support

Hello

I implemented the support for Gauss units. It is not perfect (it miss the conversions from/to SI and CGS) but I tried to follow the existing design as possible.

Unfortunately, I had to duplicate a set of physical_dimensions (Gauss define electric charge differently): you can find it in physical_dimensions/gs directory

If you want, and you know how to implements the converters, you can integrate to boost/units release. If you think this implementation does not fit the design of Boost Units please just notice me so I can create a standalone project (with only my modification).

The modifications can be found here (branch: gauss)
https://gitlab.com/micrenda/boost-units-gauss/tree/gauss

Regards

Provide interoperability between std::chrono::duration and boost::units::quantity<time>

The time dimension is special in C++, because there already is an implementation in the standard libraries: std::chrono::duration.
I think boost::units should recognize this and provide an easy way to convert between these two different implementations of what is basically the same thing.
Ideally one should be able to do:
quantity<time> q = 2h;
where 2h is a std::literals::chrono_literals::operator""h.

Degree definition causes quite big conversion error between degrees and radians.

I just noticed that the degree definition in boost/units/base_units/angle/degree.hpp uses quite inaccurate value:

BOOST_UNITS_DEFINE_BASE_UNIT_WITH_CONVERSIONS(angle,degree,"degree","deg",6.28318530718/360.,boost::units::angle::radian_base_unit,-101);

as units is meant for scientific calculation I'd think that it would be a great benefit to use the highest possible accuracy for the definition. Right now the accuracy will cause significant error over time, when for instance when stepping an integration that uses this conversion for the angles.

It seems like units doesn't have it's own constants definitions, so maybe we should just use the value from the math/constants here?

Explicit construction of quantities using units that are not defined in system compiles but mathematically fails

If I define a unit in a system that doesn't have this base dimension, e.g. angular_velocity in the degrees system:

quantity<unit<angular_velocity_dimension,degree::system>> angular_vel_deg(30*degree::degrees/si::seconds);

This compiles fine, and explicit conversions also compile fine:

quantity<unit<angular_velocity_dimension, si::system>> angular_vel_si(angular_vel_deg);

However when I go to run it, I get angular_vel_si as 30 rad/s rather than the expected 0.523 rad/s

The fact that this compiles seems to be a bug to me, but most importantly I'd like to be able to detect this situation (ideally with a static_assert) so that I don't have code that compiles but performs incorrect conversions.

With a few hours digging around I couldn't find a satisfactory way to do this, though I think it might be related to #36 but the proposed fix (#37) didn't fix my issue.

Here's my full minimal test case. Thanks in advance for any help you can provide:

#include "units/include/boost/units/systems/si.hpp"
#include "units/include/boost/units/systems/angle/degrees.hpp"
#include <cassert>
#include <iostream>
#include <cmath>


int main()
{
    double eps = std::numeric_limits<double>::epsilon();

    
    using boost::units::unit;
    using boost::units::quantity;
    namespace si = boost::units::si;
    namespace degree = boost::units::degree;

    // angle, works as expected
    {
        using boost::units::plane_angle_dimension;
        quantity<unit<plane_angle_dimension,degree::system>> angle_deg(30*degree::degrees);
        quantity<unit<plane_angle_dimension,si::system>> angle_si(angle_deg);

        std::cout << "Degrees: " << angle_deg.value() << ", radians: " << angle_si.value() << std::endl;

        assert(std::abs(angle_deg.value()-angle_si.value()) > eps);
    }
    
    // angular velocity: fails but more importantly, it compiles (when I feel that it shouldn't)
    {
        using boost::units::angular_velocity_dimension;

        quantity<unit<angular_velocity_dimension,degree::system>> angular_vel_deg(30*degree::degrees/si::seconds);
        quantity<unit<angular_velocity_dimension, si::system>> angular_vel_si(angular_vel_deg);

        std::cout << "Degrees/sec: " << angular_vel_deg.value() << ", radians/sec: " << angular_vel_si.value() << std::endl;
        
        assert(std::abs(angular_vel_deg.value()-angular_vel_si.value()) > eps);
    }
    
}

Output:

Degrees: 30, radians: 0.523599
Degrees/sec: 30, radians/sec: 30
a.out: test2.cpp:38: int main(): Assertion `std::abs(angular_vel_deg.value()-angular_vel_si.value()) > eps' failed.
zsh: IOT instruction (core dumped)  ./a.out

Expected (or compile failure would also be acceptable):

Degrees: 30, radians: 0.523599
Degrees/sec: 30, radians/sec: 0.523599

boost::units::quantity should be trivial

I think boost::units::quantity<> should be as trivial as possible.

Right now they are not ( std::is_trivial_v is false ) because of more-or-less superfluous implementations of constructors and assignment operators. The internally asserted layout compatibilities have no meaning for users of the class.

I do not really have a strong argument for making them trivial but have a strong gut feeling they should be (for users at least).

noexcept support

Many functions, but especially copy constructors of quantity, would benefit from a noexcept specifications.

io.hpp is required to compile vector of dimensioned types

To reproduce:

#include <vector>
#include <boost/units/systems/si/length.hpp>
// Uncomment to fix:
//#include <boost/units/systems/si/io.hpp>

using namespace boost::units;
using namespace boost::units::si;

auto dimensioned_sphere(std::vector<quantity<length>> const & v) {
  return v[0];
}

int main()
{
    return 0;
}

Compile error:

main.cpp:9:10: fatal error: implicit instantiation of undefined template 'boost::units::quantity<boost::units::unit<boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<1>>, boost::units::dimensionless_type>, boost::units::homogeneous_system<boost::units::list<boost::units::si::meter_base_unit, boost::units::list<boost::units::scaled_base_unit<boost::units::cgs::gram_base_unit, boost::units::scale<10, static_rational<3>>>, boost::units::list<boost::units::si::second_base_unit, boost::units::list<boost::units::si::ampere_base_unit, boost::units::list<boost::units::si::kelvin_base_unit, boost::units::list<boost::units::si::mole_base_unit, boost::units::list<boost::units::si::candela_base_unit, boost::units::list<boost::units::angle::radian_base_unit, boost::units::list<boost::units::angle::steradian_base_unit, boost::units::dimensionless_type>>>>>>>>>>>>'
    9 |   return v[0];
      |          ^
~/boost/boost/units/units_fwd.hpp:52:45: note: template is declared here
   52 | template<class Unit,class Y = double> class quantity;
      |                                             ^
1 error generated.

Error on top of 4578701.

Modular Boost C++ Libraries Request

We are in the process of making B2 build changes to all of the B2 build files
to support "modular" consumption of the Boost Libraries by users. See this list
post for some details: https://lists.boost.org/Archives/boost/2024/01/255704.php

The process requires making a variety of changes to make each Boost library
independent of the super-project structure. But the changes do not remove the
super-project structure or the comprehensive Boost release. The changes make
solely make it possible, optionally, for users, like package manages, to easily
consume libraries individually.

Generally the changes include:

  • Adding a libroot/build.jam.
  • Porting any functionality from libroot/jamfile to libroot/build.jam.
  • Moving boost-install declaration from libroot/build/jamfile is applicable.
  • Adjusting other B2 build files in the library, like test/jamfile, as needed.
  • Possible changes to C++ source files to remove includes relative to the
    super-project boostroot location.

Some examples of such changes:

We are asking how you would like us to handle the changes. We would prefer if
you allow the owners of the Boost.org GitHub project to make changes to B2
build files, as needed, to accomplish the changes. But understand
that you may want to manage the proposed changes yourself.

We previously sent emails to all known maintainers to fill out a form with their
preference. We are contacting you in this issue as we have not gotten a response
to that email. You can see the ongoing responses for that form and the responses
to these issues here https://github.com/users/grafikrobot/projects/1/views/6

We are now asking if you can reply directly to this issue to indicate your
preference of handling the changes. Please supply a response to this question
and close the issue (so that we can verify you are a maintainer).

How would you like the build changes to be processed?

  1. Pull request, reviewed and merged by a BOOSTORG OWNER.
  2. Pull request, reviewed and merged by YOU.
  3. Other. (please specify details in the reply)

Also please indicate any special instructions you want us to consider. Or other
information you want us to be aware of.

Thanks you, René

Unit conversion ignores offsets.

Unit conversions only consider conversion factors. Conversion offsets are ignored. As consequence, all conversions requiring conversion offsets are faulty.
This affects the conversion from degree celsius to kelvin or farenheit. Also all user defined units that make use of BOOST_UNITS_DEFINE_CONVERSION_OFFSET do not work correctly.

A short example:

#include <iostream>

#include <boost/units/systems/si.hpp>
#include <boost/units/systems/temperature/celsius.hpp>
#include <boost/units/systems/temperature/fahrenheit.hpp>
#include <boost/units/io.hpp>


int main(int , char **) {
    using namespace boost::units;

    quantity<celsius::temperature> tempInCelsius = 5.0 * celsius::degrees;
    quantity<fahrenheit::temperature> tempInFahrenheit(tempInCelsius);
    quantity<si::temperature> tempInKelvin(tempInCelsius);

    std::cout << tempInCelsius << std::endl;
    std::cout << tempInFahrenheit << std::endl;
    std::cout << tempInKelvin << std::endl;

    return 0;
}

Output of this short program is:

5 C
9 F
5 K

Correct would be:

5 C
41 F
278.15 K

Best regards

fma in boost/units/cmath.hpp?

I've recently had a compile fail with fma on boost::units types. However, it appears this has been implemented in boost/units/cmath.hpp, but it has been explicitly disabled using an #if 0.

 git show e634a7c6 
commit e634a7c6bef7180ec34387fd7bb7c0236c17a990
Author: Steven Watanabe <[email protected]>
Date:   Sat Jun 7 17:15:24 2008 +0000

    Remove compiler specific code from cmath.  Disable functions which cannot be implemented easily using Boost.Math

    [SVN r46217]

Is there any reason why we can't re-enable these code paths today?

Edit: I enabled those code paths, and it appears to be working. However, I had to add a couple other overloads:

template<class Unit, class Y>
inline 
BOOST_CONSTEXPR
quantity<Unit,Y>
fma BOOST_PREVENT_MACRO_SUBSTITUTION (const Y& q1,
                                      const quantity<Unit,Y>& q2,
                                      const quantity<Unit,Y>& q3)
{
    using std::fma;
    return quantity<Unit,Y>::from_value(fma(q1,q2.value(),q3.value()));
}

template<class Unit, class Y>
inline 
BOOST_CONSTEXPR
quantity<Unit,Y>
fma BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit, Y>& q1,
                                      const Y& q2,
                                      const quantity<Unit,Y>& q3)
{
    using std::fma;
    return quantity<Unit,Y>::from_value(fma(q1.value(),q2,q3.value()));
}

I imagine the case where a and x have "inverse units" and a*x+b is dimensionless could also be supported.

Patchfile: units_patch.txt

Improve documentation

Although I think the system itself is extremely powerful, I also believe that the existing documentation at https://www.boost.org/doc/libs/1_71_0/doc/html/boost_units.html is very poorly written (no offense to the one(s) that wrote it). It certainly covers a lot of stuff and maybe if you already know how things work, it is a great place to look up some (implementation) details.

If on the other hand you don't know the system (yet) and want to get started using it, the documentation is almost completely useless. I might even go a step further stating that it might even be contra-productive to look into the docs for getting started quickly as you'll be more confused than before after doing so (at least that's what i experienced).

Thus I think that there should be a simple Getting Started section that covers, how the library is to be used in general (on this the Quick Start section in the docs is actually not bad) and some simple examples how one can add custom units/dimensions to the system.

The latter might be hidden in the information flood in the following chapters in the docs but they just contain so many "you can do this part like this... or like this... or that way... or completely different altogether" that it is really really hard to extract how things should be done in the end (for a simple example).

Now where am I going with this issue here? I not only want to point out the problem I see (which I could imagine you might be aware of already) but I also want to offer my help at trying to improve the situation.
For this reason I'd like to know where and how I can submit patches to the documentation of this boost library.

is_constructible has unexpected results

#include <boost/units/base_units/metric/hour.hpp>
#include <boost/units/base_units/metric/liter.hpp>
#include <boost/units/quantity.hpp>

#include <boost/type_traits/is_constructible.hpp>

using TLiter = boost::units::quantity<boost::units::metric::liter_base_unit::unit_type>;
using THour = boost::units::quantity<boost::units::metric::hour_base_unit::unit_type>;

static_assert(!boost::is_constructible<TLiter, THour>::value, "Should not be constructible from unrelated units");

https://godbolt.org/z/W-7Nvg

This fails to compile before it is able to check the static assertion with the following error:

error: no matching function for call to 'conversion_factor(boost::units::unit<boost::units::list<boost::units::dim<boost::units::time_base_dimension, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::heterogeneous_system<boost::units::heterogeneous_system_impl<boost::units::list<boost::units::heterogeneous_system_dim<boost::units::scaled_base_unit<boost::units::si::second_base_unit, boost::units::scale<60l, boost::units::static_rational<2l> > >, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::time_base_dimension, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::dimensionless_type> >, void>, boost::units::unit<boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<3l> >, boost::units::dimensionless_type>, boost::units::heterogeneous_system<boost::units::heterogeneous_system_impl<boost::units::list<boost::units::heterogeneous_system_dim<boost::units::metric::liter_base_unit, boost::units::static_rational<1l> >, boost::units::dimensionless_type>, boost::units::list<boost::units::dim<boost::units::length_base_dimension, boost::units::static_rational<3l> >, boost::units::dimensionless_type>, boost::units::dimensionless_type> >, void>)'

         return(destination_type::from_value(static_cast<T2>(source.value() * conversion_factor(Unit1(), Unit2()))));

This has consequences for using boost::units inside a boost::variant as the constructor of the variant cannot reliably detect if the given source operand can be converted to any of the contained storage types of the variant.

In our source code base with a vendored-in copy of Boost, I fixed the issue by adding a check that conversion_factor is a defined function for the given source and target units of a quantity. If that is not the case, the constructor for explicit conversions is disabled. This does not work with an output test case at this point in time.

Furthermore, the current solution only fixes the problem in C++11 and beyond.

Before digging deeper and creating a proper PR, I wanted to know if I am looking at the right approach or if I am off on the wrong foot.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.