我在游戏项目中使用了两个数学库。一个是GLM库,另一个是Box2D的数学部分。偶尔需要在两者之间进行转换,如下所示:
b2Vec2 vec1(1.0f, 1.0f);
glm::vec2 vec2(vec1.x, vec1.y);
我想知道是否有更好的方法可以更加无缝地进行,而无需编辑任何一个库?
答案 0 :(得分:3)
我认为如果不修改库就不能隐式地进行转换。
但是,为了简化转换代码,tou可以实现简单的转换函数,例如:
inline glm::vec2 make_glmVec2(const b2Vec2 &v) {
return glm::vec2(v.x, v.y);
}
inline glm::vec3 make_glmVec3(const b2Vec3 &v) {
return glm::vec3(v.x, v.y, v.z);
}
如果这两个库的类型之间存在(几乎)直接对应关系,您甚至可以为所有转换函数使用更简单的名称,例如toGlm
,并为所需的所有类型简单重载:
inline glm::vec2 toGlm(const b2Vec2 &v) {
return glm::vec2(v.x, v.y);
}
inline glm::vec3 toGlm(const b2Vec3 &v) {
return glm::vec3(v.x, v.y, v.z);
}
修改强>
我尝试实现一个“代理”类,它可以作为来自两个库的两个类之间的桥梁。代理类包含构造函数和强制转换运算符,允许您在这些类之间创建。不幸的是,您需要显式调用构造函数,否则编译器甚至不会考虑使用此类:
//Library 1:
class Vec1 {
public:
int x;
int y;
Vec1(int _x, int _y) : x(_x), y(_y) {}
};
//Library 2:
class Vec2 {
public:
int e1;
int e2;
Vec2(int _x, int _y) : e1(_x), e2(_y) {}
};
//Your code
class VecProxy {
public:
int pxX;
int pxY;
VecProxy(const Vec1& v1) : pxX(v1.x), pxY(v1.y) {}
VecProxy(const Vec2& v2) : pxX(v2.e1), pxY(v2.e2) {}
operator Vec1() {return Vec1(pxX, pxY); }
operator Vec2() {return Vec2(pxX, pxY); }
};
int main() {
Vec1 v1(2,3);
Vec2 v2=VecProxy(v1);
Vec1 v3=VecProxy(v2);
}
请注意,您可以使用相同的名称,无论您在哪个方向投射,这可能比我之前的建议稍微好一些。我不认为你可以隐式调用构造函数。
答案 1 :(得分:2)
理论上,您可以将隐式转换运算符添加到一个或两个库中(尽管隐式转换不一定是个好主意)。但是你已经说过你不能编辑任何一个库,所以规则就是这样。
因此,一种替代方法是引入您自己的矢量类,并为其提供必要的转换运算符。然后,只要您需要代码中的向量,就始终将其存储为自定义类的对象,并在需要使用库时(隐式)进行转换。
但同样,隐式转换可能导致的麻烦可能超过表面上的好处(有关详细信息,请参阅More Effective C++的第5项)。
答案 2 :(得分:1)
我知道这有点旧,但我最近遇到了与当前项目相同的情况。我想分享我的解决方案,这对我很有用。这些只是我最常使用的类型(我在我的项目中键入了更简单(读取:lazier)类型的类型)。这可能会更多地扩展/调整,但希望这将为使用这两个库的任何人提供另一种选择。
typedef b2Vec2 b2vec2;
typedef b2Mat22 b2mat2;
typedef glm::vec2 vec2;
typedef glm::mat2 mat2;
//Conversion types
namespace Convert
{
///////////////////////////////////////
//Use case examples
//
// Turn the glm vector2 into a box2d type
// vec2 a = vec2( 1, 2 );
// b2vec2 b = Convert::v2(a).to_box2d;
//
// Access the box2d b2vec2 like a glm type
// b2vec2 c = b2vec2( 1, 2 );
// Convert::v2(c).to_glm = vec2(3,4);
//
// Turn the const glm vector2 into a box2d type
// const vec2 d = vec2( 1,2 );
// b2vec2 e = Convert::c_v2(d).to_box2d;
//Don't use these directly. See the use case examples
union _Vec2 {
_Vec2( vec2* _v ):glm(_v){}
_Vec2( b2vec2* _v ):box2d(_v){}
vec2* glm;
b2vec2* box2d;
};
union _CVec2 {
_CVec2( const vec2* _v ):glm(_v){}
_CVec2( const b2vec2* _v ):box2d(_v){}
const vec2* glm;
const b2vec2* box2d;
};
union _Mat2 {
_Mat2( mat2* _m ):glm(_m){}
_Mat2( b2mat2* _m ):box2d(_m){}
mat2* glm;
b2mat2* box2d;
};
//Convert between vec2 and b2vec2
struct v2 {
private:
_Vec2 data;
public:
vec2& to_glm;
b2vec2& to_box2d;
v2( vec2& _v )
:data( &_v )
,to_glm( *data.glm )
,to_box2d( *data.box2d ){}
v2( b2vec2& _v )
:data( &_v )
,to_glm( *data.glm )
,to_box2d( *data.box2d ){}
};
//Convert between const vec2 and const b2vec2
struct c_v2 {
private:
_CVec2 data;
public:
const vec2& to_glm;
const b2vec2& to_box2d;
c_v2( const vec2& _v )
:data( &_v )
,to_glm( *data.glm )
,to_box2d( *data.box2d ){}
c_v2( const b2vec2& _v )
:data( &_v )
,to_glm( *data.glm )
,to_box2d( *data.box2d ){}
};
//Convert between mat2 and b2mat22
struct m2 {
private:
_Mat2 data;
public:
mat2& to_glm;
b2mat2& to_box2d;
m2( mat2& _m )
:data( &_m )
,to_glm( *data.glm )
,to_box2d( *data.box2d ){}
m2( b2mat2& _m )
:data( &_m )
,to_glm( *data.glm )
,to_box2d( *data.box2d ){}
};
}