ゲームプロジェクトで2つの数学ライブラリを使用しています。1つはGLMライブラリで、もう1つはBox2Dの数学部分です。次のように、2つの間の変換が必要になる場合があります。
b2Vec2 vec1(1.0f, 1.0f);
glm::vec2 vec2(vec1.x, vec1.y);
どちらのライブラリも編集せずに、よりシームレスにそれを行うためのより良いアプローチがあるかどうか疑問に思っていますか?
ライブラリを変更せずに暗黙的に変換を行うことはできないと思います。
ただし、変換コードを単純化するために、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);
}
これら2つのライブラリのタイプ間に(ほぼ)直接の対応がある場合は、すべての変換関数に単純な名前を使用することもできます。たとえば、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);
}
編集
2つのライブラリからの2つのクラス間のブリッジとして機能する「プロキシ」クラスを実装してみました。プロキシクラスには、これらのクラスとの間で作成できるコンストラクタとキャスト演算子が含まれています。残念ながら、コンストラクターを明示的に呼び出す必要があります。そうしないと、コンパイラーはこのクラスの使用を検討しません。
//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つの代替方法は、独自のベクトルクラスを導入し、それに必要な変換演算子を提供することです。次に、コードにベクトルが必要な場合は常に、それをカスタムクラスのオブジェクトとして格納し、ライブラリを使用する必要があるときに(暗黙的に)変換します。
しかし、繰り返しになりますが、暗黙の変換が引き起こす可能性のある問題は、表面的な利点を上回る可能性があります(これについての詳細は、より効果的なC ++の項目5を参照してください)。
これは少し古いことは知っていますが、最近、現在のプロジェクトで同じ状況に遭遇しました。私は自分のソリューションを共有したかったのですが、それは私にとってうまく機能します。これらは私が最も頻繁に使用するタイプです(プロジェクトでの入力を簡単にする(読む:怠惰な)ためにタイプをtypedefしました)。これはおそらく拡張/調整される可能性がありますが、うまくいけば、これにより、これら2つのライブラリを一緒に使用するすべての人に別のオプションが提供されます。
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 ){}
};
}