Returning object reference from C++ function - arduino

I have began learning C++ for Arduino and have run into some troubles.
I have some functions reading/writing to SPIFFS files.
Since the code for opening configuration files is common I would want to have a separate function to handle it.
I have come up with following function declaration
ConfigFileOpenStatus DeviceOpenConfigFile(const char *path, File *file);
The function accepts pointer to char array for the file path, and pointer to opened file.
I then tried to make following function definition
ConfigFileOpenStatus DeviceOpenConfigFile(const char *path, File *file)
{
if (SPIFFS.exists(path))
{
file = &SPIFFS.open(path, "r+");
return !file ? Failed : Opened;
}
else
{
file = &SPIFFS.open(path, "w+");
return !file ? Failed : Created;
}
}
That did not work as compiler complained with error error: taking address of temporary [-fpermissive]
As I understand this means that the file object will be disposed once DeviceOpenConfigFile function returns?
So my question is whether its possible to implement a function in a way where I can get File object reference and release it later?

SPIFFS.open apparently returns File, by value. The returned value will be a temporary variable available on that line. So taking the address of that one doesn't make any sense, for the same reason as int func (void); ... &func() doesn't make any sense. It has nothing to do with the surrounding DeviceOpenConfigFile function.
It doesn't make sense to assign a new address to pointer passed by parameter, for the same reason as void func (int x) { x = 0; } doesn't make sense - you change a local variable only, nothing on the caller side gets changed and nothing gets returned to the caller.
It would seem that the solution you are looking for is this:
ConfigFileOpenStatus DeviceOpenConfigFile(const char *path, File* file)
{
...
*file = SPIFFS.open(path, "r+");
where file is allocated on the caller-side.

Related

C functions returning an array

Sorry for the post. I have researched this but..... still no joy in getting this to work. There are two parts to the question too. Please ignore the code TWI Reg code as its application specific I need help on nuts and bolts C problem.
So... to reduce memory usage for a project I have started to write my own TWI (wire.h lib) for ATMEL328p. Its not been put into a lib yet as '1' I have no idea how to do that yet... will get to that later and '2'its a work in progress which keeps getting added to.
The problem I'm having is with reading multiple bytes.
Problem 1
I have a function that I need to return an Array
byte *i2cBuff1[16];
void setup () {
i2cBuff1 = i2cReadBytes(mpuAdd, 0x6F, 16);
}
/////////////////////READ BYTES////////////////////
byte* i2cReadBytes(byte i2cAdd, byte i2cReg, byte i2cNumBytes) {
static byte result[i2cNumBytes];
for (byte i = 0; i < i2cNumBytes; i ++) {
result[i] += i2cAdd + i2cReg;
}
return result;
}
What I understand :o ) is I have declared a Static byte array in the function which I point to as the return argument of the function.
The function call requests the return of a pointer value for a byte array which is supplied.
Well .... it doesn't work .... I have checked multiple sites and I think this should work. The error message I get is:
MPU6050_I2C_rev1:232: error: incompatible types in assignment of 'byte* {aka unsigned char*}' to 'byte* [16] {aka unsigned char* [16]}'
i2cBuff1 = i2cReadBytes(mpuAdd, 0x6F, 16);
Problem 2
Ok say IF the code sample above worked. I am trying to reduce the amount of memory that I use in my sketch. By using any memory in the function even though the memory (need) is released after the function call, the function must need to reserve an amount of 'space' in some way, for when the function is called. Ideally I would like to avoid the use of static variables within the function that are duplicated within the main program.
Does anyone know the trade off with repeated function call.... i.e looping a function call with a bit shift operator, as apposed to calling a function once to complete a process and return ... an Array? Or was this this the whole point that C does not really support Array return in the first place.
Hope this made sense, just want to get the best from the little I got.
BR
Danny
This line:
byte *i2cBuff1[16];
declares i2cBuff1 as an array of 16 byte* pointers. But i2cReadBytes doesn't return an array of pointers, it returns an array of bytes. The declaration should be:
byte *i2cBuff1;
Another problem is that a static array can't have a dynamic size. A variable-length array has to be an automatic array, so that its size can change each time the function is called. You should use dynamic allocation with malloc() (I used calloc() instead because it automatically zeroes the memory).
byte* i2cReadBytes(byte i2cAdd, byte i2cReg, byte i2cNumBytes) {
byte *result = calloc(i2cNumBytes, sizeof(byte));
for (byte i = 0; i < i2cNumBytes; i ++) {
result[i] += i2cAdd + i2cReg;
}
return result;
}

How to open a file in Qt-method but read it use C-language function?

I know Qt has a lot of good methods for reading file.But in my work , I have to open a file by using Qt and get a pointer pointing to the start address of the file.So how to do that?
Here is my codes:
char *buffer;
if(file.open(QIODevice::ReadOnly))
{
QByteArray dataArray=file.readAll();
buffer=dataArray.data();
}
char test[1024];
for(int i=0;i<1024;i++)
{
test[i]=*buffer;
buffer++;
}
I use QByteArray QIODevice::​readAll()to read all available data from the device, and returns it as a byte array.
Then I use char * QByteArray::​data() to set my pointer buffer.
But when I try to read the data by a for loop,my Qt debugger throw me an error:read access violation,so how would this happen?
BTW , the file to be read is very big so I can't use a buffer to read them all once. Instead , I have to do as what I did here. When I read a 7kb-sizes-file ,my codes runs well. When I read a 700kb-sizes-file , here comes the problem.
The reason you are getting access violation is that dataArray is declared inside if code block and goes out of scope. You want to declare the dataArray outside of that block, eg:
QByteArray dataArray;
if(file.open(QIODevice::ReadOnly))
{
dataArray = file.readAll();
}
else
{
// give error
}
char *buffer = dataArray.data();
char test[1024];
for(int i = 0; i < 1024; i++)
{
test[i] = *buffer;
buffer++;
}
There are other potential problems in your code though:
First, what if the size of data read from the file is less than 1024? Then you will be reading past the end of the buffer (more access violations).
Second, what if the file is really big? Reading all that data at once may cause swapping/paging.

Pushing values to a Vector of pointers has garbage values

I am a C++ noob and I have written a method to get text file names including full-paths from a given directory. It gives a garbage value on the vector<wchar_t*> names. I used VS2010 debugger and analysed values. It looks like pointers are going out of scope. On the official C++ reference it says that push_back() copies values and seems like as I am pushing a pointer and it just copies pointers value.
static std::vector<wchar_t*> getFileNames(wchar_t* folder) // ex: c:\\textfiles\\My
{
using namespace std;
vector<wchar_t*> names;
wchar_t search_path[200];
swprintf(search_path, L"%s\\*.txt", folder); // ex: c:\\textfiles\\My\\*.txt
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile((wchar_t*)search_path, &fd);
if(hFind != INVALID_HANDLE_VALUE)
{
do
{
if(! (fd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) )
{
std::wstring fullPath(folder);
fullPath += L"\\";
fullPath += std::wstring(fd.cFileName); // cFilename has something like Info.txt
names.push_back((wchar_t*)fullPath.c_str());
}
}while(FindNextFile(hFind, &fd)); //goes out of scope and values become garbage
FindClose(hFind);
}
return names; //vector with garbage values
}
Is it possible to get wchar_t* pushed in to the vector someway, a better work around rather than dynamically allocating memory or using heap variables?
Can I get compiler warning for mistakes like this on VS2010 or any VS version(Now I only get casting warning and errors)?
swprintf might overflow the buffer.
(wchar_t*)search_path is a superfluous cast.
Never break out the big guns if you don't need them.
if(! (fd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) ) means you only ignore directories without interesting attributes.
fullPath += std::wstring(fd.cFileName); another superfluous cast.
names.push_back((wchar_t*)fullPath.c_str()); pushes a pointer to the internal buffer of fullpath into names, even though it will be destroyed at the end of the block.
What you should do is change the signature to
static std::vector<std::wstring> getFileNames(std::wstring folder)
Or at least
static std::vector<std::unique_ptr<wchar_t>> getFileNames(wchar_t* folder)
to take advantage of RAII and reduce the chance for errors.
In any case, you should rewrite the function, and should take advantage of the standard-library internally.
As an example, with the proper prototype:
static std::vector<std::wstring> getFileNames(std::wstring folder) {
std::vector<std::wstring> names;
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile((folder+L"\\*.txt").c_str(), &fd);
if(hFind == INVALID_HANDLE_VALUE)
return names;
auto lam = [](HANDLE* p){FindClose(*p);}
std::unique_ptr<HANDLE, decltype(lam)> guard(&hFind, lam);
folder += L"\\";
do {
if(! (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
names.push_back(folder+fd.cFileName);
} while(FindNextFile(hFind, &fd));
return names;
}
Ideally, you should store class objects in your vector, instead of character pointers (or wchar_t pointers). You can do it, but it requires some extra work. That's the part that you're missing.
The issue you're seeing is exactly what you describe: the character arrays that are owned by the wstring objects are being destroyed when the wstring goes out of scope. By using wstring::c_str(), you are not creating an independent copy of the character array, you're just looking at the one that it already has created for its own use.
So you need a way to keep a character array around longer. Either you could use the wstring, or you need to copy the character array into one of your own.
The minimal change would be something like this:
std::wstring fullPath(folder);
fullPath += L"\\";
fullPath += std::wstring(fd.cFileName); // cFilename has something like Info.txt
wchar_t *wsz = new wchar_t[fullPath.size() + 1];
wcsncpy(wsz, fullPath.c_str(), fullPath.size());
names.push_back(wsz);
That's enough to get your strings into the vector, but because you're using character pointers, it's also your responsibility to clean them up. So when you are done using the vector, you will need to iterate through and delete each one of them before you let the vector be destroyed.
As I mention in a comment below, it's much simpler to use a std::vector. There are libraries that can help you with the memory management parts, if you must use wchar_t*. You caould take a look at the Boost Smart Pointer library, for example.
The variable fullPath is a local variable.
It goes out of scope.
That is the reason the pointers are becoming junk.
Allocate a dynamic memory as
vector<wstring*> names;// instead of vector<wchar_t*> names;
std::wstring *fullPath = new wstring(folder);
Never store references of objects into a list if you are not aware of their scope.

Problems with a structure copy

I am having a compiler issue in Visual Studio 2005 using the standard C compiler when trying to do a structure copy from one location to another.
The types are defined in a file as follows:
definition.h
#define MAX 7
typedef struct{
char recordtext[18];
boolean recordvalid;
}recordtype;
typdef recordtype tabletype[MAX];
typedef struct{
tabletype table;
}global_s;
Let us pretend that a global_s "object" is instantiated and initialized somewhere and a pointer to this structure is created.
#include "definition.h"
global_s global;
global_s* pglobal = &global;
init(&pglobal);
Meanwhile, in another file (and this is where my problem is) i am trying to create a local tabletype object, and fill it with the global table member, using a get method to protect the global (lets pretend it is "static")
#include "definition.h"
extern global_s* pglobal;
tabletype t;
gettable(&t);
void gettabl (tabletype* pt)
{
*pt = pglobal->table;
}
When I go to compile, the line in the gettable function throws a compiler error "error C2106: '=': left operand must be l-value. It looks as though this should behave as a normal copy operation, and in fact if I perform a similar operation on a more basic structure I do not get the error. For example If I copy a structure only containing two integers.
Does anyone have a solid explanation as to why this operation seems to be incorrect?
(Disclaimer: I have developed this code as a scrubbed version of my actual code for example purposes so it may not be 100% correct syntactically, I will edit the question if anyone points out an issue or something needs to be clarified.)
It's the arrays in the struct; they cannot be assigned. You should define an operator=() for each of the structs, and use memcpy on the arrays, or copy them in a loop element by element.
(IF you want to get a reference to your global variable):
I am not sure, if this is correct (and the problem), but I think besides function prototypes, arrays and pointers (to arrays 1. element) are NOT exactly the same thing. And there is a difference between pointer to array and pointer to the 1. element of an array)
Maybe taking the adress of the array:
*pt = &(pglobal->table);
Anyway it might be better not to fetch the address of the whole array but the address of the first element, so that the resulting pointer can be used directly as record array (without dereferencing it)
recordtype* gettable (size_t* puLength)
{
*puLength = MAX;
return &(pglobal->table[0]);
}
(IF you want a copy of the table):
Arrays can't be copied inplace in C90, and of course you have to provide target memory. You would then define a function get table like this:
void gettable (recordtype * const targetArr)
{
size_t i = 0;
for (; i < MAX; i++) targetArr[i] = pglobal->table[i];
return;
}
an fully equivalent function prototype for gettable is:
void gettable(recordtype[] targetArr);
Arrays are provided by refernce as pointer to the first element, when it comes to function parameters. You could again ask for an pointer to the whole array, and dereference it inside gettable. But you always have to copy elementwise.
You can use memcopy to do the job as 1-liner. Modern compilers should generate equally efficent code AFAIK.

Why does my program halt when calling front() on a std::queue?

I want to use the Irrnet network library in an Irrlicht game.
The source code uses Linux sockets and I'm trying to port it for Windows replacing it with code that uses Windows' Winsock2.
The library compiles successfully but when I try to run the Quake example it crashes. I located the line at which the program stops but i can't figure out how to solve the problem.
The program stops at the second call of the function getNextItem
class NetworkType {
public :
NetworkType();
~NetworkType();
template<class T>
void getNextItem(irr::core::vector3d<T>& data);
private:
typedef std::queue<std::string> Container;
Container items;
};
template<class T>
void NetworkType::getNextItem(irr::core::vector3d<T>& data) {
T X, Y, Z;
std::istringstream item(items.front());
// the program does not get here the second time it calls this function
items.pop();
item >> X;
item >> Y;
item >> Z;
data = irr::core::vector3d<T>(X, Y, Z);
}
and exactly at this line
std::istringstream item(items.front());
Can anyone tell me why does the program stop the second time it gets to this line ?
here is the link for the complete source code
I assume by "stops" you mean "crashes" in some fashion? Likely causes for a crash on the line in question are:
The NetworkType instance that is invoking the getNextItem() method is garbage (the this pointer is garbage or null). This could happen due to bad pointer math elsewhere, a premature delete or destruction of the instance, et cetera. This would manifest as a fault when the program attempted to access the items member.
The items container is empty. In these cases the return value of front() is undefined (since it is a reference) and the constructor for istringstream may be crashing. front() itself may be raising a debug/runtime check error as well depending on your compiler and its configuration.
Actually you might have a runtime error on this one if the dequeue is empty: MSDN deque
So just check the deque isn't empty before you try to pop a value from it.
if(items.size()>0)
{
//do things
}
else
{
//error deque empty
}
[edit] confounded std and (I guess) MSDN ( OP doesn't say) lib.

Resources