0

Consider the simple code: I reserve memory for 2d vector, import the module in python, make instance of class and run the python code once. The second run of the python code make an error:

#include <vector>
#include <string>
#include <iostream>

using std::string;
using std::vector;

typedef std::vector<double> dim1;
typedef std::vector<dim1> dim2;

class DO
{
private:
    double dt;
    double gamma;
    size_t dimension;
    size_t num_steps;
    double t_simulation;

    dim1 y;
    dim2 coordinates;

public:
    DO(double dt,           // time step
       double gamma,        // parameter
       double t_simulation, // simulation time
       dim1 y               // state vector
       ) : dt(dt), gamma(gamma)
    {
        this->t_simulation = t_simulation;
        this->y = y;

        dimension = y.size();
        num_steps = int(t_simulation / dt);
        
        // coordinates.resize(num_steps);
        // for (size_t i = 0; i < num_steps; ++i)
        //     coordinates[i].resize(dimension);


        coordinates.reserve(num_steps);
        for (size_t i = 0; i < num_steps; ++i)
            coordinates[i].reserve(dimension);
    }
};

in tt.py

from  DampOscillator import DO
h = DO(0.01, 0.05, 10.0, [0.0, 1.0])

in DampOscillator.i :

%module DampOscillator

%{
#include "DampOscillator.hpp"
%}

%include stl.i
%include "std_string.i"
/* instantiate the required template specializations */
namespace std {
    %template(IntVector)     vector<int>;
    %template(DoubleVector)  vector<double>;
    %template(DoubleVector2) vector<vector<double> >;
}

%include "DampOscillator.hpp"

make
swig -c++ -python -shadow   -o DampOscillator_wrap.cpp DampOscillator.i
g++ -std=c++11 -fopenmp -O2  -fPIC -c DampOscillator_wrap.cpp -I /usr/include/python3.8 
g++ -shared -fopenmp DampOscillator_wrap.o   -o _DampOscillator.so
➜   python3 tt.py 
➜   python3 tt.py # second calling the code
[1]    51309 segmentation fault (core dumped)  python3 tt.py

I don't get any error if I use resize instead of reserve, but for later usage in an application I prefer to use reserve. I don't know what is wrong?

Abolfazl
  • 1,047
  • 2
  • 12
  • 29
  • 1
    `reserve` allocates memory and it changes vector's `capacity()`, but the actual `size()` of vector remains 0. `resize` checks if new size fits `capacity()`, if it does then resize is as cheap as to change `size()` number, if it doesn't, the whole memory is reallocated with bigger capacity – Alexey S. Larionov Jun 17 '21 at 10:04
  • Yes it does, I got confused just because of the undefined behavior. – Abolfazl Jun 17 '21 at 10:14

1 Answers1

0

reserve allocates memory for enough items but it doesn't resize it, so when you try to access the vector, it will give you an exception because you are accessing the array out of bounds.

coordinates.reserve(num_steps);
for (size_t i = 0; i < num_steps; ++i)
    coordinates[i].reserve(dimension);

If the size of coordinates is 0 then the access to coordinates[i] will cause the problem. You need to use resize here.

coordinates.resize(num_steps);
for (size_t i = 0; i < num_steps; ++i)
    coordinates[i].resize(dimension);

To clarify the difference:

 std::vector<int> v;  // size = 0
 v.reserve(10);  // The vector can now hold 10 items, but the size is still 0
 v[0] = 1; // Invalid, because size is 0.
 v.resize(1);  // the vector now still has room for 10 items, but the size is now 1.
 v[1] = 1; // ok now because the vector allows to hold 1 item at the moment.

If you know beforehand how many items the vector will hold, it makes sense to use reserve because this way you avoid possible memory allocations in between, which can have a positive impact on performance. If you don't know it, you have to use resize, because this will internally ensure that the capacity is sufficient anyway.

Devolus
  • 21,661
  • 13
  • 66
  • 113