Как сохранить повышение :: количество с возможным различным увеличением :: измерение

я использую boost::units библиотека для обеспечения физической согласованности в научном проекте. Я прочитал и попробовал несколько примеров из документации Boost. Я могу создавать свои размеры, единицы и количества. Я сделал несколько исчислений, это работает очень хорошо. Это именно то, что я ожидал, кроме этого …

В моем проекте я имею дело с временными рядами, которые имеют несколько различных единиц измерения (температура, концентрация, плотность и т. Д.) На основе шести измерений. Чтобы обеспечить безопасное и простое преобразование единиц измерения, я хотел бы добавить в каждый класс канала члена, представляющего измерения и единицы временного ряда. Кроме того, обработка данных (импорт, преобразование и т. Д.) Осуществляется пользователем, поэтому является динамичной.

Моя проблема заключается в следующем, из-за boost::units Структура, величины внутри однородной системы, но с разными размерами, имеют разные типы. Поэтому вы не можете напрямую объявить члена, такого как:

boost::units::quantity channelUnits;

Компилятор будет утверждать, что вы должны указать размеры, используя шаблонные шевроны. Но если вы сделаете это, вы не сможете хранить разные типы количеств (например, количества с разными измерениями).

Затем я искал boost::units::quantity объявление, чтобы узнать, есть ли базовый класс, который я могу использовать полиморфным способом. Но я не нашел его, а обнаружил, что boost::units интенсивно использует Шаблон мета-программирования что не является проблемой, но не совсем соответствует моим динамическим потребностям, поскольку все решается во время компиляции, а не во время выполнения.

После прочтения я попытался обернуть различные количества в boost::variant объект (приятно встретить его в первый раз).

typedef boost::variant<
boost::units::quantity<dim1>,
...
> channelUnitsType;
channelUnitsType channelUnits;

Я провел несколько тестов, и это похоже на работу. Но я не уверен в boost::variant и Посетитель-модель.

Мои вопросы следующие:

  • Есть ли другой, может быть, лучший способ иметь разрешение типов во время выполнения?
  • Является dynamic_cast один из них? Преобразование единиц происходит не очень часто, и только небольшое количество данных вызывает озабоченность.
  • Если boost::variant такое подходящее решение, в чем его недостатки?

2

Решение

Я думал об этой проблеме и пришел к следующему выводу:

1. Реализация стирания типа (плюсы: приятные интерфейсы, минусы: накладные расходы памяти)

Кажется невозможным хранить без накладных расходов общее количество с общим измерением, которое нарушает один из принципов проектирования библиотек. Даже стирание типа здесь не поможет.

2. Реализовать конвертируемый тип (плюсы: приятные интерфейсы, минусы: эксплуатационные расходы)

Единственный способ, который я вижу без затрат на хранение, — это выбрать обычную (возможно, скрытую) систему, в которой все единицы преобразуются в и из. Нет затрат на память, но есть затраты на умножение почти во всех запросах к значениям, огромное количество преобразований и некоторая потеря точности с высоким показателем (подумайте о преобразовании из числа Авогадро в число 10).

3. Разрешить неявные преобразования (плюсы: приятные интерфейсы, минусы: сложнее в отладке, неожиданные эксплуатационные издержки)

Другой вариант, главным образом с практической точки зрения, состоит в том, чтобы разрешить неявное преобразование на уровне интерфейса, см. Здесь: https://groups.google.com/d/msg/boost-devel-archive/JvA5W9OETt8/5fMwXWuCdDsJ

4. Шаблон / общий код (плюсы: нет времени выполнения или нехватка памяти, концептуально верны, философия соответствует философии библиотеки, минусы: сложнее в отладке, уродливые интерфейсы, возможное раздувание кода, множество параметров шаблона везде)

Если вы спросите разработчика библиотеки, возможно, он скажет вам, что вам нужно сделать свои функции общими. Это возможно, но это усложняет код. Например:

template<class Length>
auto square(Length l) -> decltype(l*l){return l*l;}

Я использую C ++ 11 для упрощения примера здесь (это можно сделать в C++98), а также чтобы показать, что это становится легче делать в C ++ 11 (и еще проще в C ++ 14 с decltype(auto),

Я знаю, что это не тот тип кода, который вы имели в виду, но он соответствует дизайну библиотеки. Вы можете подумать, ну как мне ограничить эту функцию физической длиной, а не чем-то другим? Ну, ответ в том, что вам это не нужно, однако, если вы настаиваете, в худшем случае …

template<class Length, typename std::enable_if<std::is_same<dimension_of<Lenght>::type, boost::units::length_dimension>::value>::type>
auto square(Length l) -> decltype(l*l){return l*l;}

(В лучшем случае decltype будет делать работу SFINAE.)

На мой взгляд, вариант 4. и, возможно, в сочетании с 3. — самый элегантный путь вперед.

2

Другие решения

Пройдя глубже в моей проблеме, я прочитал две статьи, предлагающие пути решения:

  • Костадин Дамевский, Выражение единиц измерения в интерфейсах для научных компонентов программного обеспечения;
  • Линсяо Цзян, Практическая система типов для проверки корректности размерных единиц C-программ.

Первый дает хорошие идеи для реализации интерфейса. Второй дает полный обзор того, что вы должны справиться.

Я имею в виду, что boost::units это полный и эффективный способ обеспечения согласованности измерений во время компиляции без накладных расходов во время выполнения. В любом случае, для согласованности измерений во время выполнения с изменениями размеров вам нужна динамическая структура, которая boost::units не обеспечивает. И вот я здесь: создаю класс юнитов, который будет в точности соответствовать моим потребностям. Больше работы для достижения, больше удовлетворения в конце …

О оригинальных вопросах:

  • boost::variant работает хорошо (обеспечивает динамический boost::units отсутствует) для этой работы. И кроме того, он может быть сериализован из коробки. Таким образом, это эффективный подход. Но это добавляет уровень абстракции для простой — я не говорю, тривиальной — задачи, которая может быть выполнена одним классом.
  • Кастинг достигается boost::variant_cast<> вместо dynamic_cast<>,
  • boost::any может быть проще реализовать, но сериализация становится трудным путем.
3