core: add CRTP base class to unit types

The goal here is to add general addition and scalar multiplication
functions to the unit types.

Thereto, we need a CRTP
(https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
base class.

However, this breaks compound initialization, so we have to use
named initializers:
	weight_t { 2000 } -> weight_t { .grams = 2000 }
The good thing is that this is exactly how these classes were
supposed to be used: make the unit explicit!

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2024-09-01 21:48:31 +02:00 committed by bstoeger
parent 696ba61eef
commit 12ca172a9e
26 changed files with 127 additions and 138 deletions

View file

@ -618,7 +618,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco
*/
tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]);
if (tmp != 0x7fff) {
weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false };
weightsystem_t ws = { {.grams = tmp * 10}, translate("gettextFromC", "unknown"), false };
dt_dive->weightsystems.push_back(std::move(ws));
}

View file

@ -2248,7 +2248,7 @@ duration_t dive::totaltime() const
time = dc_time;
}
}
return { time };
return { .seconds = time };
}
timestamp_t dive::endtime() const
@ -2367,7 +2367,7 @@ bool dive::cache_is_valid() const
pressure_t dive::get_surface_pressure() const
{
return surface_pressure.mbar > 0 ? surface_pressure
: pressure_t { SURFACE_PRESSURE };
: pressure_t { .mbar = SURFACE_PRESSURE };
}
/* This returns the conversion factor that you need to multiply
@ -2459,13 +2459,13 @@ int dive::mbar_to_depth(int mbar) const
depth_t dive::gas_mod(struct gasmix mix, pressure_t po2_limit, int roundto) const
{
double depth = (double) mbar_to_depth(po2_limit.mbar * 1000 / get_o2(mix));
return depth_t { (int)lrint(depth / roundto) * roundto };
return depth_t { .mm = (int)lrint(depth / roundto) * roundto };
}
/* Maximum narcotic depth rounded to multiples of roundto mm */
depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const
{
pressure_t ppo2n2 { depth_to_mbar(end.mm) };
pressure_t ppo2n2 { .mbar = depth_to_mbar(end.mm) };
int maxambient = prefs.o2narcotic ?
(int)lrint(ppo2n2.mbar / (1 - get_he(mix) / 1000.0))
@ -2475,7 +2475,7 @@ depth_t dive::gas_mnd(struct gasmix mix, depth_t end, int roundto) const
:
// Actually: Infinity
1000000;
return depth_t { (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto };
return depth_t { .mm = (int)lrint(((double)mbar_to_depth(maxambient)) / roundto) * roundto };
}
std::string dive::get_country() const
@ -2677,7 +2677,7 @@ temperature_t dive::dc_watertemp() const
}
if (!nr)
return temperature_t();
return temperature_t{ static_cast<uint32_t>((sum + nr / 2) / nr) };
return temperature_t{ .mkelvin = static_cast<uint32_t>((sum + nr / 2) / nr) };
}
/*
@ -2695,7 +2695,7 @@ temperature_t dive::dc_airtemp() const
}
if (!nr)
return temperature_t();
return temperature_t{ static_cast<uint32_t>((sum + nr / 2) / nr) };
return temperature_t{ .mkelvin = static_cast<uint32_t>((sum + nr / 2) / nr) };
}
/*
@ -2782,5 +2782,5 @@ weight_t dive::total_weight() const
// TODO: implement addition for units.h types
return std::accumulate(weightsystems.begin(), weightsystems.end(), weight_t(),
[] (weight_t w, const weightsystem_t &ws)
{ return weight_t{ w.grams + ws.weight.grams }; });
{ return weight_t{ .grams = w.grams + ws.weight.grams }; });
}

View file

@ -112,9 +112,9 @@ void set_tank_info_data(std::vector<tank_info> &table, const std::string &name,
std::pair<volume_t, pressure_t> extract_tank_info(const struct tank_info &info)
{
pressure_t working_pressure {
static_cast<int32_t>(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi))
.mbar = static_cast<int32_t>(info.bar != 0 ? info.bar * 1000 : psi_to_mbar(info.psi))
};
volume_t size { 0 };
volume_t size;
if (info.ml != 0)
size.mliter = info.ml;
else if (working_pressure.mbar != 0)
@ -128,7 +128,7 @@ std::pair<volume_t, pressure_t> get_tank_info_data(const std::vector<tank_info>
auto it = std::find_if(table.begin(), table.end(), [&name](const tank_info &info)
{ return info.name == name; });
return it != table.end() ? extract_tank_info(*it)
: std::make_pair(volume_t{0}, pressure_t{0});
: std::make_pair(volume_t(), pressure_t());
}
void add_cylinder_description(const cylinder_type_t &type)
@ -182,7 +182,7 @@ volume_t cylinder_t::gas_volume(pressure_t p) const
{
double bar = p.mbar / 1000.0;
double z_factor = gas_compressibility_factor(gasmix, bar);
return volume_t { static_cast<int>(lrint(type.size.mliter * bar_to_atm(bar) / z_factor)) };
return volume_t { .mliter = static_cast<int>(lrint(type.size.mliter * bar_to_atm(bar) / z_factor)) };
}
int find_best_gasmix_match(struct gasmix mix, const struct cylinder_table &cylinders)

View file

@ -16,8 +16,8 @@ struct gasmix {
fraction_t he;
std::string name() const;
};
static const struct gasmix gasmix_invalid = { { -1 }, { -1 } };
static const struct gasmix gasmix_air = { { 0 }, { 0 } };
static const struct gasmix gasmix_invalid = { { .permille = -1 }, { .permille = -1 } };
static const struct gasmix gasmix_air = { { .permille = 0 }, { .permille = 0 } };
enum gastype {
GASTYPE_AIR,

View file

@ -217,7 +217,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
// is not repeated ad-nauseum for broken images.
if (numPics == 0 && prefs.extract_video_thumbnails) {
QMetaObject::invokeMethod(VideoFrameExtractor::instance(), "extract", Qt::AutoConnection,
Q_ARG(QString, filename), Q_ARG(QString, filename), Q_ARG(duration_t, duration_t{(int32_t)duration}));
Q_ARG(QString, filename), Q_ARG(QString, filename), Q_ARG(duration_t, duration_t{ .seconds = (int32_t)duration}));
}
// Currently, we support only one picture
@ -231,7 +231,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
res = videoImage; // No picture -> show dummy-icon
else
markVideoThumbnail(res); // We got an image -> place our video marker on top of it
return { res, MEDIATYPE_VIDEO, { (int32_t)duration } };
return { res, MEDIATYPE_VIDEO, { .seconds = (int32_t)duration } };
}
// Fetch a thumbnail from cache.

View file

@ -309,7 +309,7 @@ static int divinglog_dive(void *param, int, char **data, char **)
state->cur_dive->watertemp.mkelvin = C_to_mkelvin(atol(data[9]));
if (data[10]) {
weightsystem_t ws = { { atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false };
weightsystem_t ws = { { .grams = atoi(data[10]) * 1000 }, translate("gettextFromC", "unknown"), false };
state->cur_dive->weightsystems.push_back(std::move(ws));
}

View file

@ -160,7 +160,7 @@ static dc_status_t parse_gasmixes(device_data_t *devdata, struct dive *dive, dc_
}
}
bool no_volume = true;
struct gasmix bottom_gas = { {1000}, {0} }; /* Default to pure O2, or air if there are no mixes defined */
struct gasmix bottom_gas = { { .permille = 1000}, {} }; /* Default to pure O2, or air if there are no mixes defined */
if (ngases == 0) {
bottom_gas = gasmix_air;
}

View file

@ -41,7 +41,7 @@ preferences::preferences() :
ascratestops(9000 / 60),
ascrate50(9000 / 60),
ascrate75(9000 / 60),
bestmixend({ 30000 }),
bestmixend({ .mm = 30'000 }),
bottompo2(1400),
bottomsac(20000),
decopo2(1600),

View file

@ -1504,8 +1504,8 @@ std::vector<std::string> compare_samples(const struct dive *d, const struct plot
const cylinder_t *cyl = d->get_cylinder(cylinder_index);
// TODO: Implement addition/subtraction on units.h types
volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ last_pressures[cylinder_index] }).mliter -
cyl->gas_volume((pressure_t){ next_pressure }).mliter;
volumes_used[cylinder_index] += cyl->gas_volume((pressure_t){ .mbar = last_pressures[cylinder_index] }).mliter -
cyl->gas_volume((pressure_t){ .mbar = next_pressure }).mliter;
}
// check if the gas in this cylinder is being used

View file

@ -786,33 +786,6 @@ int parseTemperatureToMkelvin(const QString &text)
return mkelvin;
}
int parseWeightToGrams(const QString &text)
{
int grams;
QString numOnly = text;
numOnly.replace(",", ".").remove(QRegularExpression("[^0-9.]"));
if (numOnly.isEmpty())
return 0;
double number = numOnly.toDouble();
if (text.contains(gettextFromC::tr("kg"), Qt::CaseInsensitive)) {
grams = lrint(number * 1000);
} else if (text.contains(gettextFromC::tr("lbs"), Qt::CaseInsensitive)) {
grams = lbs_to_grams(number);
} else {
switch (prefs.units.weight) {
case units::KG:
grams = lrint(number * 1000);
break;
case units::LBS:
grams = lbs_to_grams(number);
break;
default:
grams = 0;
}
}
return grams;
}
int parsePressureToMbar(const QString &text)
{
int mbar;

View file

@ -69,7 +69,6 @@ timestamp_t dateTimeToTimestamp(const QDateTime &t);
int parseDurationToSeconds(const QString &text);
int parseLengthToMm(const QString &text);
int parseTemperatureToMkelvin(const QString &text);
int parseWeightToGrams(const QString &text);
int parsePressureToMbar(const QString &text);
int parseGasMixO2(const QString &text);
int parseGasMixHE(const QString &text);

View file

@ -12,7 +12,7 @@ struct sample // BASE TYPE BYTES UNITS RANGE
{ // --------- ----- ----- ----- -----------
duration_t time; // int32_t 4 seconds (0-34 yrs) elapsed dive time up to this sample
duration_t stoptime; // int32_t 4 seconds (0-34 yrs) time duration of next deco stop
duration_t ndl = { -1 }; // int32_t 4 seconds (-1 no val, 0-34 yrs) time duration before no-deco limit
duration_t ndl = { .seconds = -1 };// int32_t 4 seconds (-1 no val, 0-34 yrs) time duration before no-deco limit
duration_t tts; // int32_t 4 seconds (0-34 yrs) time duration to reach the surface
duration_t rbt; // int32_t 4 seconds (0-34 yrs) remaining bottom time
depth_t depth; // int32_t 4 mm (0-2000 km) dive depth of this sample
@ -21,7 +21,7 @@ struct sample // BASE TYPE BYTES UNITS RANGE
pressure_t pressure[MAX_SENSORS]; // int32_t 2x4 mbar (0-2 Mbar) cylinder pressures (main and CCR o2)
o2pressure_t setpoint; // uint16_t 2 mbar (0-65 bar) O2 partial pressure (will be setpoint)
o2pressure_t o2sensor[MAX_O2_SENSORS];// uint16_t 6x2 mbar (0-65 bar) Up to 6 PO2 sensor values (rebreather)
bearing_t bearing = { -1 }; // int16_t 2 degrees (-1 no val, 0-360 deg) compass bearing
bearing_t bearing = { .degrees = -1 };// int16_t 2 degrees (-1 no val, 0-360 deg) compass bearing
int16_t sensor[MAX_SENSORS] = {}; // int16_t 2x2 sensorID (0-16k) ID of cylinder pressure sensor
uint16_t cns = 0; // uint16_t 2 % (0-64k %) cns% accumulated
uint8_t heartbeat = 0; // uint8_t 1 beats/m (0-255) heart rate measurement

View file

@ -275,9 +275,9 @@ static std::pair<volume_t, volume_t> get_gas_parts(struct gasmix mix, volume_t v
if (gasmix_is_air(mix))
return { volume_t() , volume_t() };
volume_t air = { (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) };
volume_t he = { (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) };
volume_t o2 = { vol.mliter - he.mliter - air.mliter };
volume_t air { .mliter = (int)lrint(((double)vol.mliter * get_n2(mix)) / (1000 - o2_in_topup)) };
volume_t he { .mliter = (int)lrint(((double)vol.mliter * get_he(mix)) / 1000.0) };
volume_t o2 { .mliter = vol.mliter - he.mliter - air.mliter };
return std::make_pair(o2, he);
}

View file

@ -67,62 +67,66 @@
*/
using timestamp_t = int64_t;
struct duration_t
template <typename T>
struct unit_base {
};
struct duration_t : public unit_base<duration_t>
{
int32_t seconds = 0; // durations up to 34 yrs
};
struct offset_t
struct offset_t : public unit_base<offset_t>
{
int32_t seconds = 0; // offsets up to +/- 34 yrs
};
struct depth_t // depth to 2000 km
struct depth_t : public unit_base<depth_t> // depth to 2000 km
{
int32_t mm = 0;
};
struct pressure_t
struct pressure_t : public unit_base<pressure_t>
{
int32_t mbar = 0; // pressure up to 2000 bar
};
struct o2pressure_t
struct o2pressure_t : public unit_base<o2pressure_t>
{
uint16_t mbar = 0;
};
struct bearing_t
struct bearing_t : public unit_base<bearing_t>
{
int16_t degrees = 0;
};
struct temperature_t
struct temperature_t : public unit_base<temperature_t>
{
uint32_t mkelvin = 0; // up to 4 MK (temperatures in K are always positive)
};
struct temperature_sum_t
struct temperature_sum_t : public unit_base<temperature_sum_t>
{
uint64_t mkelvin = 0; // up to 18446744073 MK (temperatures in K are always positive)
};
struct volume_t
struct volume_t : public unit_base<volume_t>
{
int mliter = 0;
};
struct fraction_t
struct fraction_t : public unit_base<fraction_t>
{
int permille = 0;
};
struct weight_t
struct weight_t : public unit_base<weight_t>
{
int grams = 0;
};
struct degrees_t
struct degrees_t : public unit_base<degrees_t>
{
int udeg = 0;
};
@ -152,8 +156,8 @@ static inline bool operator!=(const location_t &a, const location_t &b)
static inline location_t create_location(double lat, double lon)
{
location_t location = {
{ (int) lrint(lat * 1000000) },
{ (int) lrint(lon * 1000000) }
{ .udeg = (int) lrint(lat * 1000000) },
{ .udeg = (int) lrint(lon * 1000000) }
};
return location;
}
@ -255,7 +259,7 @@ static inline double mbar_to_atm(int mbar)
static inline double mbar_to_PSI(int mbar)
{
pressure_t p = { mbar };
pressure_t p = { .mbar = mbar };
return to_PSI(p);
}