Creating user-defined conversions
Asked Answered
D

1

6

I'm trying to create units for meter and kilometer. I want then to sum and convert them accordingly. I know that the boost::units library has already SI system, but I want create all from scratch, because then I need to create my own systems for my projects (so I'm doing this to learn). My purpose is to declare a "Length" variable that can be modified using units: for example I want to write

Length xLength1 = 5350 * Meters + 2 Kilometers;

For this purpose, I've created the length.h file, containing definitions of meter and kilometer, and at the end I declare the conversion between these two units:

#ifndef LENGTH_H_
#define LENGTH_H_

#include <boost/units/base_dimension.hpp>
#include <boost/units/base_unit.hpp>
#include <boost/units/scaled_base_unit.hpp>
#include <boost/units/quantity.hpp>
#include <boost/units/conversion.hpp>

struct LengthBaseDimension : boost::units::base_dimension<LengthBaseDimension,1>{};

typedef LengthBaseDimension::dimension_type LengthDimension;

struct MeterBaseUnit : boost::units::base_unit<MeterBaseUnit, LengthDimension, 1>{};
template <> struct boost::units::base_unit_info<MeterBaseUnit>
{
  static std::string name()   { return "meter"; }
  static std::string symbol() { return "m";     }
};

struct KilometerBaseUnit : boost::units::base_unit<KilometerBaseUnit, LengthDimension, 2>{};
template <> struct boost::units::base_unit_info<KilometerBaseUnit>
{
  static std::string name()   { return "kilometer"; }
  static std::string symbol() { return "km";        }
};

// I don't want to use scaled_unit because I need this for my real purpose
BOOST_UNITS_DEFINE_CONVERSION_FACTOR(KilometerBaseUnit, MeterBaseUnit, double, 1000.0);

#endif

Then I create the file units.h in which I define my own unit system

#ifndef LIB_UNITS_H_
#define LIB_UNITS_H_

#include "length.h"
#include <boost/units/unit.hpp>
#include <boost/units/static_constant.hpp>
#include <boost/units/make_system.hpp>
#include <boost/units/io.hpp>

typedef boost::units::make_system<MeterBaseUnit>::type UnitsSystem;

typedef boost::units::unit<boost::units::dimensionless_type, UnitsSystem> Dimensionless;
typedef boost::units::unit<LengthDimension                 , UnitsSystem> SystemLength;


BOOST_UNITS_STATIC_CONSTANT(Kilometer  , SystemLength);
BOOST_UNITS_STATIC_CONSTANT(Kilometers , SystemLength);
BOOST_UNITS_STATIC_CONSTANT(Meter      , SystemLength);
BOOST_UNITS_STATIC_CONSTANT(Meters     , SystemLength);

// Typedefs of dimensions
typedef boost::units::quantity<SystemLength> Length;

#endif

At least, I use this header in my main function

#include "units.h"
#include <iostream>

int main(void)
{
  Length xLength1 ( 300.0 * Meters);
  Length xLength2 (1500.0 * Kilometer );
  Length xLength3;
  std::cout << xLength2 << std::endl;
  xLength3 = xLength1 + xLength2;

  return 0;
}

This projects compiles, but It does not do what I want. When I print the variable xLength2, I obtain 1500 m instead of 1500 km or 1500000 m. The sum is also wrong, because I obrain 1800 m. It's like I consider kilometers as meters and conversion does not work.

What I'm doing wrong?

Defiant answered 19/7, 2012 at 10:24 Comment(5)
You define Kilometer to be the same as Meter. I don't recall offhand what is the preferred way to define Kilometer (perhaps there is a different macro), but you can try something like const Length Kilometer = 1000 * Meters; (shuffling the order of definitions).Commonplace
But in this case I can't write and take value in kilometers, but only in meters. I want the possibility to work with both units, and use both values where needed.Defiant
Just a heads up, if you're using MSVC++ or the gcc, you can replace your #IFDEF LENGTH_H_ etc guards with #pragma once at the top of each header.Jollification
Apparently what you want is a "variant" type of boost quantity. There are some clues here: boost.2283326.n4.nabble.com/…Dandy
Do you "have" to use boost? There are simpler ways in C++ to do this. :)Rewrite
R
0

Unless you're project is to replicate the Boost library, this seems like an insane way to code a unit system. Template hackery is rarely the best option.

Instead, use a class! Not real code below:

class temperature{
   double temp; //always in kelvin
   temperature(double temperature, char unit){
       if(unit=='k')
                temp=temperature
       if(unit=='c')
                temp=temperature+273
    }
 };

And so on to implement conversion back to whatever unit you want. Basically you pick a base unit that everything is stored as, and convert to and from that. If you're doing SI, perhaps grams, meters, seconds, kelvin, etc.

Ranite answered 15/11, 2012 at 3:42 Comment(1)
This is not Boost.Units way, thus doesn't answer the question.Leshia

© 2022 - 2024 — McMap. All rights reserved.