4

I'm using GLM and Bullet Physics and they each have their own type of vector types - glm::vec3 and btVector3. They are exactly the same data structures but they annoy me because they don't go together nicely. A lot of manual conversions have to happen.

Suppose I have a function foo(btVector3 f) and I want to pass a glm::vec3 as the parameter without having convert it to btVector3 beforehand, similar to how you can pass a const char * into a function that requires an std::string without having to cast it to string first.

Now I don't want to go into each of these libraries and put operators into the classes manually, only for that to be ruined if I update the libraries (assuming that's even allowed by their license). How would I make one type of vector automatically cast to the other and vice versa from within my own project, without going inside and editing the libraries themselves?

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    can you create your own vector class that can be cast to either of the others, in either direction, then make all your objects of that type? – Garr Godfrey Jul 22 '17 at 07:36
  • @GarrGodfrey I've definitely thought about this, but I would rather use the time it would take to make that class on other things. But if it turns out that this is the only way, I guess it's what I'll have to resort to. EDIT: it also wouldn't be very clean because glm uses a lot of different data types like matrices and different dimensions of vectors. If I wanted it to look nice it would take a lot of code. – Elijah Seed Arita Jul 22 '17 at 07:38
  • you could have that class inherit from one of them, say glm::vec3, then only worry about casting to btVector3 and constructing from btVector3. Should only be a few lines. – Garr Godfrey Jul 22 '17 at 07:40
  • @GarrGodfrey it's fine for a workaround, but again it's not really optimal. I would still prefer to have these types automatically convert. But I guess the question is coming to: is this even possible? – Elijah Seed Arita Jul 22 '17 at 07:47
  • use gym::value_ptr it can be helpful and also it's faster. both gym, bulletphysics support init from scalars – nullqube Jul 22 '17 at 08:55
  • @nullqube thank you yes I do use this when possible – Elijah Seed Arita Jul 22 '17 at 09:02
  • _"They are exactly the same data structures [...]"_ This is incorrect. ``btVector3`` contains 4 floats. If you don't believe me, go to its definition and take a look. In Visual Studio, hold Ctrl and click it, or right click it and choose "Go To Definition". Also, I very much dislike how ``btVector3`` has ``.x()``, ``.y()``, ``.z()`` functions (or their ``.get()`` counterparts), as opposed to GLM. All those unnecessary function calls impact performance. You don't have to OOP _everything._ So no, they're definitely not the same. – Andrei Despinoiu Sep 29 '20 at 11:57

2 Answers2

2

Doesn't appear to be a way to make two classes implicitly castable without modifying at least one of the classes:

From the C++ spec

When considering the argument to a constructor or to a user-defined conversion function, only one standard conversion sequence is allowed (otherwise user-defined conversions could be effectively chained).

and

An expression e is said to be implicitly convertible to T2 if and only if T2 can be copy-initialized from e, that is the declaration T2 t = e; is well-formed (can be compiled), for some invented temporary t.

And if you look at what it means to be copy-initialized http://en.cppreference.com/w/cpp/language/copy_initialization

only a converting constructor can be used, not even an assignment operator. Since you cannot add a constructor to the classes, you would be stuck using a derived class. But, because the implicit casting cannot be chained (ie. the compiler won't look for conversions when converting A to C using an intermediate type), you would need to at least explicitly cast in some cases.

using the idea of a derived class, however, it may be less frequent than you think. If you declare any intermediate vector variables as your own class, you only need to explicitly cast when passing the return value from one library directly to another. You would be able to do this just fine:

MyVec v = GLM::GetVector(a,b,c);   // returns GLM::vec3

Bullet::TransformV(v, matrix);   // accepts btVector3 

You class would be something like:

class MyVec : GLM::vec3
{
public:
      MyVec(GLM::vec3 *vec) {
        ....
      }
      MyVec(btVector3 *vec) {
        ....
      }
      operator btVector3*() const { ... }
}
Garr Godfrey
  • 8,257
  • 2
  • 25
  • 23
1

As far as I know, you cannot implement implicit conversion operators outside of the class. What I'm used to do in similar situations is just creating a couple of simple conversion functions with really short (but still understandable) names. So in your case, assuming that each of vectors have just 3 fields x, y, z, such functions would look like this

inline btVector3 glm2bt(const glm::vec3& vec)
{
    return { vec.x, vec.y, vec.z };
}

inline glm::vec3 bt2glm(const btVector3& vec)
{
    return { vec.x, vec.y, vec.z };
}

See full sample at Coliru

Then, if you have a function foo working with one of the types

void foo(glm::vec3 v) { /* implementation */ }

and you want to feed an object of another type to it, the code at the caller site will still be quite compact

btVector3 vec = getVectorFromSomeWhere();
foo(bt2glm(vec));

Actually, based on my personal experience with similar situations, in retrospective I believe that it is a better solution than if I were able to define an implicit conversion operator you're looking for. At a cost of typing just several more symbols you get an easy way to locate all the inter-dependencies between those third-party libraries. Which may become very important at a certain point in life of your code base.

Vasiliy Galkin
  • 1,894
  • 1
  • 14
  • 25
  • Actually this is my current workaround which I am not liking. Starting to seem like this is the best option though. This is the one thing that has been irritating me with c++ – Elijah Seed Arita Jul 22 '17 at 08:24
  • But what in particular don't you like in this approach? Personally, I'm not even sure it should be considered as a "workaround" because the code is more explicit and arguably is easier to understand for a side observer. And the only downside I see is a bit more typing... or am I missing something? – Vasiliy Galkin Jul 22 '17 at 08:29
  • There is nothing particularly wrong with it, I just hoped that there was an easier solution. It seems that there isn't. Since this is what I've been doing and what I will continue to do I will accept this as the answer. – Elijah Seed Arita Jul 22 '17 at 08:30