3

I'd like to return an Eigen::Vector from a function and wondering what's the proper way to do it. Something like

Eigen::VectorXd& getMesh(int N) {
    Eigen::VectorXd mesh(N + 1);  // nb. of nodes
    // Nodes are equally spaced
    for (int i = 0; i < N + 1; i++) {
        mesh[i] = i * (1.0 / N);
    }

    return mesh;
}

int main() {
    // Mesh with 100 cells
    Eigen::VectorXd mesh = getMesh(100);

    return 0;
}

Now of course, we might get into problems here since the memory used to create the mesh vector in getMesh() might not be dynamically allocated i.e. when we return from the function, out reference "points" to deleted memory.

I could allocate the memory inside the main function and then pass it to the getMesh function, but is that considered fine? What other possibilities would I have?

handy
  • 696
  • 3
  • 9
  • 22
  • 1
    Does this answer look like it could be any help? https://stackoverflow.com/a/752699/3712532 – irowe Jul 15 '20 at 16:55
  • 1
    Anything wrong with using [`linSpaced`](https://eigen.tuxfamily.org/dox-devel/classEigen_1_1DenseBase.html#aaef589c1dbd7fad93f97bd3fa1b1e768)? `Eigen::VectorXd mesh = Eigen::VectorXd::LinSpaced(100, 0.0, 1.0);` – chtz Jul 15 '20 at 17:38
  • @chtz It was merely an example. Doesn't matter what I return, it's really just about "how to properly return stuff without copying it." – handy Jul 16 '20 at 11:04
  • @chtz In the end nothing - the question isn't about "how to create a mesh" though. – handy Jul 16 '20 at 11:12
  • @handy: keep in mind that the "copy" that happens when you return a `VectorXd` by value is not very costly (even without RVO). The 101 `double`s coordinate values are allocated separately on the heap and do not get copied when returning, only the pointer to it (`sizeof(VectorXd)` is 16 bytes). – Louen Jul 16 '20 at 17:20
  • 1
    @Louen okay thanks, I'll go bak to my "systems course" notes and review that, seems like I actually remember something wrongly. Thanks :) – handy Jul 16 '20 at 17:29

1 Answers1

5

You must not return a reference to a local variable of your function. This is called a dangling reference. Note that your compiler should give you a warning when you do this : e.g Visual Studio.

warning C4172: returning address of local variable or temporary: mesh

The problem with your current getMesh() function is that the local variable mesh gets allocated on the stack in the function execution. Then you return a reference to it in main, but the stack memory where mesh was allocated is freed when the getMesh() function exits. This means it will very likely get overwritten by later data in your program.

Note that this issue will happen even if the memory used to store the coordinates stored in your vector. Your mesh variable is an instance of a class (VectorXd) with several member variables. One of them will be the size of your vector, and the other a pointer to the dynamically-allocated storage for your vector's data.

This is an example of what VectorXd's definition could look like

class FakeVectorXd {
    int current_size;
    int max_size;
    double* data;
};

When you create mesh, the member variables current_size, max_size and data are allocated locally on the stack. Then in the vector's constructor data's value is set to point to a dynamically allocated memory area to store your vector's coordinates. When you return from getMesh(), the stack location where current_size, max_size and data live in memory is now marked as available and they will be overwritten.

What you can do is simply return the VectorXd by value. This is perfectly fine:

Eigen::VectorXd getMesh(int N) {
    Eigen::VectorXd mesh(N + 1);
    for (int i = 0; i < N + 1; i++) {
        mesh[i] = i * (1.0 / N);
    }
    return mesh;
}
Louen
  • 3,617
  • 1
  • 29
  • 49
  • 2
    One could mention that returning by value usually is fine because of [return value optimization](https://en.wikipedia.org/wiki/Copy_elision#Return_value_optimization) (ignoring the fact, that Eigen has a built-in function for what the OP re-implemented). – chtz Jul 15 '20 at 17:44
  • It is fine because the compiler optimized it for you but what if you want to do it yourself. How would you return? @chtz Practice – handy Jul 16 '20 at 11:11