CompoundUnit.h
1 /* Distributed under the Apache License, Version 2.0.
2  See accompanying NOTICE file for details.*/
3 
4 //----------------------------------------------------------------------------
11 //----------------------------------------------------------------------------
12 #pragma once
13 
14 // Let's try to make this as lightweight as possible so that CCompoundUnit
15 // objects can be efficiently manipulated within the context of an enclosing
16 // physical quantity object (name TBD: PhysicsQuantity? DimensionedQuantity?
17 // How about just Quantity?) when arithmetic with them needs to be done. This
18 // means maximizing use of inline functions, even those that manipulate the
19 // CUEVecType elements, which in turn requires dealing with Microsoft's BS for
20 // exporting STL classes and classes containing STL data objects as data members.
21 // (See http://support.microsoft.com/kb/168958) The only STL class that you can do
22 // this with is vector<>, but fortunately that's all we need here. Usually, I get
23 // around this by declaring the data objects as pointers to STL template
24 // instantiations that I dynamically allocate and manipulate in non-inline functions,
25 // but since we're going for inline methods here, doing so wouldn't buy me anything,
26 // so I may as well make them first class data members. That may have the added
27 // benefit of coaxing out any helpful warning messages that the compiler might
28 // mistakenly think are not relevant when the data members are merely pointers to
29 // STL classes. -CRV
30 
31 // Note: The ordering of the following two declarations is vital. The above-mentioned
32 // knowledge base article only talks about exporting the vector class that I'm
33 // explicitly instantiating, but that results in an error message about the allocator
34 // needing to be exported. Adding the allocator export after the vector export didn't
35 // solve the problem. But the paragraph about nested classes in the KB article alludes to
36 // a circular dependency that manifests itself as different error messages depending
37 // on which thing you export first. So, on a hunch, I reversed the order, and voila!
38 
39 class CDM_DECL CCompoundUnit
40 {
41  // Define the vector type that holds our individual components of a CompoundUnit
42  typedef std::vector<CCompoundUnitElement> CUEVecType;
43 
44 public:
45  // Default ctor
47  :m_CUD(nullptr), m_dBigness(1.0), m_bStaleBigness(true), m_bStaleDimension(true),
48  m_bDBFlag(false), m_bExplicitDBFlag(false), m_bExplicitNonDBFlag(false)
49  {
50  };
51 
52  // Construct directly from a unit string specification
53  CCompoundUnit(const std::string &unitString)
54  :m_CUD(nullptr), m_dBigness(1.0), m_bStaleBigness(true), m_bStaleDimension(true),
55  m_bDBFlag(false), m_bExplicitDBFlag(false), m_bExplicitNonDBFlag(false)
56  {
57  ParseString(unitString);
58  }
59 
60  // Copy ctor
61  CCompoundUnit(const CCompoundUnit &src) // Copy constructor
62  :m_CUEVec(src.m_CUEVec), m_dBigness(src.m_dBigness), m_bStaleBigness(src.m_bStaleBigness),
63  m_strUnit(src.m_strUnit), m_bStaleDimension(src.m_bStaleDimension),m_bDBFlag(src.m_bDBFlag),
64  m_bExplicitDBFlag(src.m_bExplicitDBFlag), m_bExplicitNonDBFlag(src.m_bExplicitNonDBFlag)
65  {
66  // In the initializer list, I'm assuming that initializing one vector with another
67  // copies the vector correctly. For the CUnitDimension object, we can't just
68  // initialize our pointer with the src's pointer, because each CCompoundUnit owns its
69  // CUnitDimension object, so we need to invoke the copy constructor on the
70  // CUnitDimension. Unfortunately, since the pointer might be nullptr, we can't simply
71  // do this in the initializer list.
72  if (src.m_CUD == nullptr)
73  {
74  m_CUD = nullptr;
75  }
76  else
77  {
78  m_CUD = new CUnitDimension(*src.m_CUD);
79  }
80  };
81 
82  // dtor
83  virtual ~CCompoundUnit()
84  {
85  if (m_CUD)
86  {
87  delete m_CUD;
88  }
89  };
90 
91  // Obtain a dimension object that uniquely represents the dimensions of this
92  // compound unit in terms of fundamental quantities
93  const CUnitDimension *GetDimension() const;
94 
95  // Obtain the relative magnitude of this compound unit in relation to
96  // an equivalently-dimensioned compound unit comprised of the base units
97  // of each fundamental quantity type. The bigness is the key to unit conversion.
98  // For any compound unit of a given dimension, the value of a quantity in that
99  // unit times the bigness of that unit is an invariant quantity.
100  const double &GetBigness() const;
101 
102  // Obtain the bias of this compound unit. Biases denote additive offsets of one unit
103  // with respect to another unit of the same type. I am currently operating under the
104  // assumption that it is nonsensical to process biases in a compound unit consisting
105  // of more than one fundamental quantity type raised to the power of 1.0. If I come
106  // across a counter-example to this, I may have to re-think things, but for now, I am
107  // assuming that when multiplying a biased unit by other quantities, the bias must be
108  // irrelevant. Currently, the only quantity type that has biased units is temperature,
109  // and this principle seems to hold. E.g., J/degC means the same thing as J/degK even
110  // though there's a bias in converting from degC to degK.
111  double GetBias() const;
112 
113  // We can do arithmetic with these, so overload a few operators
114  // But do *NOT* define operator+, operator-, or the assignment
115  // versions of these operators. You cannot "add" two CompoundUnit
116  // objects. You can add two *values* if they have equivalent
117  // compound unit objects, but the only operations that
118  // can be performed on a compound unit object itself are multiplication,
119  // division, and raising to a power.
121  {
122  if (this != &rhs)
123  {
124  m_dBigness = rhs.m_dBigness;
125  m_bDBFlag = rhs.m_bDBFlag;
126  m_bExplicitDBFlag = rhs.m_bExplicitDBFlag;
127  m_bExplicitNonDBFlag = rhs.m_bExplicitNonDBFlag;
128 
129  // Vector objects should know how to deep-copy themselves
130  m_CUEVec = rhs.m_CUEVec;
131  // Free my dimension if I have one, whether it's stale or now
132  if (m_CUD)
133  {
134  delete m_CUD;
135  m_CUD = nullptr;
136  }
137  // We need to deep-copy the CUnitDimension, not copy pointers
138  // But only copy if RHS is not stale. No sense duplicating
139  // something that's stale. Also, if it's nullptr, it's stale, so
140  // no need for a separate nullptr check.
141  if (! rhs.m_bStaleDimension)
142  {
143  m_CUD = new CUnitDimension(*rhs.m_CUD);
144  }
145  m_bStaleBigness = rhs.m_bStaleBigness;
146  m_bStaleDimension = rhs.m_bStaleDimension;
147  }
148  return *this;
149  };
150 
151  CCompoundUnit & operator*=(const CCompoundUnit &rhs);
152  CCompoundUnit & operator/=(const CCompoundUnit &rhs);
153 
155  {
156  return CCompoundUnit(*this)*= rhs;
157  }
158 
160  {
161  return CCompoundUnit(*this)/= rhs;
162  }
163 
164  // There's no "raise to a power" operator, but this is the next best thing.
166 
167  // Compare two CompoundUnits
168  // We don't care about how the units are precisely represented, whether
169  // one contains "J" and the other contains "kg m^2 s^-2", or "s^-2 m^2 kg"
170  // for that matter. We care about whether they are equivalent. And they are
171  // equivalent if and only if they are equivalent in both dimension and bigness.
172  bool operator==(const CCompoundUnit &rhs) const
173  {
174  return ( (*this->GetDimension() == *rhs.GetDimension()) &&
175  (this->GetBigness() == rhs.GetBigness()) &&
176  (this->GetBias() == rhs.GetBias()));
177  }
178 
179  bool operator!=(const CCompoundUnit &rhs) const
180  {
181  return !(*this == rhs);
182  }
183 
184  // Is this CompoundUnit dimensionally-compatible with a pre-defined
185  // quantity type?
186  bool IsOfType(int quantityTypeID);
187  bool IsOfType(const std::string &quantityName);
188 
189  // Is this CompoundUnit dimensionless
190  bool IsDimensionless() const
191  {
192  return this->GetDimension()->IsDimensionless();
193  }
194 
195  // Is this CompoundUnit in "decibel" mode
196  bool IsDecibel() const;
197 
198  bool IsUnitEmpty() const
199  {
200  return this->m_strUnit.empty();
201  }
202 
203  // Returns either 10 or 20, depending on whether this compound unit is of a
204  // Quantity Type that obeys the 20-Log-Rule
205  double GetDecibelLogScaleFactor() const;
206 
208  {
209  // Make this explicitly decibel mode, in case we need to do a conversion to/from dB.
210  // We need explicit flags for each condition, so that with both of them off, the
211  // dB state is driven by the presence of a dB mode in the Compound Unit Elements
212  // (through the CompoundUnit expansion of a Unit Descriptor).
213  m_bExplicitDBFlag = true;
214  m_bExplicitNonDBFlag = false;
215  m_bDBFlag = true;
216  }
217 
219  {
220  // Make this explicitly not-decibel mode
221  m_bExplicitDBFlag = false;
222  m_bExplicitNonDBFlag = true;
223  m_bDBFlag = false;
224  }
225 
226  // Used for incrementally building up a CompoundUnit
227  CCompoundUnitElement & AddElement(const CCompoundUnitElement &);
228 
229  // Erase everything
230  void clear()
231  {
232  if (m_CUD)
233  {
234  delete m_CUD;
235  m_CUD = nullptr;
236  }
237  m_dBigness = 1.0;
238  m_bStaleBigness = true;
239  m_bStaleDimension = true;
240  // The explicit dB flag is true if the CompoundUnit explicitly contained a "dB" token.
241  // The regular dB flag is true if either the explicit dB flag is true or if any of the
242  // Compound Unit Elements refer to Unit Descriptors whose Compound Unit expansion has
243  // its dB flag set
244  m_bExplicitDBFlag = false;
245  m_bDBFlag = false;
246  m_CUEVec.clear();
247  m_strUnit.clear();
248  }
249 
250  // Build up my internals from a string specification.
251  void ParseString(const std::string &unitString);
252 
253  std::string GetString() const {return m_strUnit;}
254 
255  // Auxiliary output routine
256  std::ostream & PrintSelf(std::ostream &output) const;
257 
258 protected:
259  void BuildDimension() const;
260  void ComputeBigness() const;
261 
262 private:
263  // Many of these are declared "mutable" so that they can be changed on a "const"
264  // object. Ordinarily we don't let const objects be modified, but this allows us to
265  // declare certain member function "const" when the function is supposed to be "read-only",
266  // but in reality the "read" causes the update of cached values, and those must be
267  // declared "mutable"
268  mutable std::string m_strUnit;
269  mutable double m_dBigness;
270  CUEVecType m_CUEVec;
272  mutable bool m_bStaleBigness;
273  mutable bool m_bStaleDimension;
274  mutable bool m_bExplicitNonDBFlag;
275  mutable bool m_bExplicitDBFlag;
276  mutable bool m_bDBFlag;
277 };
278 
279 inline CCompoundUnit pow(const CCompoundUnit &baseref, CCompoundUnitElement::ExponentType exp)
280 {
281  // Construct a "C++ temporary" that we modify by raising to the power.
282  // It's the same principle that guides the implementation of operator*
283  // (see above) as described by Myers, except that there is no operator
284  // that corresponds to raising to a power, so we use an ordinary
285  // function instead.
286 
287  return (CCompoundUnit(baseref)).Raise(exp);
288 }
289 
290 inline CCompoundUnit sqrt(const CCompoundUnit &argref)
291 {
292  return pow(argref, 0.5);
293 }
294 
295 inline std::ostream & operator<<(std::ostream &out, const CCompoundUnit &ccu)
296 {
297  //return ccu.PrintSelf(output);
298  out << ccu.GetString();
299  return out;
300 }
301 inline std::ostream& operator<< (std::ostream& out, const CCompoundUnit* ccu)
302 {
303  if (ccu == nullptr)
304  out << "";
305  else
306  out << ccu->GetString();
307  return out;
308 }
bool operator==(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:172
virtual ~CCompoundUnit()
Definition: CompoundUnit.h:83
CCompoundUnit & operator=(const CCompoundUnit &rhs)
Definition: CompoundUnit.h:120
CCompoundUnit & Raise(CCompoundUnitElement::ExponentType)
Definition: CompoundUnit.cpp:594
CCompoundUnit(const CCompoundUnit &src)
Definition: CompoundUnit.h:61
bool m_bDBFlag
Definition: CompoundUnit.h:276
bool IsDimensionless() const
Definition: CompoundUnit.h:190
std::string GetString() const
Definition: CompoundUnit.h:253
CUEVecType m_CUEVec
Definition: CompoundUnit.h:270
const CUnitDimension * GetDimension() const
Definition: CompoundUnit.cpp:235
bool m_bStaleDimension
Definition: CompoundUnit.h:273
CCompoundUnit operator/(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:159
CCompoundUnit()
Definition: CompoundUnit.h:46
CUnitDimension * m_CUD
Definition: CompoundUnit.h:271
void DecibelModeOn()
Definition: CompoundUnit.h:207
void DecibelModeOff()
Definition: CompoundUnit.h:218
std::string m_strUnit
Definition: CompoundUnit.h:268
double GetBias() const
Definition: CompoundUnit.cpp:290
Definition: CompoundUnitElement.h:15
bool operator!=(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:179
Definition: SnapValue.h:37
bool m_bExplicitNonDBFlag
Definition: CompoundUnit.h:274
double m_dBigness
Definition: CompoundUnit.h:269
CCompoundUnit(const std::string &unitString)
Definition: CompoundUnit.h:53
CCompoundUnit operator*(const CCompoundUnit &rhs) const
Definition: CompoundUnit.h:154
void clear()
Definition: CompoundUnit.h:230
bool m_bStaleBigness
Definition: CompoundUnit.h:272
bool IsUnitEmpty() const
Definition: CompoundUnit.h:198
Definition: UnitDimension.h:21
Definition: CompoundUnit.h:39
const double & GetBigness() const
Definition: CompoundUnit.cpp:251
bool IsDimensionless() const
Definition: UnitDimension.h:216
bool m_bExplicitDBFlag
Definition: CompoundUnit.h:275
std::vector< CCompoundUnitElement > CUEVecType
Definition: CompoundUnit.h:42