Malkalma
Malkalma

Reputation: 17

I need the max size of my vector (the value I gave) C++

I have the following codes:

Fixed sized vector:

template <typename T, size_t siz>
struct fixVec : public std::vector<T> {
    void push_back(const T& d) {
    if (std::vector<T>::size() >= siz) throw std::bad_alloc();
    std::vector<T>::push_back(d);
}
};

I use "fixVec" like this:

ClonableHeteroStore<ClonableTestClass, fixVec<ClonableTestClass*, 1>, MyException> t1;

It creates a vector with the size of 1. (1)

EXPECT_NO_THROW(t1.add(new ClonableTestClass));

It adds 1 element, its fine, no error. (2)

EXPECT_THROW(t1.add(new ClonableTestClass), MyException);

Here it should throw the exception. (3)

My problem is that: how can I check when I reach the "max_size()" of MY vector. Not the max size of the vector my PC can handle. Or how can I get back the value I gave in code (1) (the number between the !):

ClonableHeteroStore<ClonableTestClass, fixVec<ClonableTestClass*, !!!1!!! >, MyException> t1;

I have a class named ClonableHeteroStore where I have the following code:

template <typename T1, class S, typename PRED = int>
class ClonableHeteroStore 
{
private:
    S flakker;
    size_t currently;
    size_t maxi;
public:
    ClonableHeteroStore() :  { currently = 0; maxi = 0; }

    void add(T1 *rhs)
    {
        if (currently < ???????? )
        {
            flakker.push_back(rhs);
            currently++;
        }
        else
        {
            delete rhs;
            throw PRED();
        }
    }

What should it do: In case we did not reach the maximum size of the vector we gave in code (1) it should add a new element to the vector. In case we reached the limit we gave in code (1), it should drop an exception.

So line (2) won't have any problem because the vector's size is 1 and this is the first element we put in it. The "if" part should run.

Line (3) will throw an exception because it tries to put a second element, but the vector size is 1. The "else" part should run.

The only code is possible to change is the ClonableHeteroStore class. Somehow I should find out, how big is my vector. I read alot that vector is dinamically allocating it's size etc. etc., but in this case, it has to throw an exception if I jump over the limit (1).

Thanks for any help!

Upvotes: 1

Views: 365

Answers (3)

Remy Lebeau
Remy Lebeau

Reputation: 598279

Fixed sized vector:

Why are you using std::vector instead of std:array for a "fixed" array?

It creates a vector with the size of 1. (1)

No, it doesn't. It creates a vector whose size() and capacity() are initially 0, not 1. Your siz value is being used as an artificial max capacity, you are still allowing the vector to grow dynamically until it reaches your max.

EXPECT_THROW(t1.add(new ClonableTestClass), MyException);

Here it should throw the exception. (3)

True, and worse, it leaks the ClonableTestClass object as a result. You should be using std::unique_ptr<ClonableTestClass> instead so you maintain ownership of the object at all times and free it correctly, even if an exception is thrown.

how can I check when I reach the "max_size()" of MY vector.

You already are. That is exactly what size() >= siz is doing.

Or how can I get back the value I gave in code (1) (the number between the !):

ClonableHeteroStore<ClonableTestClass, fixVec<ClonableTestClass*, !!!1!!! >, MyException> t1;

It is only available in the siz template parameter. If you want OUTSIDE code to know the value of siz, then you need to expose public access to it, eg:

template <typename T, size_t siz>
struct fixVec : public std::vector<T>
{
    const size_t max_elements_allowed;
    fixVec() : max_elements_allowed(siz) {}
    ...
};

Or, if you are using C++11 or later:

template <typename T, size_t siz>
struct fixVec : public std::vector<T>
{
    const size_t max_elements_allowed = siz;
    ...
};

Then you can do this:

void add(T1 *rhs)
{
    if (currently < flakker.max_elements_allowed)
    {
        flakker.push_back(rhs);
        currently++;
    }
    else
    {
        delete rhs;
        throw PRED();
    }
}

FYI, you don't really need currently, you could just use flakker.size() instead:

void add(T1 *rhs)
{
    if (flakker.size() < flakker.max_elements_allowed)
    {
        flakker.push_back(rhs);
    }
    else
    {
        delete rhs;
        throw PRED();
    }
}

That said, std::vector has a capacity() method, which is what you are really looking for:

void add(T1 *rhs)
{
    if (flakker.size() < flakker.capacity())
    {
        flakker.push_back(rhs);
    }
    else
    {
        delete rhs;
        throw PRED();
    }
}

You just need to pre-allocate the vector's internal array up front:

template <typename T, size_t siz>
struct fixVec : public std::vector<T>
{
    fixVec() { std::vector<T>::reserve(siz); } // <-- add this!
    ...
};

I have a class named ClonableHeteroStore where I have the following code:

...

What should it do: In case we did not reach the maximum size of the vector we gave in code (1) it should add a new element to the vector. In case we reached the limit we gave in code (1), it should drop an exception.

Since your fixVec is already handling that, you should just add the element unconditionally and let push_back() throw if needed:

void add(T1 *rhs)
{
    try
    {
        flakker.push_back(rhs);
    }
    catch (...)
    {
        delete rhs;
        throw PRED();
    }
}

A better design is to let the caller maintain ownership of the object that is being added and do not release that ownership unless add() is successful. That way, if an exception is thrown, the caller can free the object correctly and not leak it. That should not be add()'s responsibility to handle:

void add(T1 *rhs)
{
    try
    {
        flakker.push_back(rhs);
    }
    catch (...)
    {
        throw PRED();
    }
}

...

ClonableTestClass *obj = new ClonableTestClass;
try
{
    t1.add(obj);
}
catch (...)
{
    delete obj;
    throw;
}

Or, if you are using C++11 or later:

std::unique_ptr<ClonableTestClass> obj(new ClonableTestClass);
t1.add(obj.get());
obj.release();

Or:

void add(std::unique_ptr<T1> rhs)
{
    try
    {
        flakker.push_back(rhs.get());
        rhs.release();
    }
    catch (...)
    {
        throw PRED();
    }
}

...

t1.add(std::unique_ptr<ClonableTestClass>(new ClonableTestClass));

The only code is possible to change is the ClonableHeteroStore class. Somehow I should find out, how big is my vector.

If you can't change fixVec to make its siz value public, and you must find out its siz, then you have to resort to some template trickery, eg:

template<typename T, size_t siz>
size_t get_capacity(const fixVec<T, siz> &) { return siz; }

...

void add(T1 *rhs)
{
    if (flakker.size() < get_capacity(flakker))
        flakker.push_back(rhs);
    else
        throw PRED();
}

But, this is exactly why STL containers expose standard size() and capacity() methods, so you don't have to resort to tricks like this.

On the other hand, ClonableHeteroStore doesn't really need to know that siz value to begin with. It should just perform the push_back() unconditionally and let it throw if needed.

I read alot that vector is dinamically allocating it's size etc. etc., but in this case, it has to throw an exception if I jump over the limit (1).

You really should be using std::array instead of std::vector.

If you must use std::vector, than you should consider writing a custom Allocator for your vector to use instead of using the default Allocator. The custom Allocator can throw if it is asked to allocate memory for too many elements. Then you can let std::vector behave normally, and not override its push_back() at all.

Upvotes: 0

ambiso
ambiso

Reputation: 588

Interesting question!

You could for example use template template parameters:

template<template<typename, size_t> typename X, typename T, size_t N>
constexpr size_t get_size(const X<T, N>&) {
    return N;
}

int main() {
    std::cout << get_size(fixVec<int, 5>{}) << std::endl;
}

This will not only work for fixVec but any template type with a type and a size_t as template arguments!

So for example the following is also valid:

get_size(std::array<int, 5>{});

Upvotes: 1

Fran&#231;ois Andrieux
Fran&#231;ois Andrieux

Reputation: 29072

Two common solutions for extracting template arguments from a type are type traits that use partial explicit specialization or to use a function template to deduce the template arguments from the function argument. The function-based approach consists of writing a function that expects a fixVec<T, I> and getting the compiler to deduce I :

// The compiler will deduce T and I from the argument
template<class T, size_t I>
size_t get_vec_size(const fixVec<T, I> &) {
    return I;
}

void foo()
{
    fixVec<char, 10> my_vec;
    const auto my_vec_max_size = get_vec_size(my_vec);
}

To use partial explicit specialization you write a new template and specialize it for the type whose template arguments you need and, again, get the compiler to deduce those arguments for you :

template<class T>
struct vec_size {};

template<class T, size_t I>
struct vec_size<fixVec<T, I>> : std::integral_constant<size_t, I> {};

void foo()
{
    fixVec<char, 10> my_vec;
    const auto my_vec_max_size = vec_size<decltype(my_vec)>::value;
}

Upvotes: 3

Related Questions