Problem statement:
Write a program that converts all given temperatures from a given input temperature scale to a given output temperature scale. The temperature scales to be supported are Kelvin, Celsius, Fahrenheit, Rankine, Delisle, Newton, Rømer, Réaumur.
Synopsis: tempconv INPUT_SCALE OUTPUT_SCALE [TEMPERATURE]...
The INPUT_SCALE
and OUTPUT_SCALE
shall be given as follows:
K
for KelvinC
for CelsiusF
for FahrenheitR
for RankineD
for DelisleN
for NewtonRø
for RømerRé
for Réaumur.
Example:
tempconv K C 0 273.15 373.15
-273.15
0.0
100.0
My solution in C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TEMPERATURE_CONVERTERS \
TEMPERATURE_CONVERTER(kelvin, kelvin, kelvin, "Kelvin", "K", "k") \
TEMPERATURE_CONVERTER(celsius, celsius + 273.15, kelvin - 273.15, "Celsius", "°C", "C", "c") \
TEMPERATURE_CONVERTER(delisle, 373.15 - delisle * 2 / 3, (373.15 - kelvin) * 3 / 2, "Delisle", "°De", "De", "DE", "de") \
TEMPERATURE_CONVERTER(fahrenheit, (fahrenheit + 459.67) * 5 / 9, kelvin * 9 / 5 - 459.67, "Fahrenheit", "°F", "F", "f") \
TEMPERATURE_CONVERTER(newton, newton * 100 / 33 + 273.15, (kelvin - 273.15) * 33 / 100, "Newton", "°N", "N", "n") \
TEMPERATURE_CONVERTER(rankine, rankine * 5 / 9, kelvin * 9 / 5, "Rankine", "°R", "R", "r") \
TEMPERATURE_CONVERTER(réaumur, réaumur * 5 / 4 + 273.15, (kelvin - 273.15) * 4 / 5, "Réaumur", "°Ré", "°Re", "Ré", "RÉ", "ré", "Re", "RE", "re") \
TEMPERATURE_CONVERTER(rømer, (rømer - 7.5) * 40 / 21 + 273.15, (kelvin - 273.15) * 21 / 40 + 7.5, "Rømer", "°Rø", "°Ro", "Rø", "RØ", "rø", "Ro", "RO", "ro") \
struct TemperatureConverter {
const char **names;
double (*toKelvin)(double other);
double (*fromKelvin)(double kelvin);
};
#define TEMPERATURE_CONVERTER(name, toKelvinExpression, fromKelvinExpression, ...) \
double name ## ToKelvin(double name) { \
return toKelvinExpression; \
} \
double name ## FromKelvin(double kelvin) { \
return fromKelvinExpression; \
} \
const struct TemperatureConverter name = { \
(const char *[]) { __VA_ARGS__, NULL }, \
name ## ToKelvin, \
name ## FromKelvin, \
};
TEMPERATURE_CONVERTERS
#undef TEMPERATURE_CONVERTER
#define TEMPERATURE_CONVERTER(name, ...) &name,
const struct TemperatureConverter* const converters[] = { TEMPERATURE_CONVERTERS NULL };
const struct TemperatureConverter *TemperatureConverter_get(const char *requestedName) {
for (const struct TemperatureConverter *const *converter = converters; *converter; converter++)
for (const char **name = (*converter)->names; *name; name++)
if (strcmp(*name, requestedName) == 0)
return *converter;
fprintf(stderr, "error: Unknown temperature scale %s\n", requestedName);
exit(1);
}
int main([[maybe_unused]] const int argc, const char *const argv[]) {
double (*const toKelvin)(double) = TemperatureConverter_get(argv[1])->toKelvin;
double (*const fromKelvin)(double) = TemperatureConverter_get(argv[2])->fromKelvin;
for (argv += 3; *argv; argv++)
printf("%.6g\n", fromKelvin(toKelvin(atof(*argv))));
}
Note:
- I know that the error handling is incomplete, and I'm fine with that.
- I know that some code styles say that
if
,for
etc should always have braces. I'm fine with single statementif
,for
to not have braces.
maybe_unused
is part of C23 and hasn't been accepted yet as part of the standard. \$\endgroup\$