Обеспечение семантического контроля над размерностями станда
От: CrystaX Россия https://crystax.me/
Дата: 20.11.05 21:58
Оценка: 43 (8)
Собственно, это логическое продолжение кода из ветки В поисках семантической корректности
Автор: Нахлобуч
Дата: 18.05.05
, а именно Re[15]: В поисках семантической корректности
Автор: CrystaX
Дата: 23.05.05
. Наконец у меня дошли руки до отделения зерен от плевел и вот результат.

Сам код будет в следующем сообщении, это всего один файл. Но вначале его возможности и пример использования. В примере использованы для демонстрации физические величины, выраженные в системах единиц СИ и СГС.

Возможности:
1. Независимые типы данных, такие как mass и length. Присвоить один другому нельзя — будет ошибка компиляции.
2. Автоматическое конвертирование величин одной и той же размерности из одной системы единиц в другую. К примеру, присвоив килограммам один грамм, в результирующей переменной (si::mass) обнаружим 0.001. Вычисление коэффициентов преобразования производится в compile time.
3. Контроль за корректностью формул со стороны компилятора. Так, если ускорению попытаться присвоить результат деления длины на время, возникнет ошибка компиляции. С моей точки зрения, это наиболее важное свойство моего кода. Особенно это заметно на трехэтажных формулах, где очень легко потерять из виду, какая же в результате получается размерность. В случае использования моего кода подобного рода ошибки полностью исключаются.

Пример:
#include <iostream>
#include <units.hpp>

using namespace units;

int main()
{
    cgs::mass m1(1); // 1 грамм
    si::mass m2(m1); // Автоматическое преобразование в килограммы. В результате получаем 0.001 кг

    std::cout << "Mass in SI: " << m2 << ", in CGS: " << m1 << std::endl;

    cgs::length l1(100); // 100 см
    si::length l2(l1);   // Автоматическое преобразование. Результат - 1 м

    std::cout << "Length in SI: " << l2 << ", in CGS: " << l1 << std::endl;

    cgs::time t1(1); // 1 с
    si::time t2(t1); // Тоже 1 с, т.к. единицы измерения времени в системах СИ и СГС совпадают.

    std::cout << "Time in SI: " << t2 << ", in CGS: " << t1 << std::endl;

    // Теперь немного более сложный пример. Предыдущие величины (масса, длина, время) являются
    // базовыми (ортами) в обеих системах единиц. Здесь же мы имеем дело с производными величинами -
    // скоростью, енергией и мощностью
    cgs::velocity v1(10); // 10 см/с
    si::velocity v2(v1);  // Результат преобразования - 0.1 м/с

    std::cout << "Velocity in SI: " << v2 << ", in CGS: " << v1 << std::endl;

    si::energy e1(0.0001); // 10e-4 Дж.
    cgs::energy e2(e1);    // e2 = 1000 эрг

    std::cout << "Energy in SI: " << e1 << ", in CGS: " << e2 << std::endl;

    // Это - пример работы с внесистемными единицами. В данном случае мощность переводится из ваттов
    // в лошадиные силы
    si::power p1(1000); // 1000 Вт
    ext::power p2(p1);  // 1.35962 лс

    std::cout << "Power in SI: " << p1 << ", in Ext system: " << p2 << std::endl;

    // Пример работы с простейшими арифметическими действиями
    si::acceleration a1(1); // 1 м/с2
    cgs::acceleration a2;
    a2 = a1 + cgs::acceleration(200); // 1 м/с2 + 200 см/с2 = 300 см/с2, как и должно было быть

    std::cout << "Acceleration: a1 = " << a1 << ", a2 = " << a2 << std::endl;

    // Сила
    si::force f1(10);      // 10 Н
    cgs::force f2 = f1*2;  // 10 Н * 2 = 2000000 дин

    si::force f3 = m1*l2/(t1*t1); // 1 г * 1 м / (1 с * 1 с) = 0.001 Н
    // А вот так уже не скомпилируется - не та размерность
    //si::force f4 = m1*l2/t1;

    // Вот что выдает MS VC 7.1: error C2440: 'initializing' : cannot convert from 
    //     'units::unit<T,System,Tag>' to 'units::unit<T,System,Tag>'
    // А вот что GCC 3.4.2 (mingw): 
    // test.cpp: In function `int main()':
    // test.cpp:59: error: conversion from `units::unit<long double, units::helpers::systems::CGS, 
    //         boost::mpl::vector<mpl_::int_<1>, mpl_::int_<1>, mpl_::int_<-0x000000001>, mpl_::int_<0>, 
    //         mpl_::int_<0>, mpl_::int_<0>, mpl_::int_<0>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, 
    //         mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na> >' 
    // to non-scalar type `units::unit<long double, units::helpers::systems::SI, units::helpers::force_tag>'
    // requested


    std::cout << "Force: f1 = " << f1 << ", f2 = " << f2 << ", f3 = " << f3 << std::endl;

    // Здесь ошибка компиляции - пытаемся складывать енергию, массу и скорость
    //std::cout << "ERROR = " << (e1 + e2 + v1 + v2 + m1 + m2) << std::endl;

    // Здесь происходит преобразование на лету. В результате имеем 0.0002 Дж - результат имеет тот же тип, 
    // что и левый операнд
    std::cout << "Result = " << e1 + e2 << std::endl;

    // Здесь выводится единица, т.к. e1 и e2 содержат одно и то же значение, но выраженное в разных 
    // системах единиц
    std::cout << "Result = " << e1/e2 << std::endl;
    
    return 0;
}


Исходники дальше.
... << RSDN@Home 1.1.4 stable rev. 510>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.