Library of Classes

Pages: 1... 34567
1
2
3
4
5
6
7
8
9
10
11
12
int numb ;
cout<<"Are you bugging me?" <<endl ;
cout<<"Type a number: " <<endl ;
cin>> numb ;
if (numb%2==0)
{
cout<<"You are bugging me!" <<endl ;
}
else
{
cout<<"You are bugging me!" <<endl ;'
} 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <cstdlib>
#include <iostream>

class temperature
{
   //...
} ;

class distance
{
   //...
}

class speed
{
   //...
}
Are you OK? O_o Maybe you need a break...
Ok, sorry about the delay. I actually haven't finished writing all classes. Here's my header so far, in parts:

Dimensions.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#pragma once

/*
===========================
 Boring Introductory Stuff
===========================
*/
#define _DIMENSIONS_H_

#include <vector>
#include <string>
#include <windows.h>

#ifndef tstring
#ifdef _UNICODE
typedef std::wstring tstring;
#else
typedef std::string tstring;
#endif // _UNICODE
#endif // tstring

//Some useful typedefs from the Windows SDK.
//Since these are defined in WinNT.h, I'll take the chance of using this condition:
#ifndef _WINNT_
#ifdef _UNICODE

typedef wchar_t *LPTSTR;
typedef wchar_t const *LPCTSTR;

#else

typedef char *LPTSTR;
typedef char const *LPCTSTR;

#endif // _UNICODE
#endif // _WINNT_

//If tchar.h has been included, the following is defined already.
#ifndef _T
#ifdef _UNICODE

#define __T(x) L##x

#else

#define __T(x) x

#endif // _UNICODE

#define _T(x) __T(x)

#endif // _T

#ifndef NULL
#define NULL 0
#endif // NULL
/*
===============================
 END Boring Introductory Stuff
===============================
*/

/*
===============
 Basic Classes
===============
*/

class Unit
{
	//Public Interface
public:
	virtual bool IsStandardUnit() const { return false; }
	virtual double GetConversionFactor() const { return 1 }
	virtual LPCTSTR GetName() const { return NULL; }
	virtual LPCTSTR GetSymbol() const { return NULL; }
};

class UnitContext
{
	//Member data.
private:
	double m_exponent;
	Unit &m_unit;

	//Constructors
public:
	UnitContext() : m_exponent(0), m_unit()
	{ }
	UnitContext(double exponent, Unit &unit) : m_exponent(exponent), m_unit(unit)
	{ }

	//Public Interface
public:
	double GetExponent() const { return m_exponent; }
	void SetExponent(double newExponent) { m_exponent = newExponent; }
	Unit const& GetUnit() const { return m_unit; }
	void SetUnit(Unit const &newUnit) { m_unit = newUnit };
}

class CompoundUnit : public Unit
{
	//Internal data.
protected:
	std::vector<UnitContext> _units;

	//Constructors
public:
	CompoundUnit() : Unit()
	{ }
};

class Prefix : public Unit
{
	//Member Data
private:
	double m_convFactor;
	tstring m_name;
	tstring m_symbol;

	//Constructors
private:
	Prefix(double convFactor, LPCTSTR name, LPCTSTR symbol)
		: m_convFactor(convFactor), m_name(name), m_symbol(symbol), Unit()
	{ }

	//Public Interface
	double GetConversionFactor() const { return m_convFactor; }
	LPCTSTR GetName() const { return m_name.c_str(); }
	LPCTSTR GetSymbol() const { return m_symbol.c_str(); }

	//Static Members
public:
	static Prefix const Neutral;

	static Prefix const Deca(10, _T("Deca"), _T("D"));
	static Prefix const Hecto(100, _T("Hecto"), _T("H"));
	static Prefix const Kilo(1000, _T("Kilo"), _T("k"));

	static Prefix const deci(0.1, _T("deci"), _T("d"));
	static Prefix const centi(0.01, _T("centi"), _T("c"));
	static Prefix const milli(0.001, _T("milli"), _T("m"));
};

/*
===================
 END Basic Classes
===================
*/
Dimensions.h (cont'd):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/*
================
 Distance Units
================
*/

//Forward declaration of standard unit.
class MeterUnit;

class DistanceUnit : public Unit
{
	//Constructors
public:
	DistanceUnit() : Unit()
	{ }

	//Static Members
public:
	static DistanceUnit const &StandardUnit = MeterUnit;
};

class MeterUnit : public DistanceUnit
{
	//Constructors
public:
	MeterUnit() : DistanceUnit()
	{ }

	//Public Interface.
public:
	bool IsStandardUnit() const { return true; }
	LPCTSTR GetName() const { return _T("meter"); }
	LPCTSTR GetSymbol() const { return _T("m"); }
};

class FootUnit : public DistanceUnit
{
	//Constructors
public:
	FootUnit() : DistanceUnit
	{ }

	//Public Interface
public:
	double GetConversionFactor() const { return 0.3048; }
	LPCTSTR GetName() const { return _T("foot"); }
	LPCTSTR GetSymbol() const { return _T("ft"); }
};

/*
====================
 END Distance Units
====================
*/

/*
============
 Time Units
============
*/

class TimeUnit : public Unit
{
	//Constructors
public:
	TimeUnit() : Unit()
	{ }
};

class SecondUnit : public TimeUnit
{
	//Constructors
public:
	SecondUnit() : TimeUnit()
	{ }

	//Public Interface
public:
	bool IsStandardUnit() const { return true; }
	LPCTSTR GetName const { return _T("second"); }
	LPCTSTR GetSymbol const { return _T("s"); }
};

class HourUnit : public TimeUnit
{
	//Constructors
public:
	HourUnit() : TimeUnit()
	{ }

	//Public Interface
public:
	double GetConversionFactor() const { return 3600; }
	LPCTSTR GetName const { return _T("hour"); }
	LPCTSTR GetSymbol const { return _T("h"); }
};

/*
================
 END Time Units
================
*/


At this point I am about to define the dimensions (distance, time, speed, etc.).

Disclaimer: I haven't even tried to compile this!!! Me being so confused most of the time about .Net, easily I'm doing something illegal in the above. Please spare me the brutal flaming for now, hehe. I just can't seem to finish writing these classes.

I will post later on the rest of the header (the dimension classes).
You may have already discussed this earlier - I have only skimmed over the explosion of intermediate posts - but who here thinks it's worth distinguishing between, for example
km/s and m/ms (since kilometres per second = metres per millisecond)

My initial idea (only had 20 mins to work on it last night) was to have a unit defined as this class:
1
2
3
4
5
6
7
8
9
10
11
12
struct Unit {
	int M, L, T;
	double SI_Scale;

        Unit(int m = 0, int l = 0, int t = 0, double si = 0.0)

	Unit operator*(const Unit& u) const;
	Unit operator/(const Unit& u) const;

	bool operator==(const Unit& u) const;
	bool operator!=(const Unit& u) const;
};

However, it does not distinguish between the above units.

[edit] Then for time units we would have:
 
Unit seconds(0, 0, 1, 1.0), minutes(0, 0, 1, 60.0), hours(0, 0, 1, 3600.0), days(0, 0, 1, 86400.0);


Unit type are then defined using this:
(I have used a template so a multiple precision library numeric type could be used later in place of double if necessary.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// exception thrown if mismatched unit quantities are added/subtracted
class UnitMismatchException : public std::exception
{
	public: const char* what()
	{
		return "Quantities of mismatched units were added; this is forbidden."; 
	}
};

template <class T = double> class UnitType {
	T value;
	Unit unit;

public:
	UnitType() : value(), unit(), Name(""), Prefix("") {}
	UnitType(const T& t, const Unit& unit, const char* Name, const char* Prefix)
		: value(t), unit(unit), Name(Name), Prefix(Prefix) {}

	T GetValue() { return value; }
	T GetValue(const Unit& ut) { return value * unit.SI_Scale / ut.SI_Scale; }

	// add two unit quantities; if their units have different scales, use the units of the one on the left
	UnitType operator+(const UnitType& ut)
	{
		// check units
		if (ut.unit != unit)
			throw UnitMismatchException();

		// add quantities in units of *this
		UnitType temp;
		temp.value = value+ ut.value * ut.unit.SI_Scale / unit.SI_Scale;

		// set units
		temp.unit = unit;

		return temp;
	}
	// unary negation (do not change unit)
	UnitType operator-()
	{
		return UnitType(-value, unit);
	}
	// subtraction
	UnitType operator-(UnitType& ut)
	{
		return (*this + (-ut));
	}
	// multiplication and division; mismatched units now allowed
	UnitType operator*(const UnitType& ut)
	{
		return UnitType(value * ut.value, unit * ut.unit);
	}
	UnitType operator/(const UnitType& ut)
	{
		return UnitType(value / ut.value, unit / ut.unit);
	}

};

The flaw with this is the lack of named units. I'll try and think of a solution later, but I have to go revise now :/ The names would have to be optional, as I have allowed arbitrary units, so the user might be using unnamed units.

[edit] Now I need to add the names and prefixes to the unit type, and probably make it's data private with accessors or at least public and const.
Last edited on
I did not think about km/s being == to m/ms. However, does that really matter? If the user wants the speed in km/s, who am I to tell him/her "I'll give it to you in m/ms because it is the same"? The classes should give the value in the requested units.

As for your approach, you are making the Unit type to have a value. I don't think a unit conceptually owns the value to the left of it. This is why I separate the objects: Units and Dimensions. In my implementation, Units can even be singletons because they don't hold any state per usage; my Unit class just knows its name, its symbol and its conversion factor to whatever standard unit is defined in its category (DistanceUnit, TimeUnit, etc.).

My design also automatically allows the "discovery" of conversion factors in compound units. I show that in my other post (http://www.cplusplus.com/forum/general/42704/). The exact same rationale can be used to derive the symbol of the compound unit, which cuts me a LOT of code.

One thing I haven't thought of: How can I make my units "transform" into common compound units? Example: kgms^-2 == Newton. If a multiplication yields kgms^-2, then the smart thing would be to get the result with the Unit being Newton.
Regarding automatic discovery of Newtons and other similar units: I think I can resolve this with a global unit resolver. A global unit resolver would be a 100% static class where units register themselves in a std::map collection whose key is the GetSymbol() of the compound class. Then I force every operator in the dimension objects to check the resulting units against this unit resolver. If the resolver finds a match, then the match is used.

hehe, I think that works beautifully. :-)
Your link is subject to the usual problem of absorbing the end bracket ;)

If the user wants the speed in km/s, who am I to tell him/her "I'll give it to you in m/ms because it is the same"?

But the value is exactly the same. As for whether km/s or m/ms is displayed after the value, that could still be controlled by the unit name system I am adding at the moment.

Your approach, which separates each type of unit (mass, length, time, etc) solves this problem, however.

As for generating the notation with the prefixes (kW, km, etc.), is there not a problem here if the user is using imperial units. We don't use kilofeet, etc :P

I don't think a unit conceptually owns the value to the left of it.

The class UnitType may be poorly named. However, it is supposed to be a numerical value with a unit. The actual unit is defined by the class Unit.

I understand the imperial units issue. I am not forcing the use of prefixes in any case, so it is 100% up to the user of the library to add them or not, right? Or do you mean something else? Besides, I can always add one more method to the Unit class: virtual bool PrefixUsage() { return true; }. This would allow the user of the library to inspect at runtime whether a particular unit "discourages" the use of prefixes.

And yes, my separation of units into unit types comes in really handy. For example, I am adding a ConvertTo(Unit) method to all dimension classes (Distance, Time, Speed, Force, Acceleration, etc.). All dimensions inherit from Dimension<class UnitType>. I can then easily force ConvertTo() to accept only units of the same type (time, distance, etc.).
Last edited on
Fair enough :)

Just to clarify, are we envisaging this being used something like this:
1
2
3
typedef double Temperature;
// etc
Temperature temp = Temperature(70.0, Kelvin);

?
I'm afraid I don't follow that piece of code. You are defining Temperature to be a synonym of double. ....===:::....¡¡¡¡???? hehe Please explain.
So, a question: What are we ACTUALLY starting with?
@ Xander314: Are you confusing languages? That doesn't compile.
That actually seems good what webJose put.
I think we should use that to start with.
Last edited on
I didn't understand Xander's either. Maybe he wants automatic casting to double? That's possible with an operator overload:

1
2
3
4
5
6
class Temperature
{
    double m_value;
    ...
    operator double() const { return m_value; }
};
I've got what webJose put in my c++ sheet, so after all that what should we think about doing?
It's probably best to forget my piece of code. Having looked back at it, I conclude that I wasn't in my right mind when I wrote it...

I typedefed temperature as a double since I was thinking that all quantities in units would be numeric. This, of course, forgot that a unit type need to keep track of the units it's in.

As for the second line, I don't really know. I think I forget I had made Temperature a double and was imagining myself to be calling its constructor.

Basically, it was one of the stupidest pieces of code I have ever written, and I have no idea what I was thinking :P I hope you won't mind if I delete it...


I do have an actual idea, but it is undeveloped as yet. If I get the change to finish it, I shall post the code :D
Actually I don't have any of it in my c++ sheet.
Xander, Thanks for the explanation, hehe.

Guys, question:

I want to create singletons of my units. Examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MeterUnit : public LengthUnit
{
    //Definitions here.
    //Now a static field that holds the singleton:
public:
    static MeterUnit const Meter;
};

class FootUnit : public LengthUnit
{
    //Definitions here.
    //Now a static field that holds the singleton:
public:
    static FootUnit const Foot;
};


The above sure looks nice. Questions/problems now:

1. Is the following valid?
1
2
3
4
5
6
7
using FootUnit::Foot;
using MeterUnit::Meter;

//So that a user of the library could just use the singletons like this:

Length myLength(10, Meter);
Length otherLength(40, Foot);


2. Consider the following simplified class as the base of all my dimension classes:
1
2
3
4
5
6
7
8
9
template<class T>
class Dimension
{
    T &m_unit;
    void ConvertTo(T &newUnit)
    {
        m_unit = newUnit;
    }
};


How can I resolve the const problem? It will arise in the user of the library in code such as:
1
2
Length myLength(10, Meter);
myLength.ConvertTo(Foot); //Illegal!!! Foot (or FootUnit::Foot) is const and the parameter in ConvertTo is not. 
Last edited on
Pages: 1... 34567