Optional structural typing possibility in C++ or any other language?
In C++ how to tell compiler that Ogre::Vector3 IS_SAME_AS SomeOtherLIB::Vector3 ? I feel that.. in languages like c++ which are not structural typed but th开发者_Go百科ere are cases when it makes sense.
Normally as game developer when working with 4+ libraries that provide sort or their own Vector3 implementation. The code is littered with ToOgre, ToThis, ToThat conversion function. Thats a lot of Float3 copying around which should not happen on first place.
Is in C++ or any other languages where we dont have to convert (copying) from one type to another which is essentially the samething. But any solution in C++ as most of the good gamedevs libs are for c/c++.
If you use templates you can define functions that take any type of argument as long as the necessary operations are defined on that type. Example:
class Foo { void quack() {} };
class Bar { void quack() {} };
class Baz {};
template<typename Duck>
void f(Duck d) {
d.quack();
}
int main() {
f(Foo()); // works
f(Bar()); // works
f(Baz()); // compile error because Baz does not have a quack method
return 0;
}
While it doesn't suit any situation, templates can give you "compile-time duck typing".
Lets say you have two vector types:
struct Vec3A {
float x, y, z;
};
struct Vec3B {
float p[3];
};
You can define function templates that hide the implementation how of to get the components:
template<class T> float get_x(const T&);
template<class T> float get_y(const T&);
template<class T> float get_z(const T&);
template<> float get_x<Vec3A>(const Vec3A& v) { return v.x; }
// ...
template<> float get_x<Vec3B>(const Vec3B& v) { return v.p[0]; }
// ...
With such helpers you can now write generic functions that work on both:
template<class T> float length(const T& t) {
return std::sqrt(std::pow(get_x(t), 2),
std::pow(get_y(t), 2),
std::pow(get_z(t), 2));
}
You can also continue by specializing utilities like length()
for performance or other reasons, e.g. if a certain vector already has a member function providing you with the length:
template<> float length<Vec3C>(const Vec3C& v) {
return v.length();
}
If you are really sure in case of non-virtual structures, you can do a reinterpret_cast. However, it's better to:
- do templated wrapper functions as shown by sepp2k
- inherit from one of the vectors and add a conversion operator to the other vector
- add a separate _cast function that does the conversion
Haxe is a highly portable language with completely optional structural subtyping:
typedef Vector3 = { x : double, y : double, z : double };
class FancyVector3 {
public var x : double, y : double, z : double;
function dot(Vector3 v) {
return x * v.x + y * v.y + z * v.z;
}
function length() {
return Math.sqrt(dot(this));
}
}
Not only is Vector3 an already usable structure, it also acts as a structural interface for other classes. Such typedef
'd structs can specify function signatures as well as fields.
Haxe also has a CFFI for talking to C++ (although it still requires conversion methods), and bindings already exist for a few C++ game engines, as well as a variety of lower-level frameworks. Cross-platform engines written in pure Haxe are also being developed, targeting variously C++, Flash, and JS (both Canvas and WebGL).
This is probably not the solution you are looking for right now, but may become more interesting within a few years.
精彩评论