0

I am working on a code where an array is passed to a function by passing the pointer to the first location. In the function, part of the array is used. This creates an unsafe situation because there is a chance if the caller function does not guess the max size of the array correctly the callee function can write past the array size and a stack overflow can occur. I was thinking of a solution to this and thought of using a function template and passing the array as reference as shown in this example.

modifyArray.h

#define MAXSIZE 10

class modifyArray
{    
public:
    void create();

    void unsafeFunction(double*);

    template<int N>
    void safeFunction(double (&array)[N] );

private:
    int computeLength();
};

modifyArray.cpp

#include <iostream>
#include "modifyArray.h"

int modifyArray::computeLength()
{
    return 11;
}

void modifyArray::create()
{
    double testarray[MAXSIZE];
    unsafeFunction(testarray);    
    safeFunction(testarray);
}

void modifyArray::unsafeFunction(double* array)
{
    int operatingSize = computeLength();
    for(int i = 0; i < operatingSize; i++) {
        array[i] = i*i;
    }
}

template<int N>
void modifyArray::safeFunction(double (&array)[N] )
{
    int operatingSize = computeLength();
    std::cout<< "Max size" << N <<std::endl;
    if(operatingSize > N) return; // Return or raise an exception

    for(int i = 0; i < operatingSize; i++) {
        array[i] = i*i;
    }
}

main.cpp

#include "modifyArray.h"    

int main(int argc, const char * argv[]) {    
    modifyArray C;    
    C.create();
    return 0;
}

I am looking for a solution that is minimally invasive to the existing code. Here I just have to add a template statement, change the argument from double* to reference, and insert an if statement to check the size. I don’t want to do a major rewrite. Also I don’t want to use dynamic allocation, vector, or std::array mostly because of the performance reasons. This is a low level function in a numerical simulation code and performance is very important. Is there a better solution? Is there a pitfall to doing what I am doing?

  • 4
    *[...]or std::array mostly because of the performance reasons*. A `std::array` it just a wrapper for an array and has the same run-time performance as a raw array. – NathanOliver Apr 26 '16 at 14:28
  • 2
    Either pass another parameter stating the number of elements or just use a `std::array`. The performance will be the same. – graham.reeds Apr 26 '16 at 14:30
  • 1
    Also I don't think you are describing a stack overflow. You are describing an out-of-bounds exception. – graham.reeds Apr 26 '16 at 14:32
  • *std::array mostly because of the performance reasons.* -- The `std::array` alleviates all of the issues you stated in your post. A `std::array` knows its own size, so you don't need to pass another parameter denoting this information, and as others stated, a `std::array` **is** an array, just with member functions. So there is no degradation in performance. – PaulMcKenzie Apr 26 '16 at 14:51
  • Prefer to pass `std::vector`. They are a lot easier than arrays. With arrays, you will need to pass the array, the capacity and optionally the number of occupied elements. The capacity of an array is lost when it is passed to a function. – Thomas Matthews Apr 26 '16 at 14:55
  • @ThomasMatthews It depends on how the array is passed. See me answer below. – NathanOliver Apr 26 '16 at 14:56

1 Answers1

4

If you really want to work with raw arrays and you want to safely modify all of the elements of the array without walking of the end then you can pass the array by reference and then use a range based for loop.

tmeplate <typename T, typename Function, std::size_t N>
void do_work(T (&arr)[N], Function f)
{
    for (auto & e : arr)
        e = f();
}

The above will apply the result of calling function to every element of the array and is guaranteed to stay in the bounds of the array. You could use it like

int main()
{
    int arr[10];
    do_work(arr, []() { static int i = 0; i++; return i * i; });
    for (auto e : arr)
        std::cout << e << " ";
}

output:

1 4 9 16 25 36 49 64 81 100 

Live Example

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • Thank you for the idea. In your example, the loop in `do_work` always goes over the complete length of the `arr`, in my actual code, I want to call `do_work` multiple times on the same array but utilize different lengths of the array each time. But thanks for the idea. Also `safeFunction` and `unsafeFunction` and `computeLength` here are just examples, my actual code does more complicated things. – user3469604 Apr 26 '16 at 18:30
  • @user3469604 If the us the case then you can always compare how far you want to run against `N` as `N` will be the size of the array. – NathanOliver Apr 26 '16 at 18:40