Home > Back-end >  Is safe boost::multi_array_ref returned by a function?
Is safe boost::multi_array_ref returned by a function?

Time:04-22

I'm trying to use boost::multi_array_ref to use a block of contiguous data in my code. But I worry about if I use the code like below, the 1d C array won't be confirmed to save:

#include <iostream>
#include "boost/multi_array.hpp"
boost::multi_array_ptr<double, 2> test(int N,int c,int d) {
    double *data = new double[N];
    boost::multi_array_ref<double, 2> a(data, boost::extents[c][d]);
    for (int i = 0; i < 10; i  ) {
        for (int j = 0; j < 10; j  ) {
            a[i][j] = 10 * i   j;
        }
    }
    return a;
}
int main()
{
    boost::multi_array_ptr<double, 2> a = test(100000000,10000,10000);
    for (int i = 0; i < 10; i  ) {
        for (int j = 0; j < 10; j  ) {
            std::cout << a[i][j] << std::endl;
        }
    }
}

The result is right and I want to use std::unique_ptr to replace the C style array, but the Visual Studio told me:

no instance of constructor "boost::multi_array_ref<T, NumDims>::multi_array_ref [with T=double, NumDims=2Ui64]" matches the argument list   

I don't know which kind of parameter boost::multi_array_ref need. Why the std::unique_ptr can't be use as parameter but C style array can? Can I use C style array without worry? Is safe boost::multi_array_ref returned by a function?

I'm sure that it will cause memory leak,but how to solve it?

CodePudding user response:

double *data = new double[N];

That's a raw pointer and no-one owns the allocation. You're correct that it leads to memory leaks. However, since you want to include ownership, why use multi_array_ref?

Live On Compiler Explorer

#include <algorithm>
#include <numeric>
#include "boost/multi_array.hpp"

auto test(int c, int d) {
    boost::multi_array<double, 2> a(boost::extents[c][d]);
    std::iota(a.data(), a.data() a.num_elements(), 0);
    return a;
}

#include <fmt/ranges.h>
int main()
{
    boost::multi_array<double, 2> a = test(4, 5);

    fmt::print("{}\n", a);
    fmt::print("{}\n", test(3, 6));
    fmt::print("{}\n", test(6, 2));
}

Prints:

[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17]]
[[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]]

And has no memory leaks.

If you need the multi-array-ref:

You might hack your way around with owning smart pointers:

using ARef    = boost::multi_array_ref<double, 2>;
using ARefPtr = boost::shared_ptr<ARef>;

Now you can allocate the data like you did before - but without the leak:

auto test(int c, int d) {
    auto data = boost::shared_ptr<double[]>(new double[c*d]);

See this post for details and compatibility with std::shared_ptr

And Use the aliasing constructor to share ownership of data while changing the apparent element type to ARef:

    auto a = ARefPtr(data, new ARef(data.get(), boost::extents[c][d]));
    std::iota(a->data(), a->data()   a->num_elements(), 0);
    return a;
}

#include <fmt/ranges.h>
int main()
{
    ARefPtr a = test(4, 5);

    fmt::print("{}\n", *a);
    fmt::print("{}\n", *test(3, 6));
    fmt::print("{}\n", *test(6, 2));
}

Of course, now you're dealing with smart pointers, so you need more de-references. See it Live:

[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19]]
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17]]
[[0, 1], [2, 3], [4, 5], [6, 7], [8, 9], [10, 11]]

CodePudding user response:

I have found a perfect solution for me without boost.

A self-defined 2d array(Matrix) class

But if someone can give an answer about boost::multi_array_ref, please delete this answer.

  • Related