How to avoid "Warning: ISO C++ forbids variable length array ːfilename' [-Wvla]" error in C++11

Asked

Viewed 691 times

1

I have the following piece of code that aims to create a buffer for the file name that will be created based on some information provided at class instantiation:

char fileName[size];
memset(fileName,'\n',size);
sprintf(fileName, "%s/%s%s-%05u.%s", filePath.c_str(), filePrefix.c_str(), date, this->image_counter++, fileExtension.c_str());
cv::imwrite(fileName, frame);
delete[] fileName;

When compiling such code I’m getting the following message:

ObjectTracker.cpp: In member function ‘void ObjectTracker::PersistImage(cv::Mat&)’:
ObjectTracker.cpp:80:20: warning: ISO C++ forbids variable length array ‘fileName’ [-Wvla]
  char fileName[size];
                    ^
ObjectTracker.cpp:85:11: warning: deleting array ‘fileName’
  delete[] fileName;
           ^

What would be the most elegant and technical way to write such code and thus avoid this error?

I’m wearing C++11;

2 answers

2


Two errors:

  1. The C language allows creating arrays on the variable-sized stack, but C++ does not. In C++ the size of arrays allocated to the stack must be known at build time.
  2. You’re giving delete to free memory of an array allocated to the stack. This is not allowed, nor necessary. Objects created on the stack are automatically destroyed at the end of the scope that declared them.

If the array is to have a fixed size, set at compile time, it is not necessary to give delete[] in it.

If its size is variable, then you will need to make dynamic allocation. Some options:

Manual management:

char *fileName = new char[size];
memset(fileName,'\n',size);
sprintf(fileName, "%s/%s%s-%05u.%s", filePath.c_str(), filePrefix.c_str(), date, this->image_counter++, fileExtension.c_str());
cv::imwrite(fileName, frame);
delete[] fileName;

Automatic management with unique_ptr:

std::unique_ptr<char[]> fileName(new char[size]);
memset(fileName.get(),'\n',size);
sprintf(fileName.get(), "%s/%s%s-%05u.%s", filePath.c_str(), filePrefix.c_str(), date, this->image_counter++, fileExtension.c_str());
cv::imwrite(fileName.get(), frame);
//Não é necessário dar delete, quando o unique_ptr sair de escopo ele liberará a memória

The two tests generate exactly the same Assembly, as can be checked here in a simplified example: https://godbolt.org/g/Z1nJ9A. There is no loss of performance by using the std::unique_ptr.

Another option is with the std::vector:

std::vector<char> fileName(size, '\n');//Cria um vector de tamanho size, preenchido com \n
sprintf(fileName.data(), "%s/%s%s-%05u.%s", filePath.c_str(), filePrefix.c_str(), date, this->image_counter++, fileExtension.c_str());
cv::imwrite(fileName.data(), frame);
//Não é necessário dar delete, quando o vector sair de escopo ele liberará a memória

But in this case the generated code is a little lower.

  • Thank you for the answer, it was very enlightening, in the case of vector I think it would be heavy for what wish as can be seen in the code above is just a temporary buffer that will be created thousands of times and needs to be very very fast even, both by time and processor overload (Cortex-A) in the case of the second option qualo impact of using the unique_ptr, which are its main features?

  • 1

    I ran some tests and updated my response. As expected, unique_ptr is a "free" abstraction, the generated code is the same as manual memory management. Already the vector disappointed me a little. I think his builder is a little too complicated for the optimizers.

0

In C++ I would do the following:

string fileName = filePath + "/" + filePrefix + date + to_string(image_counter++) + fileExtension + "\n";
cv::imwrite(fileName.c_str(), frame);

A deficiency of the above code is that the counter is not formatted with 5 positions. I don’t think there’s an easy and cool way to solve this in pure C++ without using sprintf, and even with sprintf I think it’s a bit tricky. One way without using sprintf is this:

string counter = to_string(image_counter++);
string zeros(5-counter.size(), '0'); // supoe que counter nao vai ser maior que 99999
string fileName = filePath + "/" + filePrefix + date + zeros + counter + "." + fileExtension + "\n";
cv::imwrite(fileName.c_str(), frame);

Browser other questions tagged

You are not signed in. Login or sign up in order to post.