user3810155
user3810155

Reputation:

Compile time checking for the number of arguments in a variadic template

I made a generic n-dimensional array class, but I want to make it safer with some compile-time checks.

The constructor and operator() are only expected to work correctly when the number of arguments given equals dimension. Still, if I write code like

Array<int, 2> a(1, 2, 3); //3 dimensions for a 2d array == crash

or

Array<int> a(1); //plain 1D array
std::cout << a(1, 2); //segfault

it just silently compiles, and I need to run a debugger, but it's quite confusing to debug variadic templates.

Can you think of a good way to provide a compile time check for the correct number of arguments matching the dimension?

template<typename T, int dimension = 1>
class Array
{
private:
    std::unique_ptr<T[]> pointer;
    int size[dimension];
    int realSize;

public:
    Array()
    {
    }

    template<typename... Ns>
    Array(Ns... ns)
    : realSize(1)
    {
        create(1, ns...);
    }

private:
    template<typename... Ns>
    void create(int d, int n, Ns... ns)
    {
        realSize *= n;
        size[d - 1] = n;
        create(d + 1, ns...);
    }

    void create(int d)
    {
        pointer = std::unique_ptr<T[]>(new T[realSize]);
    }

    int computeSubSize(int d) const
    {
        if (d == dimension)
        {
            return 1;
        }
        return size[d] * computeSubSize(d + 1);
    }

    template<typename... Ns>
    int getIndex(int d, int n, Ns... ns) const
    {
        return n * computeSubSize(d) + getIndex(d + 1, ns...);
    }

    int getIndex(int d) const
    {
        return 0;
    }

public:
    template<typename... Ns>
    T& operator()(Ns... ns) const
    {
        return pointer[getIndex(1, ns...)];
    }

    int getSize(int d = 1) const
    {
        return size[d - 1];
    }
};

Upvotes: 3

Views: 1354

Answers (1)

R Sahu
R Sahu

Reputation: 206567

You can add compile time assert statements at the right places to prevent code like

Array<int, 2> a1(1, 2, 3);

and

Array<int> a3(1);
std::cout << a3(1, 2);

from being compiled.

Here's once place:

  template<typename... Ns>
     Array(Ns... ns)
     : realSize(1)
     {
        static_assert(sizeof ...(Ns) == dimension, "Incorrect number of arguments");
        create(1, ns...);
     }

That prevents the line

Array<int, 2> a1(1, 2, 3);

from being compiled.

Another place:

  template<typename... Ns>
     T& operator()(Ns... ns) const
     {
        static_assert(sizeof ...(Ns) == dimension, "Incorrect number of arguments");
        return pointer[getIndex(1, ns...)];
     }

That prevents

Array<int> a3(1);
std::cout << a3(1, 2);

from being compiled.

Here are some valid statements:

Array<int, 2> a2(1, 2);

Array<int> a3(1);
std::cout << a3(0);

Upvotes: 1

Related Questions