1

I'm trying to use a set of template classes with a variadic parameter. I have several options ahead of me that I could choose from. Before any of my templates are declared or defined I currently have these prototypes: I'm familiar with templates but I haven't had much experience with variadic types when working with templates so the syntax does get a little confusing to me at times. Being that they are all empty shells they do currently compile.

template<typename ClassType, typename... Args>  class MatrixReference;
template<typename ClassType, typename... Args>  class MatrixStorage;
template<typename ClassType, typename... Args>  class MatrixAllocation;

I have a User End Class that will use these classes depending on the intentions of use; it is currently an empty shell for now until I get the other classes defined correctly with the appropriate behavior:

template<typename ClassType, typename... Args>
class Matrix {      
};

The rest of the class from the prototypes shown above will inherit from a base class so that the above user class will have a container of them such that the container would be: std::vector<std::unique_ptr<MatrixBase>> or std::vector<shared_ptr<MatrixBase>> and the vector will only ever contain 1 of each type from the listed prototypes. For instance vector[0] would contain a MatrixStorage, vector[1] would container a MatrixReference and vector[2] would contain a MatrixAllocation. Each of these classes have different responsibilities as their names suggest. The storage class will contain a raw stack copy of the elements. The reference class will be use to reference those copies. The allocation class will be used when the elements are declared on the heap. The base class looks like this:

template <typename ClassType = void>
class MatrixBase {
protected:
    MatrixBase(){}
    virtual ~MatrixBase(){}
}; // Matrix

I have also thought about inheriting them from a non template base as this class does nothing but serve the purpose of being able to store different class types into a single container. I may go ahead and change this to a non template type but for now I'm using it as is to stay with the conventions of its derived types.

Now onto the declaration of my class templates: I really only need to use one of them here since all follow the same pattern, but I'll show all 3 anyway since they are currently empty shells.

// Stores All Of The Contents Of The Matrix
template<typename ClassType, typename... Args>
class MatrixStorage : public MatrixBase<ClassType> {
}; // MatrixStorage    

// Used To Reference The Storage Class Of The Matrix
template<typename ClassType, typename... Args>
class MatrixReference : public MatrixBase<ClassType> {
}; // MatrixReference

// Used Only When User Wants To Create A Matrix On The Heap
template<typename ClassType, typename... Args>
class MatrixAllocation : public MatrixBase<ClassType> {
}; // MatrixAllocation

The design approach that I'm looking for is that when this class is used it follows the pattern where the first type is always the type of data the matrix will store either it be an int, float, or some other user defined type; the next parameter is where the use of a variadic parameter comes in so that if one instantiates a template as such:

Matrix<float,2,2> mat2x2; // Default constructor making it empty

This will generate a 2x2 sized Matrix of floats

Matrix<int,3,3,3> mat3x3x3; 

This would generate a 3x3x3 volumetric matrix of ints

So the variadic template part will always be + integers and the minimal requirement would be Matrix<type, 1> where this would be in a sense a scalar or a single element matrix or a 1x1 matrix.

This is where I'm presented with a few options. I could use the following

  • size_t... N
  • unsigned... D
  • typename... Args

Currently at the moment as you can see it is declared with the last of the choices. So now comes the main question:

If I decided to use the Parameter Pack where I have a helper class as such:

template <typename ClassType,typename... Dimensions>
class DimensionPack {
public: 
    typename std::tuple<ClassType, std::tuple<Dimensions...> >::type Dim;
    const unsigned int numarguments = sizeof...(Dimensions);
};

The question becomes; is there a known way to make the Variadic Parameter of the same type namely either size_t or unsigned int? If so an example would be appreciated or a reference link would help as well; I've searched and haven't found anything helpful that is similar enough to help me through this.

If there isn't I don't mind having to use size_t or unsigned int but I was preferring to be able to use the helper template to pack and unpack the variadic parameters for me this way I don't have to implement that in each and every class.

I also have a 2 other derived classes not shown here but one would be used with logging them to the screen, where the other would be used with reading and parsing from a file and writing out to a file.

Also as a side note: For extremely large data sets or extremely large sized Matrices: I also have this helper class to use for them:

template<typename ClassType, std::size_t bufferSize>
class MatrixBuffer {
    static std::vector<ClassType> matrixBuffer = std::vector<ClassType>().reserve( bufferSize );
};

Edit

I forgot to add this to the original question but I'm adding it now for a little more clarity. I do have a need to test each variadic parameter's value to see if it is odd or even and the results of them will be stored into a vector with a size of the amount of parameters storing a 0 for even or a 1 for odd. This is one of the reasons why I was leaning towards the use of a parameter pack because I could just pass it to a helper function that would return back the vector that is needed.

Francis Cugler
  • 7,788
  • 2
  • 28
  • 59
  • 1
    Is there a reason why you can't just use `template`? – Alex Zywicki Feb 02 '17 at 03:54
  • @AlexZywicki I was thinking that as well, but wasn't sure if the helper class for extracting and expanding into an std::tuple would make things easier or not. – Francis Cugler Feb 02 '17 at 04:00
  • @AlexZywicki Oh; I almost forgot but I do have a need to check if each dimension of the matrix is odd or even. So instead of just having `std::size_t...` storing them into the parameter pack to generate a vector of the size of that pack and to fill it with 0 for even or 1 for odd is also needed. I can go ahead and add that clarity to my question. – Francis Cugler Feb 02 '17 at 04:14
  • So, the answer is there isn't a good reason not to use `size_t...Args`. You are walking down the wrong path. – Yakk - Adam Nevraumont Feb 02 '17 at 16:29
  • @Yakk No; I was considering all options, and in the end I did use `size_t...` but as for my helper class instead of saving into a tuple; I ended up initialize an `std::vector<>` with values of the parameter pack. – Francis Cugler Feb 03 '17 at 13:20
  • 1
    @FrancisCugler Dynamic allocation to store compile time constants seems like a bad idea, but not my monkeys, not my circus. – Yakk - Adam Nevraumont Feb 03 '17 at 13:58

2 Answers2

2

std::size_t... Args and typename... Args are not the same. The first would expect integers like

Matrix<float,2,2> mat2x2;

while the second would expect types instead.
Of course, you could use std::integral_constant, but that'd be more verbose:

template <std::size_t N>
using size = std::integral_constant<std::size_t, N>;

Matrix<float,size<2>,size<2>> mat2x2;

On the other hand you could use std::index_sequence:

template<typename ClassType, std::size_t... Dims>
class Matrix {
    using Dimensions = std::index_sequence<Dims...>;
};
O'Neil
  • 3,790
  • 4
  • 16
  • 30
  • Yeah I understand that; but I'm leaning towards using a helper template class just for the parameter pack to store the contents into an `std::tuple` that'll help to pack and unpack the parameter list. I also need to check these parameters to see if they are in the range x > 0 and to test if they are even or odd and save those results into a container. I would then be able to pass that to my helper function... – Francis Cugler Feb 02 '17 at 07:37
  • Or am I over looking this... where I could use and yet still save that into an std::tuple in the same helper classes and functions. – Francis Cugler Feb 02 '17 at 07:38
  • @FrancisCugler And what is the problem with unpacking `std::index_sequence`? – O'Neil Feb 02 '17 at 07:40
  • wasn't familiar with index_sequence – Francis Cugler Feb 02 '17 at 07:41
  • I don't want an instantiation to look like this: `Matrix, <3>>` etc. I'd prefer just `Matrix` – Francis Cugler Feb 02 '17 at 07:43
  • @FrancisCugler Then you don't have a choice using `unsigned...`/`std::size_t...`. – O'Neil Feb 02 '17 at 07:44
  • Then I wouldn't be able to save them into std::tuple – Francis Cugler Feb 02 '17 at 07:46
  • @FrancisCugler No, a tuple expects types. `std::index_sequence` is what you need. – O'Neil Feb 02 '17 at 07:47
  • Oh okay; I'll have to look into the documentation of std::index_sequence. – Francis Cugler Feb 02 '17 at 07:48
1

Using static_assert it is possible to check at compile time :

template <typename ClassType,typename... Dimensions>
class DimensionPack {
        public:
            DimensionPack(const Dimensions&... args){
                checkType<Dimensions...>(args...);
            }
            ~DimensionPack(){}
        private:
            template<typename T> void checkType(const T& t) {
                static_assert(std::integral_constant<bool, std::is_same<T, size_t>::value>(), "T is not of type size_t");
            }
            template<typename T, typename... V> void checkType(const T& t, const V&... v) {
                static_assert(std::integral_constant<bool, std::is_same<T, size_t>::value>(), "T is not of type size_t");
                checkType<V...>(v...);
            }
    };
Clonk
  • 2,025
  • 1
  • 11
  • 26