web-dev-qa-db-ja.com

C ++でのタイプセーフな物理演算

物理ユニットを別個のタイプとして定義し、それらのタイプ間の有効な操作を定義することは、C++で意味をなすでしょうか?

単純な浮動小数点値を使用してそれらを表すのではなく、多くの型と多くの演算子のオーバーロードを導入することには利点がありますか?

例:

class Time{...};
class Length{...};
class Speed{...};
...
Time operator""_s(long double val){...}
Length operator""_m(long double val){...}
...
Speed operator/(const Length&, const Time&){...}

TimeLengthおよびSpeedは、異なる演算子からの戻り型としてのみ作成できますか?

58
Mircea Ispas

物理ユニットを別個のタイプとして定義し、それらのタイプ間の有効な操作を定義することは、C++で意味をなすでしょうか?

もちろんです。標準のChronoライブラリは、これを時点と期間について既に行っています。

単純な浮動小数点値を使用してそれらを表すのではなく、多くの型と多くの演算子のオーバーロードを導入することには利点がありますか?

はい:タイプシステムを使用して、ランタイムオーバーヘッドを追加することなく、コンパイル時に距離に質量を追加するなどのエラーをキャッチできます。

自分で型と演算子を定義したくない場合は、Boostに nitsライブラリ があります。

53
Mike Seymour

私は本当にこれをお勧めします boost :: units すべての変換コンパイル時間を実行し、誤った次元の疑似コード例を使用しようとすると、コンパイル時間エラーも発生します。

length l1, l2, l3;
area a1 = l1 * l2; // Compiles
area a2 = l1 * l2 * l3; // Compile time error, an area can't be the product of three lengths.
volume v1 = l1 * l2 * l3; // Compiles
21
Viktor Sehr

私はこの道を行きました。利点はすべて、タイプセーフの通常の多数の優れた利点です。私が遭遇した欠点:

  • 秒の2乗など、計算の中間値を節約する必要があります。これらの値をtypeにすることは多少意味がありません(seconds ^ 2は明らかにvelocity isのようなタイプではありません)。
  • ますます複雑になる計算を実行する必要があり、そのためには、より多くのオーバーロード/演算子定義が必要になります。

結局のところ、単純な計算と単純な目的にとっては非常にクリーンです。しかし、数学が複雑になると、型指定された単位系でニースを再生するのが難しくなります。

13
David

誰もがタイプセーフの保証をプラスとして述べてきました。もう1つの大きなプラスは、単位(メートル)から概念(長さ)を抽象化する機能です。

したがって、たとえば、単位を処理するときの一般的な問題は、SIとメトリックを混合することです。概念がクラスとして抽象化されている場合、これはもはや問題ではありません。

Length width = Length::fromMeters(2.0);
Length height = Length::fromFeet(6.5);
Area area = width * height; //Area is computed correctly!
cout << "The total area is " << area.toInches() << " inches squared.";

クラスのユーザーは、内部表現がどの単位を使用するかを知る必要はありません...少なくとも、深刻な丸めの問題がない限り。


角度を使って三角関数ライブラリーがこれを実行することを本当に望みます。なぜなら、それらが度またはラジアンを期待しているかどうかを常に調べなければならないからです...

強力なコンパイル時のタイプセーフなユニットライブラリを探しているが、Boost依存関係のドラッグに躊躇している人は、 nits をチェックしてください。ライブラリは、依存関係のない単一の.hファイルとして実装され、単体テスト/ドキュメントを構築するためのプロジェクトが付属しています。 msvc2013、2015、gcc-4.9.2でテストされており、これらのコンパイラーの新しいバージョンでも動作するはずです。

完全開示:私はライブラリの作成者です

4

Boost.UnitsライブラリのCPPcon 2015でチュートリアルプレゼンテーションを行いました。これは、すべての科学アプリケーションが使用する必要がある強力なライブラリです。ただし、ドキュメントが不十分なため、使用するのは困難です。うまくいけば、私のチュートリアルがこれに役立ちます。あなたはslides/codeを見つけることができます here:

1
Robert Ramey

私はあなたがそうするのが間違っていると言っているわけではありませんが、私が取り組んでいるプロジェクトでそれをやり過ぎました。特にチームに所属している場合は、適切な変数の名前付け(とんでもないことを説明する)、コードレビュー、およびユニットテストを行うことで、問題を回避できます。一方、Boostを使用できる場合、ユニットはチェックインする必要があるかもしれません(私はしていません)。

1
Scott

タイプセーフをチェックするには、専用ライブラリを使用できます。

最も賢く使用するのはboost :: unitsであり、実行時間のオーバーヘッドがなく、多くの機能を備えています。このライブラリが理論的に問題を解決する場合。より実用的な観点から見ると、インターフェースは非常に扱いにくく、ドキュメントが不適切であるため、問題が発生する可能性があります。次元数が増えるとコンパイル時間が大幅に増えるため、大規模なプロジェクトを使用する前に、妥当な時間でコンパイルできることを明確に確認してください。

doc: http://www.boost.org/doc/libs/1_56_0/doc/html/boost_units.html

別の方法は、unit_liteを使用することです。 boostライブラリよりも機能は少ないですが、コンパイルはより速く、インターフェースはより単純で、エラーメッセージは読み取り可能です。このlibにはC++ 11が必要です。

コード: https://github.com/pierreblavy2/unit_lite

ドキュメントへのリンクはgithubの説明にあります(ここに3つ以上のリンクを投稿することはできません!!!)。

1
zorbaglub

はい、それは理にかなっています。物理学だけでなく、あらゆる分野で。金融では、金利は、逆の時間間隔(通常は1年ごとに表現)の単位です。お金にはさまざまな単位があります。それらの間の変換はクロスレートでのみ行うことができ、ある通貨を別の通貨で割った次元があります。利息の支払い、配当の支払い、元金の支払いなどは、通常、頻繁に発生します。

2つの値が乗算されて不正な値になることを防ぐことができます。ドルやユーロの合計などを防ぐことができます。

1
Fred Mitchell