
Janek,
become the same template. But in fact, maybe following Ockham's razor you should just follow your suggestion and use
template<class Unit,class Y = double> class quantity;
because 'class Unit' *cannot* have a reasonable default. While class Y=double *can* have a default.
I agree that that's the most sensible choice. Since there's really no gain, and potentially some compile-time cost on top of a library that's already compile-time heavy, I'll just flip the order of the template arguments for the quantity class and default to double precision value_type.
I hope that you have read the reviews of Andy's Little quantity library, and why it was rejected. Some of the reasones were considered show-stoppers. I remember few of them, but better you will read the reviews.
I followed the debate fairly closely, and saved a number of the posts. I have made an effort to address as many issues as seemed feasible without experiencing extreme "feature creep"... I don't think it is reasonable to try to address every conceivable application in unit conversion with this library, so I am targeting a flexible system that can form a foundation for dimensional analysis that can be used to build more specialized user libraries.
1) support for powers of N (where N is different from 10) in multipliers: usually people operate using kilo (10^3), mega (10^6). But sometimes a unit is expressed in different system, like a power of 2 (harddrive capacity, file size: http://en.wikipedia.org/wiki/ Kibi )
My approach to this is to delegate this behavior to a value_type that handles powers (see scaled_value.hpp and unit_example_6.cpp). I have not defined prefixes for metric or binary prefixes in that file, but that would be easy. The rationale for this approach is that a kilometer in the MKS system really is 1000 meters, just as a kilobyte is 2^10 bytes... Therefore, it is more a question of how to preserve precision in numerical computations using values that may have large power values (nanometers/femtoseconds etc...), which falls to the value_type.
C) only those multipliers that are declared either by the user, or in an included file that contains some predeclared multipliers are used by the program. Allow the user to chose between explicit and implicit conversion, when he wants. You don't know if someone wants to store 1km or 1000m in his variable.
As I alluded to above, if the user wants 1km, it can be done like this: quantity< scaled_value<double,10,3>,SI::length> q1 (scaled_value<double,10,3>(1.0)*meter); while 1000m could be either quantity<double,SI::length> q2(double(1000)*meter); or quantity< scaled_value<double,10,0>,SI::length> q3 (scaled_value<double,10,0>(1000.0)*meter); If you don't care about the potential loss of precision, of course you can just define prefix factors as constants (like I do in si_units.hpp): static const long double kilo(1e3); quantity<double,length> q2(1*kilo*meter); Part of the problem is that there are many equally reasonable needs to address specific domains; it isn't possible to accommodate all of these in a single library. That's why I've tried to make this library as flexible as possible to facilitate add-on libraries dealing with special requirements.
3) Do not eforce SI on anyone:
I absolutely agree. This library is completely system agnostic, with SI (and CGS) systems provided for convenience.
Make it easy to declare one's own unit system. This point it is not about imperial and metric. It is about physics. For instance parsecs and light-years do not belong to SI. But try to store 100 parsecs with 'double' precision using meters. (or yottameters 10^24 (?)). This is not practical, any calculations that operate on parsecs must be done in parsecs, or we lose precision.
This forms the core of the example documentation, in which a mini-SI system is implemented. The code is in test_system.hpp. If you look at the documentation, you can see how to do completely general dimensional analysis on any units you choose to define.
Of course you can provide few #include <boost/unit/SI.hpp>, #include <boost/unit/imperial.hpp>, #include <boost/unit/ relativity.hpp>, etc files. But you never know what kind of unit system will be useful for somebody.
This was a major design consideration for the library - the whole header file for test_system.hpp is only about 50 lines of code, including IO, various convenience typedefs, etc... - basically everything one would need to define for a unit system with length, time, and mass only... Physics-based systems such as various natural units should be easy to implement. Systems such as imperial units are not intrinsically harder, but are left to the interested individual because they do not actually define any base units - is the fundamental unit of length in imperial units the inch? the foot? the yard? the mile? The same problem arises for mass, area, etc...
It should be equally easy to declare that I will use a meter (1 line) and a second (1 line, makes 2 lines total) in my program, instead of including whole SI.hpp (1 line), which I don't need.
This would just involve increasing the granularity of the SI header to split it up into mini-headers for each fundamental unit: <boost/units/systems/SI.hpp> would become #include <boost/units/systems/SI_length.hpp> #include <boost/units/systems/SI_mass.hpp> #include <boost/units/systems/SI_time.hpp> etc... Certainly easy enough to implement this, since there is no coupling between fundamental units. This fact, which I should probably make clearer, also makes it easy for the user to add new fundamental units to existing unit systems: #include <boost/units/systems/SI.hpp> namespace SI { struct foo_tag : public ordinal<10> { }; typedef dimension< boost::mpl::list<dim<foo_tag,static_rational<1> >
::type foo_type;
typedef unit<system,foo_type> foo; static const foo foo_unit; } quantity<double,SI::foo> q1(1.5*foo_unit); q1*quantity<double,SI::length>(1.0*meter) -> 1.5 foo meter Adding preprocessor shortcuts would make this even simpler, though I suspect that the number of users who will implement their own unit systems will be a small minority...
4) Make input/output as decoupled as possible. Basic cout << of the unit is enough for beginning. Do NOT make it sophisticated in any way, otherwise you will start writing another library.
This is what is already implemented. I demonstrate interoperability with Boost.Serialization in one of the examples. I'm happy to stop there for now...
5) leave door open for adapting your library into other math libraries. For example, let's say that I have a 3D vector of meters:
vector<3,quantity<meter> > a,b; vector<3,quantity<meter*meter> > c;
c=dot_product(a,b);
What is the return type of function dot_product ? Think about it, and you will see what I mean. Where that function is defined? Well, boost still has not a small vector library, but you should leave an open door for making one.
I've dealt with this quite carefully in the existing library; take a look at unit_example_7.cpp for interoperability with my own array class (output only, since the array class code is not included) unit_example_8.cpp for interoperability with Boost.Quaternion unit_example_9.cpp for interoperability with std::complex and a toy reimplementation of complex that handles operators correctly The latter example shows some of the problems that can arise when heterogeneous operators are not implemented by the container class. For example, in the case you provide, to be completely general, the return type of the dot_product function should be template<class A,class B> add_typeof_helper< multiply_typeof_helper<A,B>::type,multiply_typeof_helper<A,B>::type
::type dot_product(const vector<N,A>& a,const vector<N,B>& b)
In your particular case, quantity<meter*meter> is correct, but not in general... This algebra is (to the best of my knowledge) correctly implemented in the library as it stands. For new value_types with unconventional algebras, the following helper classes need to be specialized: add_typeof_helper subtract_typeof_helper multiply_typeof_helper divide_typeof_helper power_typeof_helper root_typeof_helper so the compiler can determine the result type of an algebraic expression involving that type.
Ok, I don't remember any more. Better you will have a look at that reviews. I still have them in my inbox, so in fact I can forward them to you.
I'd appreciate that; thanks for the input. If you get a chance to look at the library itself, I'd appreciate your opinion... Matthias