Choosing implementation based on template arguments

Started by
8 comments, last by King Mir 10 years, 5 months ago

In the following code:


template <class COORD>
class Foo
{
public:

    void setCoordinate( const COORD& coord ) { m_Coordinate = coord; }
    const COORD& getCoordinate() const { return m_Coordinate; }

    /* ... other methods ... */

private:

    COORD m_Coordinate;
    
    /* ... other data ... */
};

If I were to instantiate the class with the following code:


Foo<void> foo;

I would be forming a reference to "void", which would throw a compiler error.

Is there a way to completely omit the methods "setCoordinate" and "getCoordinate" as well as the data member "m_Coordinate" in the specific case where the template parameter COORD is void?

The class "Foo" still works without those methods in place, it's just more limited.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
Advertisement

look for template specialization


template<>
class Foo<void>{
...implementation specific for foo<void>
};

Oh you can specialize entire classes as well? Wow, how did I miss that...

So does this mean I'm forced to re-write the entire class specific to match a void template parameter? The class in question isn't exactly small, and the only thing that changes is omitting those two methods. That's a lot of redundant code.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

Then you can use a common base class that defines the common functions.

template<typename COORD> struct Foo_base
{
    /* common base members and methods */
};

template<typename COORD> struct Foo : Foo_base<COORD>
{
    void setCoordinate( const COORD& coord ) { m_Coordinate = coord; }
    const COORD& getCoordinate() const { return m_Coordinate; }

private:
    COORD m_Coordinate;
};

template<> struct Foo<void> : Foo_base<void>
{
};

Thanks!

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

Sorry, but another question arose. Say I wish to specialise single methods in the class for specific COORD arguments.


struct Vector2D
{
    Vector2D( int x, int y ) : x(x), y(y) {}
    int x;
    int y;
};

template <class COORD>
class Foo
{
public:
    void print( const COORD& coord );
};

template <class COORD>
void Foo<COORD>::print( const COORD& coord )
{
    std::cout << "Can't print - not specialised" << std::endl;
}

template <>
void Foo<Vector2D>::print( const Vector2D& coord )
{
    std::cout << "2D coordinates: " << coord.x << "," << coord.y << std::endl;
}

int main()
{
    Foo<int>().print(6);
    Foo<Vector2D>().print(Vector2D(3,5));
    return 0;
}

However, I'd like the user to have the ability to use their own Vector2D class - he could call it "Vec2" or something else. Obviously I can't see into the future, so I can't specialise a method beforehand.

With the assumption that every Vector2D class thrown at it should have public data members named "x" and "y", how can I solve this?

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

If you assume that any template parameter has an x and y member, then just access the x and y members.

template <class COORD>
void Foo<COORD>::print( const COORD& coord )
{
    std::cout << "2D coordinates: " << coord.x << "," << coord.y << std::endl;
}

Ah, what I meant was that there could be completely other types as well which may or may not have "x" and "y", such as 1-dimensional types (which won't have "x" and "y"), or 3-dimensional types (which would have "x","y","z"), but would still count as coordinates, and require a specialised function to be dealt with.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty

The core of the problem is that your Foo class is responsible for printing the value of an arbitrary type. Delegate that work to the type itself, or at least the author of the type, instead. The Foo class then don't have to know how any arbitrary type wants to format itself, instead it lets the type print itself as it wants to.

And I am also going to assume that you want to print some member, for example m_Coordinate, and not just a parameter. Your example doesn't make sense as a member function since there's no need for it to access the object on which it is called.

namespace mynamespace
{
    struct myvector {};

    void format(myvector const &)
    {
        std::cout << "myvector" << std::endl;
    }
}

void format(int const &)
{
    std::cout << "int" << std::endl;
}
 
template<typename COORD> struct Foo : Foo_base<COORD>
{
    void print() {format(m_Coordinate);}

private:
    COORD m_Coordinate;
};

And while you're at it, pass the stream to the format function as well so you don't hard code it to cout. Now you add formatters for any type you need.

Ah, what I meant was that there could be completely other types as well which may or may not have "x" and "y", such as 1-dimensional types (which won't have "x" and "y"), or 3-dimensional types (which would have "x","y","z"), but would still count as coordinates, and require a specialised function to be dealt with.

This kind of thing is possible with the use of enable_if or similar template meta-programming, especially if you make your member functions templates too, but as Brother Bob points out, you might want to reconsider the design.

This topic is closed to new replies.

Advertisement