Home > Back-end >  force template specialization with an incomplete (partially specified) templated type
force template specialization with an incomplete (partially specified) templated type

Time:10-10

I am encountering some issues when I try to force template specialization with an incomplete (partially specified) templated type.

Let's consider this oversimplified example: I have a template function declaration for which I provide forced specification for some common types in the corresponding C file.

in .h file:

template <class T>
void doSomething(const T &value);

in cpp file:

// force template specializations with various types
template <>
void doSomething<float>(const float &value)
{
  // actual code specialization for type float, es:
  printf("%.3f", value);
}
...

and for example, I want to do the same specialization for glm vector (from OpenGL Mathematics library), which is templated on the number of components and on the data type of its components (and also on precision, that I will skip in this example for clarity).

What I would like to avoid is to explicitly write the actual specialization for all possible values of N (leading to code repetition, since it is basically the same loop but with different number of iterations), as shown here:

template <>
void doSomething<glm::vec<2, float>>(const glm::vec<2, float> &value)
{
  printf("{");
  printf("%.3f", value[0]);
  for (int i = 1; i< 2   i)
    printf(", %.3f", value[i]);
  printf("}");
}

template <>
void doSomething<glm::vec<3, float>>(const glm::vec<3, float> &value)
{
  printf("{");
  printf("%.3f", value[0]);
  for (int i = 1; i< 3   i)
    printf(", %.3f", value[i]);
  printf("}");
}

template <>
void doSomething<glm::vec<4, float>>(const glm::vec<4, float> &value)
{
  printf("{");
  printf("%.3f", value[0]);
  for (int i = 1; i< 4   i)
    printf(", %.3f", value[i]);
  printf("}");
}

What I would like to do is writing something like this:

template <glm::length_t N>
void doSomething<glm::vec<N, float>>(const glm::vec<N, float> &value)
{
  printf("{");
  printf("%.3f", value[0]);
  for (int i = 1; i< N   i)
    printf(", %.3f", value[i]);
  printf("}"); 
}
// force full template specializations
template void doSomething<glm::vec<2, float>>(const glm::vec<2, float> &value);
template void doSomething<glm::vec<3, float>>(const glm::vec<3, float> &value);
template void doSomething<glm::vec<4, float>>(const glm::vec<4, float> &value);

but this solution arises the following compilation error: "illegal use of explicit template arguments"

I have read that "Function partial specialization" is not allowed in C , is this limitation still valid in modern standards C (14/17/20)? Is this the cause of the issue or maybe I am missing something? Thank you all in advance.

CodePudding user response:

Partial Function Specialization is not allowed. However, you can avoid explicitly writing the code for all possible values of N by using Function Overloading. The example below is based on your code, but I've replaced glm::vec<> with std::array<> since they are similar. Also, I only used a .C file for convenience.

#include <cstdio>
#include <iostream>
#include <array>

using namespace std;

// Overload the function for various types
void doSomething(const float &value)
{
  // actual code for type float:
  printf("%.3f", value);
}

// overloading not specialization
template<std::size_t N>
void doSomething(array<float, N> &value)
{
    printf("{");
    printf("%.3f", value[0]);
    for (int i = 1; i < N;   i)
        printf(", %.3f", value[i]);
    printf("}");
}

int main()
{
    float f = 5.0;
    doSomething(f);
    cout << endl;

    std::array<float, 3> a1 = {1, 2, 3};
    doSomething<3>(a1);
    cout << endl;

    std::array<float, 4> a2 = {1, 2, 3, 4};
    doSomething<4>(a2);
    cout << endl;
}

Here is the output:

$ g   --version
Apple clang version 13.1.6 (clang-1316.0.21.2.5)
$ g   -std=c  17 doSomething.C 
$ a.out
5.000
{1.000, 2.000, 3.000}
{1.000, 2.000, 3.000, 4.000} 
$

CodePudding user response:

As an alternative to Luis' answer with overloading, you can also add an additional level of abstraction. You can write a separate function that handles the array and then call it from within doSomething:

template<std::size_t N>
void doSomethingForArray(array<float, N> &value)
{
    printf("{");
    printf("%.3f", value[0]);
    for (int i = 1; i < N;   i)
        printf(", %.3f", value[i]);
    printf("}");
}

template <>
void doSomething<glm::vec<2, float>>(const glm::vec<2, float> &value)
{ doSomethingForArray(value); }

template <>
void doSomething<glm::vec<3, float>>(const glm::vec<3, float> &value)
{ doSomethingForArray(value); }


template <>
void doSomething<glm::vec<4, float>>(const glm::vec<4, float> &value)
{ doSomethingForArray(value); }

Given that your original solution still requires to explicitly instantiate all 3 functions, I don't think this is much of an overhead.

  • Related