1

Currently, I've implemented a template class Mat that's a wrapper around the matrix class of a third party library (T is the type of the components: double, int, etc.). Now, I wish to implement a Tensor class uses one Mat for storage and a second Mat for mapping indices.

I've templated the Tensor class as Tensor where T is the same as for the Mat class, Order and Dim are integers and are the order (or rank) and dimension (2 or 3) of the tensor. Sym is a boolean flag for symmetry.

The Tensor class will make use of Voigt notation to condense higher-order tensors onto matrices (for example, a 3-by-3-by-3-by-3 tensor can be mapped to a 6-by-6 matrix; this is done by mapping each pair of indices to a single index). In Voigt notation, a 3-by-3 tensor can be mapped to a 6-by-1 vector (matrix) but moving the (0,0) component in the tensor to the 0-location in the vector. similar (1,1) -> 1, (2,2) -> 2, (1,2)-> 3, (0,2)-> 4, and (0,1)-> 5. A similar rule exits for 2-by-2 tensors (they map to a 3-by-1 matrix).

To this end, I'd like my Tensor class to own the matrix:

0 5 4
5 1 3
4 3 2

If Dim == 3 and Sym == true. There are corresponding maps for unsymmetric tensors and 2D tensors (4 in all). These don't depend on the other template parameters (T and Order).

Thus, at what point do I specialize them? (Here, the question becomes applicable to anyone who has a templated class that needs a static const member to only partial specializations).

I've checked out this question here: Where to define static const member variables of a template class. But it doesn't discuss partial specialization.

So far, I have a forward declaration and a class definition in the same header file:

//cl_Tensor.hpp
namespace myNamespace
{
template< typename T, int Order, int Dim, bool Sym >
class Tensor;
}

template< typename T, int Order, int Dim, bool Sym >
class myNamespace::Tensor
{
protected:
    myNamespace::Mat< T> mMat; // storage
    static const myNamespace::Mat < uint > mTensorMap;

public:
// member functions and the like...
}

In my unit tests for my Tensor class, I can type:

template<> const moris::Mat< moris::uint> moris::Tensor< moris::real, 1, 2, true>::mTensorMap = { { 0, 2}, {2, 1} };
template<> const moris::Mat< moris::uint> moris::Tensor< moris::real, 1, 2, false>::mTensorMap = { { 0, 3}, {2, 1} };
template<> const moris::Mat< moris::uint> moris::Tensor< moris::real, 1, 3, true>::mTensorMap = { { 0, 5, 4}, {5, 1, 3}, {4, 3, 2} };
template<> const moris::Mat< moris::uint> moris::Tensor< moris::real, 1, 3, false>::mTensorMap = { { 0, 5, 4}, {8, 1, 3}, {7, 6, 2} };

The problem is that I have to do this for every order (1, 2, 3, and 4). And should I have Tensors of other types (here, real is a typdef for long double), I will have too much duplicate code.

Where can I initialize the maps then?

Community
  • 1
  • 1
Luis Negrete
  • 43
  • 1
  • 6

2 Answers2

2

You cannot partially specialize a static data member of a class template, but you can explicitly specialize them as you said in your question. But there's nothing from stopping you from outsourcing the definitions to some other function template:

namespace myNamespace {
    template< typename T, int Order, int Dim, bool Sym >
    const Tensor<T, Order, Dim, bool>::Mat<Dim> mTensorMap = 
        TensorMapCreator<T, Order, Dim, Sym>::makeMap();
}

Then it's just a matter of creating a class template TensorMapCreator with a static member function makeMap() that you can partially specialize according to what it is you specifically want to do:

template <typename T, int Order, int Dim, bool Sym>
struct TensorMapCreator;

template <typename T, int Order, bool Sym>
struct TensorMapCreator<T, Order, 2, Sym> {
    static Mat<2> makeMap(); 
};

template <typename T, int Order, bool Sym>
struct TensorMapCreator<T, Order, 3, Sym> {
    static Mat<3> makeMap(); 
};

// etc.
Barry
  • 286,269
  • 29
  • 621
  • 977
0

I was able to go off the other answer and came up with the following solution. In the Tensor header file, I only declare the static const in the class definition. Afterwards, I template the member initialization using a MapCreator class.

//Tensor.hpp
template< typename T, int Order, int Dim, bool Sym >
class space::Tensor
{
protected:
    Mat< T> mStorageMat;
    static const Mat < unsigned int > mTensorMap;
public:
    // ...
};

template< typename T, int Order, int Dim, bool Sym >
const space::Mat< unsigned int> space::Tensor<T, Order, Dim, Sym>::mTensorMatp = space::TensorMapCreator< Dim, Sym>::makeMap();

Then, the TensorMapCreator class is only templated for the two arguments that my member variable depends on:

//TensorMapCreator.hpp
namespace space {
    template< int Dim, bool Sym>
    class TensorMapCreator;

    class TensorMapCreator< 2, true >; // specialized forward
    class TensorMapCreator< 2, false>; // declarations
    class TensorMapCreator< 3, true >;
    class TensorMapCreator< 3, false>;
}

class space::TensorMapCreator< 2, true >
{
public:
    static
    space::Mat< unsigned int>
    makeMap()
    {
        // creates the correct map
    }
};
// 3 more specializations for the other
// combinations of dimmension and symmetry
Luis Negrete
  • 43
  • 1
  • 6