2 つの可能性が思い浮かびます。1 つは UFCS を使用して、最初のパラメーターとして型を取り、ドット構文で呼び出すことができる他のモジュールで名前付き関数 (演算子のオーバーロードでは機能しません) を定義します (ここで数学を台無しにしたら許してください)。
module myvector;
struct vector {
float x;
float y;
}
module myvectormath;
import myvector;
vector add(vector lhs, vector rhs) {
// inside, it is just a regular function
vector result;
result.x = lhs.x + rhs.x;
result.y = lhs.y + rhs.y;
return result;
}
利用方法:
import myvector;
import myvectormath;
// but it can be called with dot notation
vector a = vector(0,0).add(vector(5, 5));
別の可能な方法は、データを構造体または mixin テンプレートに配置し、必要な関数を使用して別の構造体に配置して計算を行うことです。
// data definition
module myvector;
// the data will be an external named type, so we can pass it on more easily - will help interop
struct VectorData {
float x;
float y;
}
// and this provides the stuff to get our other types started
mixin template vector_payload() {
// constructors for easy initialization
this(float x, float y) {
_data.x = x;
_data.y = y;
}
this(VectorData d) {
_data = d;
}
// storing our data
VectorData _data;
// alias this is a feature that provides a bit of controlled implicit casting..
alias _data this;
}
// math module #1
module myvectormath;
import myvector;
struct vector {
// mixin all the stuff from above, so we get those ctors, the data, etc.
mixin vector_payload!();
// and add our methods, including full operator overloading
vector opBinary(string op:"+")(vector rhs) {
vector result;
result.x = this.x + rhs.x;
result.y = this.y + rhs.y;
return result;
}
}
// math module #2
module myvectormath2;
import myvector;
struct vector {
// again, mix it in
mixin vector_payload!();
// and add our methods
vector opBinary(string op:"+")(vector rhs) {
vector result;
// this one has horribly broken math lol
result.x = this.x - rhs.x;
result.y = this.y - rhs.y;
return result;
}
}
// usage
import myvectormath;
// OR
//import myvectormath2;
void main() {
vector a = vector(0, 0) + vector(5, 5);
import std.stdio;
writeln(a);
}
使用法モジュールでは、インポートを置き換えるだけで、残りのコードは変更されません。ただし、両方のモジュールを同時に使用して混合したい場合はどうなりますか? ここで、内部構造体 _Data、それを受け取るコンストラクター、およびこの魔法のエイリアスが登場します。まず、両方をインポートして、何が起こるかを確認します。
test32.d(23): エラー: test324.d(4) の myvectormath.vector が test322.d(4) の myvectormath2.vector と競合します
そのため、まず、名前のあいまいさを解消したいと考えています。これを行うにはさまざまな方法があります。詳細については、D ドキュメントのインポート セクションを参照してください: http://dlang.org/module.html#Import
ここでは、完全修飾名のみを使用します。
// usage
import myvectormath;
import myvectormath2;
void main() {
// specify the kind we want to use here...
myvectormath.vector a = myvectormath.vector(0, 0) + myvectormath.vector(5, 5);
import std.stdio;
writeln(a); // and we get a result of 0, 5, so it used the addition version correctly
}
それらを内部で簡単に移動するにはどうすればよいでしょうか? バージョン #2 を使用する関数を作成しましょう。
void somethingWithMath2(myvectormath2.vector vec) {
// whatever
}
これは myvectormath.vector で、これは myvectormath2 であるため、変数 "a" を渡すと問題が発生します。
test32.d(27): エラー: 関数 test32.somethingWithMath2 (ベクトル a) は、引数の型 (ベクトル) を使用して呼び出すことはできません
しかし、外部データ構造体、ctor、および mixin テンプレートのエイリアス this のおかげで、それらを非常に簡単に変換できます。
somethingWithMath2(myvectormath2.vector(a));
コンパイル!内部で動作する方法は、myvectormath2.vector に (float、float) と (VectorData) の 2 つのコンストラクターがあることです。どちらも a の型と一致しないため、次に a のエイリアスである this... VectorData を試します。そのため、VectorData ctor を暗黙的に変換してから一致させます。
データを渡すこともできます:
import myvector;
void somethingWithMath2(VectorData a_in) {
// to do math on it, we construct the kind of vectormath we're interested in:
auto a = myvectormath2.vector(a_in);
// and use it
}
そして、次のように呼び出します。
// will implicitly convert any of the sub vectormath types to the base data so this just works
somethingWithMath2(a);
データを渡すのはおそらく最も良い方法です。なぜなら、呼び出し元は、あなたがデータを使って何をしようとしているのかを知る必要がないからです。
ちなみに、ここで使用するコンストラクターは些細なものであり、実行時に重大な損失が発生することはありません (コンパイラー スイッチがインライン化するように設定されている場合、おそらくまったくない可能性があります。これは基本的に単なる reinterpret_cast であり、データ表現は同一です)。
myvectormath2.vector + myvectormath.vector を追加できないことに注意してください。これはタイプの不一致になります。ただし、それを許可したい場合は、オーバーロードされた演算子を変更して、数学タイプの 1 つではなく VectorData を受け入れるようにするだけです! 次に、暗黙的に変換され、作業する同じデータが得られます。VectorData は、OOP 用語の基本クラスであると考えてください。
これで基本はカバーされていると思います。さらに質問がある場合はお知らせください。