3

I'm trying to implement my own math library, and I'm starting off with vectors. The idea is to give the class a pointer to an array of numbers, then copy the array and store it in the data address given by a private variable pointer. To begin with, I used alloca to try and free up some memory for the private variable

vml.h

namespace vml {
    // Vectors
    template <typename in_type, const int in_length>
    class vec {
    public:
        vec(in_type* in_data) {
            std::cout << data << std::endl;
            std::copy(in_data, in_data + in_length, data);
        }
        vec() {
            data = nullptr;
        }
        in_type& operator()(int index) const {
            _ASSERT(0 <= index && index < in_length);
            return data[index];
        }

    private:
        in_type* data = alloca(in_length * sizeof(in_type));
    };

main.cpp

int main() {
    int list[] = { 1,2,3 };
    int list2[] = {2,4,6 };

    vml::vec<int, 3> a(list);
    vml::vec<int, 3> b(list);

    return 0;
}

This gives no errors however, for some reason, alloca returns the same address twice when calling two instances. I searched this up everywhere and I couldn't find an explanation why. So I decided to allocate memory using an array. If you can answer this question that would be extremely helpful. Thanks.

  • 1
    you did declare `operator()` as `const` – 463035818_is_not_an_ai Feb 24 '21 at 12:00
  • 4
    1st) Using `alloca()` (in C++) is bad idea in general. 2nd) Using `alloca()` for member variables seems to me an especially bad idea. Considering, that `alloca()` was meant for temp. local storage in functions (in C)... (I recalled [alloca()](https://man7.org/linux/man-pages/man3/alloca.3.html) just to be sure that I didn't miss or confuse something...) – Scheff's Cat Feb 24 '21 at 12:01
  • You need not only memory, but you need to construct objects (ie call their constructor). C memory allocation functions don't do that – 463035818_is_not_an_ai Feb 24 '21 at 12:03
  • 1
    this looks very relevant: https://stackoverflow.com/a/1029951/4117728 – 463035818_is_not_an_ai Feb 24 '21 at 12:05
  • 1
    Can't reproduce (even with fixes to make the code compile) the behaviour you describe in the first case. The error message is because it is not possible to return a non-`const` reference to a (non-mutable) member of a `const` object. Change the return type of the `operator()` to `const in_type &`. The assignment `data = nullptr` should also not compile in the second case, but you have not mentioned that. – Peter Feb 24 '21 at 12:08
  • @Peter That's weird, that's all the errors that I get. – Joshua Pasa Feb 24 '21 at 12:09
  • 2
    As you are using a compile time constant for the data length just replace the manual memory management with `std::array` this will place the data on the stack as you require. – Richard Critten Feb 24 '21 at 12:42
  • 1
    @RichardCritten Can I just use the normal arrays `in_type data[in_length]`? Do you know to solve the issue with the constant array? – Joshua Pasa Feb 24 '21 at 13:01

3 Answers3

8

You have to be very careful with alloca. It allocates memory on the stack rather than the heap. That memory is freed as soon as the function which called alloca exits. In this case, it will be called in the constructor so when you call operator() that memory has already been freed and you are dealing with undefined behavior.

Unless you really need to avoid heap allocations and you know for sure that you wont overflow the stack and you understand all the limitations of using alloca, it's best to steer clear of it.

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
  • I want to allocate memory on the stack though because I dont need to add more data to the list – Joshua Pasa Feb 24 '21 at 12:19
  • @JoshuaPasa you might want to check out this question: https://stackoverflow.com/questions/354442/looking-for-c-stl-like-vector-class-but-using-stack-storage – Den-Jason Feb 24 '21 at 12:21
  • I called `alloca` outside of the constructor so shouldn't it stay around until the class instance is out of the scope? – Joshua Pasa Feb 24 '21 at 13:10
  • @JoshuaPasa - I'm not sure exactly where the allocation happens when you use that initialization syntax in a class. I believe it's part of the constructor call but you'd have to check with a language lawyer on that. In any case, `alloca` is actually part of the C runtime. It has no concept of classes. – Ferruccio Feb 24 '21 at 13:30
  • @Ferruccio is there any way to use alloca such that it only gets freed when the class is out of the scope. A bit like how variables in a class get freed. That's why I wanted to use `in_type data[in_length]` but for some reason, it initializes as a constant even though i didn't add `const` to the prefix, however, it does solve the issue of the memory address – Joshua Pasa Feb 24 '21 at 13:34
  • @JoshuaPasa - No, all `alloca` does is increase the size of the current stack frame to make room for whatever you want to store there. As soon as the function returns the entire stack frame disappears. – Ferruccio Feb 24 '21 at 13:49
  • `vec(in_type* in_data, in_type*a=(in_type*)alloca(in_length * sizeof(in_type))):data(a){...}` would pull alloca out of the constructor and let it live longer, but that's still an incredibly bad idea, the size you want to allocate is constant and alloca is meant for non-constant sizes. – Marc Glisse Feb 24 '21 at 13:59
  • @Ferruccio How comes it works when initializing an array inside of the class such as `in_type data[in_length]`? – Joshua Pasa Feb 24 '21 at 14:01
  • @JoshuaPasa - because in that case you are defining an array whose scope is the same as the class scope. In the alloca case you are defining a pointer whose scope is the same as the class scope, but you are initializing it to a chunk of memory which will go out of scope as soon the the constructor is done. The pointer is still in scope and will still point to that same memory, but that memory is no longer yours. – Ferruccio Feb 24 '21 at 14:14
2

Lets start with the basics, your stack is most likely only 1 MB, so after a few vectors and recursive calls you program will likely die.

To solve it if you want it on stack you could use std::array as data

Warning untested code ahead

template <typename in_type, const int in_length>
class vec {
public:
    vec(in_type* in_data) {
        std::cout << data << std::endl;
        std::copy(in_data, in_data + in_length, data);
    }
    vec() = default;
    in_type& operator()(int index) const {
        _ASSERT(0 <= index && index < in_length);
        return data[index];

    }

private:
  std::array<in_type, in_length> data;
};

Alternatively if you want to use all the nice things from std::array

template <typename in_type, const int in_length>
class vec : public std::array<in_type, in_length> {
public:
  using std::array::array; // use constructors, might need template param to compile
}

This also means that if you at some point just want to change to heap you just allocate your vec as every other class.

Another alternative is to use C++17 PMR, use an allocation on the stack as the storage and make vec PMR aware.

Surt
  • 15,501
  • 3
  • 23
  • 39
1

You cannot wrap alloca in a function and return its pointer outside, since the stack of wrapper function would be freed.

If you call it as member initializer, it is actually called from constructor, and may be freed when constructor returns and then re-used.

Alex Guteniev
  • 12,039
  • 2
  • 34
  • 79