Razioark
Razioark

Reputation: 23

Merge templates that are the same, and use different functions instead

I want to create a vector (math) structure that be treated as cartesian, or polar. There should only be one structure, but automatically passed to the appropriate functions.

I have the follow code:

struct cartesian {};
struct polar {};

template<unsigned int N, typename format = cartesian>
struct Vec
{
    float v[N];
};

However, although the format parameter does not change the internals of the structure, I believe I end up with two different types, rather than the one. This isn't necessary. Instead of two different types, I would prefer that only the following two functions are necessary:

template <unsigned int N>
Vec<N, cartesian> nrm(Vec<N, cartesian> v)
{
    return v;
}

template <unsigned int N>
Vec<N, polar> nrm(Vec<N, polar> v)
{
    return v;
}

The compiler determines which function to use based on the format template parameter.

I'm not sure I explained that too well, but hopefully you can understand what I mean. Please ask for any clarification.

Is there any way this can be accomplished (with templates)?

Thanks.


For R Sahu:

The two separate functions would be like:

Vec<N, cartesian> nrm(Vec<N, cartesian> v)
{
    float vLen = sqrt(v[0] * v[0] + v[1] * v[1]);

    Vec<N, cartesian> result = v;

    result[0] /= vLen; // 0 is X
    result[1] /= vLen; // 1 is Y

    return result;
}

template <unsigned int N>
Vec<N, polar> nrm(Vec<N, polar> v)
{
    Vec<N, polar> result = v;

    result[1] = 1.0F; // 1 is length of vector

    return result;
}

Sorry if return v; was causing confusion, it was just to make the error checker quiet, and I wanted to remove verbosity.


Non templated example might help:

INT CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow)
{
    // I believe the code is currently being compiled down to the non-template equivalent:
    // How I DON'T want it

        // 2 separate type definitions
        Vec2Car cartesianVec = { 1, 1 };
        Vec2Pol polarVec = { 0.785F, 1.41F };

        // Appropriate function is called for the type passed in
        Vec2Car cartesianNrm = nrm(cartesianVec);
        Vec2Pol polarNrm = nrm(polarVec);

    // How I DO want it

        Vec2CarPol cartesianVec = Vec<2, cartesian>(1, 1); // created with <format = cartesian>, doesn't change type definition, but compiler should know what to call below
        Vec2CarPol polarVec = Vec<2, polar> ( 0.785F, 1.41F); // as above

        // cartesianVec was created with <format = cartesian>, so use this the appropriate version of nrm();
        Vec2CarPol cartesianNrm = nrm(cartesianVec);
        // polarNrm was created with <format = polar>, so use this the appropriate version of nrm();
        Vec2CarPol polarNrm = nrm(polarVec);

    return EXIT_SUCCESS;
}

Upvotes: 1

Views: 102

Answers (2)

TAS
TAS

Reputation: 2079

As long as you have format as one of the template parameters there will be two structures generated by the compiler. It is the type that allows the compiler to select which of the two functions to call at compile time.

Upvotes: 0

R Sahu
R Sahu

Reputation: 206737

Just use one function template with two template parameters.

template <unsigned int N, typename format>
Vec<N, format> nrm(Vec<N, format> v)
{
    return v;
}

or, as suggested by @cdhowie, use

template <typename T>
T nrm(T v)
{
    return v;
}

Update, in response to updated question

Your functions are a little confusing. When you use:

result[0] /= vLen; // 0 is X
result[1] /= vLen; // 1 is Y

do you mean

result.v[0] /= vLen; // 0 is X
result.v[1] /= vLen; // 1 is Y

I am assuming that's what you mean.

You can have the most re-usable code by using:

template<unsigned int N, typename format = cartesian>
struct Vec
{
   typedef format Format;
   float v[N];
};

void nrm(float v[], cartesian dummy)
{
   float vLen = sqrt(v[0] * v[0] + v[1] * v[1]);
   v[0] /= vLen;
   v[1] /= vLen;
}

void nrm(float v[], polar dummy)
{
   v[1] = 1.0F;
}

template <typename T>
T nrm(T v)
{
   T result = v;
   nrm(result.v, T::Format());

   // Not sure what's the purpose of computing result.
   // It is not being returned.

   return v;
}

Upvotes: 2

Related Questions