It's harder to do than criticize. Here's my attempt to implement a Celsius-to-Fahrenheit conversion table in C++.
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#define DEGREE_SIGN "\u00B0"
class Fahrenheit;
class Celsius {
public:
explicit Celsius(double c) : c(c) {
if (c < MIN_VALUE) {
throw "Temperature below minimum";
} else if (c > MAX_VALUE) {
throw "Temperature above maximum";
}
}
Celsius(Fahrenheit f);
operator double() const { return c; }
Celsius operator +(Celsius incr) const { return Celsius(c + incr.c); }
Celsius &operator +=(Celsius incr) { c += incr.c; return *this; }
friend std::ostream &operator<<(std::ostream &os, Celsius c) {
return os << (double)c << DEGREE_SIGN << 'C';
}
static const Celsius MIN; // Absolute zero
static const Celsius MAX; // Absolute hot (approx.)
private:
double c;
static const double MIN_VALUE;
static const double MAX_VALUE;
};
class Fahrenheit {
public:
explicit Fahrenheit(double f) : f(f) {
if (f < MIN_VALUE) {
throw "Temperature below minimum";
} else if (f > MAX_VALUE) {
throw "Temperature above maximum";
}
}
Fahrenheit(Celsius c);
operator double() const { return f; }
Fahrenheit operator +(Fahrenheit incr) const { return Fahrenheit(f + incr.f); }
Fahrenheit &operator +=(Fahrenheit incr) { f += incr.f; return *this; }
friend std::ostream &operator<<(std::ostream &os, Fahrenheit f) {
return os << (double)f << DEGREE_SIGN << 'F';
}
static const Fahrenheit MIN; // Absolute zero
static const Fahrenheit MAX; // Absolute hot (approx.)
private:
double f;
static const double MIN_VALUE;
static const double MAX_VALUE;
};
Celsius::Celsius(Fahrenheit f) : c((f - 32) / 9 * 5) {}
const double Celsius::MIN_VALUE = -273.15;
const double Celsius::MAX_VALUE = 1.4e32;
const Celsius Celsius::MIN = Celsius(Celsius::MIN_VALUE);
const Celsius Celsius::MAX = Celsius(Celsius::MAX_VALUE);
Fahrenheit::Fahrenheit(Celsius c) : f(c / 5 * 9 + 32) {}
const double Fahrenheit::MIN_VALUE = -459.67;
const double Fahrenheit::MAX_VALUE = 2.5e32;
const Fahrenheit Fahrenheit::MIN = Fahrenheit(Fahrenheit::MIN_VALUE);
const Fahrenheit Fahrenheit::MAX = Fahrenheit(Fahrenheit::MAX_VALUE);
#ifdef _WIN32
const char PATH_SEPARATOR = '\\';
#else
const char PATH_SEPARATOR = '/';
#endif
const char *basename(const char *path) {
const char * sep = strrchr(path, PATH_SEPARATOR);
return sep ? sep + 1 : path;
}
int main(int argc, char *argv[]) {
if (argc != 4) {
std::cout << "Usage: " << basename(argv[0]) << " MIN MAX STEP" << std:: endl;
return 1;
}
try {
char *minEnd, *maxEnd, *stepEnd;
Celsius min(strtod(argv[1], &minEnd)),
max(strtod(argv[2], &maxEnd)),
step(strtod(argv[3], &stepEnd));
if (*minEnd != '\0') {
std::cerr << "Invalid minimum temperature" << std::endl;
return 1;
} else if (*maxEnd != '\0') {
std::cerr << "Invalid maximum temperature" << std::endl;
return 1;
} else if (*stepEnd != '\0') {
std::cerr << "Invalid temperature step" << std::endl;
return 1;
}
if (step <= 0.0) {
std::cerr << "Step must be positive" << std::endl;
return 1;
} else if (step > max - min) {
std::cerr << "Step exceeds temperature range" << std::endl;
return 1;
} else if (max < min) {
std::cerr << "Minimum temperature exceeds maximum temperature" << std::endl;
return 1;
}
for (Celsius c = min; c <= max; c += step) {
std::cout << std::setw(8) << c
<< std::setw(8) << Fahrenheit(c)
<< std::endl;
}
} catch (const char *msg) {
std::cerr << msg << std::endl;
return 1;
}
return 0;
}
Some concerns I have:
- What do you think of the input validation?
- Is the code repetition justified? Much of the
Fahrenheit
class is not strictly necessary for the problem, and is included for completeness. Still, does the repetition enhance clarity, or is it harmful?