我想到了两种可能性。一个是使用 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));
另一种可能的方法是将数据放入结构或混合模板中,然后通过将其放入具有所需功能的另一个结构中来进行数学运算:
// 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
}
如果您将变量“a”传递给它,它会抱怨,因为它是 myvectormath.vector,这是 myvectormath2。
test32.d(27): 错误: 函数 test32.somethingWithMath2 (vector a) is not callable using argument types (vector)
但是,我们可以很容易地转换它们,这要归功于 mixin 模板中的外部数据结构、ctor 和别名:
somethingWithMath2(myvectormath2.vector(a));
编译!在幕后工作的方式是 myvectormath2.vector 有两个构造函数:(float, float) 和 (VectorData)。它们都不匹配 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,这将是类型不匹配。但是,如果您确实想允许这样做,您所要做的就是将重载的运算符更改为接受 VectorData 而不是其中一种数学类型!然后它将隐式转换,并且您可以处理相同的数据。将 VectorData 视为 OOP 术语中的基类。
我认为这涵盖了基础,如果您还有其他问题,请告诉我。