1

I wrote helper functions for setting vertex properties. As you can see the 3 functions are basically the same, except that each function sets a different member variable of the vertices. As it is against the DRY principle and looks ugly I would like to combine them.

void setPositions(const int offset, const sf::Vector2f (&positions)[vertexCount]) {
    int index = ensureSizeAndGetIndex(offset);
    for(int i = 0; i < vertexCount; i++) {
        vertices[index + i].position = positions[i];
    }
}

void setTexCoords(const int offset, const sf::Vector2f (&texCoords)[vertexCount]) {
    int index = ensureSizeAndGetIndex(offset);
    for(int i = 0; i < vertexCount; i++) {
        vertices[index + i].texCoords = texCoords[i];
    }
}

void setColors(const int offset, const sf::Color (&colors)[vertexCount]) {
    int index = ensureSizeAndGetIndex(offset);
    for(int i = 0; i < vertexCount; i++) {
        vertices[index + i].color = colors[i];
    }
}

Things I considered:

  • Templates wouldn't work here as they don't handle member variables
  • I could combine the first two functions by passing a bool flag of which variable to use. But that wouldn't help for the third function.
  • I could add pointers to the member variables of the vertex class and an enum to choose from, but that would be too much (performance) overhead
  • Lambdas maybe, meta programming maybe?

Just would be interested for the cleanest solution here.

4
  • 1
    Why do you think templates aren't able to handle member variable pointers? Commented May 29, 2017 at 14:01
  • Identifiers can't be used as template parameters. That's what I meant. Commented May 29, 2017 at 14:20
  • you mean like this? Commented May 29, 2017 at 14:28
  • I wasn't aware it is possible that way. Works very well now. Commented May 29, 2017 at 17:17

1 Answer 1

1

If texCoords, position and color are the same type, the solution is very simple. Class member pointers.

Assuming that the class in question looks something like this:

class V {
public:

   int texCoords;
   int position;
   int color;
};

Then each one of your member functions becomes a wrapper that supplies the appropriate class member pointer. For example, setTexCoords becomes

void setTexCoords(const int offset, const sf::Vector2f (&texCoords)[vertexCount]) {
  setCommon(offset, texCoords, &V::texCoords);
}

The other two wrappers are analogous, and the common code becomes:

void setCommon(const int offset, const sf::Vector2f (&texCoords)[vertexCount],
               int V::*member) {
    int index = ensureSizeAndGetIndex(offset);
    for(int i = 0; i < vertexCount; i++) {
        vertices[index + i].*member = texCoords[i];
    }
}

Class member pointers look like regular pointers, and they share the same principle, but they're not traditional pointers, and it is necessary to fully understand how they work. For more information see your C++ book.

Sign up to request clarification or add additional context in comments.

2 Comments

With the proper use of templates, the solution can still work when the members' types don't match.
Very helpful answer. Class member pointers seem appropriate. The member variables of V don't have the same type, but I think that can easily be solved by making setCommon a function template.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.