I'm using the below code to try and make it easy to do sensible calculations for different variables, i.e. pressure + height is nonsensical, and pressure in atm += pressure in bar should add taking units into account. I've tried playing round with dynamic and static cast as well as CRTP on the advice of other fine members of the stack exchange community, but couldn't get those methods working. My code as is works but the + operator returns a Variable type not a Pressure type.
It's all in one file at the moment as I haven't messed with declaring template types in implementation files.
#ifndef PARENT_VARIABLE_H_
#define PARENT_VARIABLE_H_
#include <string>
#include <map>
#include <stdio.h>
#include <typeinfo>
#include <boost/assign.hpp>
template <typename V,typename D> // To let different variables use different S.F.
struct Variable{
/*
* This structure is a abstract base class from which all dimensional variables
* are derived. Derived variables may be operated with and units will be largely
* automatically handled.
*/
protected:
//Functions
Variable(V v, std::string u,std::string * m_type, D conversions [], const std::map<std::string,short> * units);
//~Variable();
bool check_type(const std::string & two) const;
V consistant_units(const Variable & two) const;
//Variables
V value;
std::string unit;
const std::map<std::string, short> * valid_units; //contains all units for which conversions have been defined.
D * conversion; //An n by n array for calculating conversions
std::string * type;
public:
V get_value() const;
std::string get_unit() const;
void change_unit(const std::string & new_unit);
//Overloaded operators
//Changes self
Variable & operator+= (const Variable &t);
//Returns new Variable type
Variable & operator+(const Variable & other) const;
};
//////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* Class functions
*/
FILE * E_FILE;
void warning(const std::string & message){ //Placeholder function I'm not sure what I want to do with my warning messages yet
E_FILE = fopen ("error_file.txt","w");
std::cout<<message; // might change to output file
fclose (E_FILE);
}
template<typename V,typename D>
Variable<V,D>::Variable(V v, std::string u, std::string * m_type, D conversions [], const std::map<std::string,short> * units){
value = v;
unit = u;
conversion = conversions;
valid_units = units;
type = m_type;
}
template <typename V,typename D>
V Variable<V,D>::get_value() const{
return value;
}
template <typename V,typename D>
std::string Variable<V,D>::get_unit() const{
return unit;
}
template <typename V,typename D>
void Variable<V,D>::change_unit(const std::string & new_unit){
if (valid_units->find( new_unit ) == valid_units->end()){//Check the unit is defined
std::string message = new_unit +" is not a valid unit. /n";
warning(message);
}
else{
int target = valid_units->find(new_unit)->second;
int original = valid_units->find(unit)->second;
value*=conversion[ (original*valid_units->size()) + target];
unit=new_unit;
}
}
template <typename V,typename D>
bool Variable<V,D>::check_type(const std::string & two) const{
if(*type!=two){
std::string message = two + " is not the same as "
+ *type + " adding them is nonsensical. /n";
warning(message);
return false;
}
return true;
}
template <typename V,typename D>
V Variable<V,D>::consistant_units(const Variable<V,D> & two) const{
if (unit!=two.unit){
std::string message = *type+
" units different, answer is in "+ unit + " /n";
warning(message);
int target = valid_units->find(unit)->second;
int original = valid_units->find(two.unit)->second;
return (two.value)*conversion[ (original*valid_units->size()) + target];
}
return two.value;
}
template <typename V,typename D>
Variable<V,D> & Variable<V,D>::operator +=(const Variable<V,D> & t){
if (check_type(*t.type)){
V other_value = consistant_units(t);
value += other_value;
}
return *this;
}
template <typename V,typename D>
Variable<V,D> & Variable<V,D>::operator+(const Variable<V,D> &other) const {
return Variable(*this) += other;
}
/////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* Pressure
*/
template <typename V>
class Pressure : public Variable<V,double> {
public:
Pressure(V v,std::string u);
//~Pressure();
};
///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* Pressure
*/
static const std::map<std::string, short> PRESSURE_UNITS =
boost::assign::map_list_of("kPa",0)("atm",1)("psi",2);
static double PRESSURE_CONVERSION[3][3]= {{1,0.009869232667,0.145037738},
{101.325,1,14.6959488},
{6.89475729,0.0680459639,1}};
static std::string PRESSURE_TYPE = "Pressure";
template <typename V>
Pressure<V>::Pressure(V v, std::string u) : Variable<V,double>(v,u,&PRESSURE_TYPE,PRESSURE_CONVERSION[0],&PRESSURE_UNITS){
}
#endif /* PARENT_VARIABLE_H_ */