I want to convert the following code from objective C to C++.
In the class myClass, I have this attribute:
float tab[dim1][dim2][dim3];
In an objective-C file, the multidimensional array is filled from a binary file:
NSData *dataTab=[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"pathOfMyTab" ofType:#""]];
[dataTab getBytes:myClass -> tab length:[dataTab length]];
How could I translate this part into C++ ?
I am assuming that your file contains the byte-representation of the array. If this is the case, then to mimic the behaviour of your Objective-C code using only C++ (the only thing that makes this C++ is the reinterpret_cast<>, otherwise it is just straight C), you could use the following code. I have not added any error checking, but left some comments where you might want to perform some.
float tab[dim1][dim2][dim3];
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef dataTabURL = CFBundleCopyResourceURL(mainBundle, CFSTR("pathOfMyTab"), NULL, NULL);
CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, dataTabURL); // check for NULL return value
CFReadStreamOpen(stream); // check for errors here
CFReadStreamRead(stream, reinterpret_cast<UInt8 *>(tab), sizeof tab); // check that this function returns the number of bytes you were expecting (sizeof tab)
CFReadStreamClose(stream);
// we own "stream" and "dataTabURL" because we obtained these through functions
// with "create" in the name, therefore we must relinquish ownership with CFRelease
CFRelease(stream);
CFRelease(dataTabURL); // ditto
If you already have the path available in a std::string, then you can use the following C++ code to mimic the behaviour of your Objective-C code:
// make sure to include this header
#include <fstream>
// ... then elsewhere in your .cpp file ...
float tab[dim1][dim2][dim3];
std::string path = "path/to/mytab"; // obtain from somewhere
std::ifstream input(path, std::ios::binary); // check that the file was successfully opened
input.read(reinterpret_cast<char *>(tab), sizeof tab); // check that input.gcount() is the number of bytes you expected
I believe in this case we have to use reinterpret_cast<> because the file contains the actual representation of the array (assuming it was previously written to the file in a similar manner).
You can use a hybrid approach, once you have the CFURLRef containing the path to the resource, you can obtain a file system representation of the URL using this function (providing a suitably sized output buffer to store the result), and from there you should be able to pass that to one of std::ifstream's constructors (although, you may need to cast to the appropriate type).
C++ doesn't support variable-length arrays (the size of arrays must be known at compile time). There is also no matrix type provided by the standard library, so if the dimensions of your table vary at run time, then you will need a completely separate approach to the one in my answer. You could consider serialising the output from Objective-C (using e.g. JSON or another format) such that the dimensions of the matrix are also written to the output, making it easier to parse the file in C++.
Take a look at fstream, fread and read, all read binary files, pick the approach that suits.
On my mind the simplest and fastest way is to use memcpy() to copy NSData' bytes into target array with same structure (dimensions) as a source one. See, for example:
https://github.com/Voldemarus/MultiDimensionalArrayDemo/tree/master
#import "DemoClass.h"
#define DIM1 3
#define DIM2 4
#define DIM3 2
#interface DemoClass() {
int src[DIM1][DIM2][DIM3]; // source (initial) array
int dst[DIM1][DIM2][DIM3]; // destination array
}
#end
#implementation DemoClass
- (instancetype) init
{
if (self = [super init]) {
for (int i = 0; i < DIM1; i++) {
for (int j = 0; j < DIM2; j++) {
for (int k = 0; k < DIM3; k++) {
int value = i*100 + j*10 + k;
src[i][j][k] = value;
}
}
}
}
return self;
}
int getIntFromArray(int *array, int i, int j, int k) {
int offset = j*DIM3 + i*DIM2*DIM3;
return array[offset];
}
void putIntToArray(int *array, int i, int j, int k, int value) {
int offset = j*DIM3 + i*DIM2*DIM3;
array[offset] = value;
}
- (void) run
{
// Step 1. Save array into NSData
NSInteger s = sizeof(int)*DIM1*DIM2*DIM3;
NSData *data = [[NSData alloc] initWithBytes:src length:s];
NSAssert(data, #"NSData should be created");
//Step2 - Create new array
int *bytes = (int *)[data bytes];
memcpy(dst,bytes,s);
// Step 3. Compare src and dst
for (int i = 0; i < DIM1; i++) {
for (int j = 0; j < DIM2; j++) {
for (int k = 0; k < DIM3; k++) {
int template = i*100 + j*10 + k;
int s = src[i][j][k];
int d = dst[i][j][k];
// NSLog(#"i %d j %d k %d -->s = %d d = %d",i,j,k,s,d);
NSAssert(s == template, #"Source array should have value from template");
NSAssert(d == s, #"Destination array should be identical to the source");
}
}
}
}
#end
float tab[dim1][dim2][dim3] looks like a three-dimensional array. The standard implementation is with three nested FOR loops.
So your C++ implementation can look like this:
read dim1, dim2, dim3 from somewhere, usually the first values in the file (for example 12 bytes, 4 bytes for each number)
read the rest of the file in three nested FOR loops
Something like:
for (size_t i = 0; i < dim1; ++i)
for (size_t j = 0; j < dim2; ++j)
for (size_t k = 0; k < dim3; ++k)
tab[i][j][k] = read_float_value(inputFile);
In Objective-C you can write the file in a similar way.
Here are some examples to get you started:
Three dimensional arrays of integers in C++
3D array C++ using int [] operator
I wrote the function readChar() which is designed to read the characters send by my WiFi module one by one(function works has advertised) and pass them to a char buffer of increasing size. The function should stop when char *endChar (multiple characters) have been detected or the number of character returned by timedRead() has exceeded size_t length.
I have several issues:
1/. I don't understand the syntax (found inside the Arduino Stream library) :
*buffer++ = (char)c;
Can you explain how the array buffer gets filled?
And why buffer[index] = (char)c; doesn't work here?
2/. I would like to compare buffer and endChar in the loop, possibly by using strcmp(buffer,endChar) (maybe there is a better way). But that doesn't seem to work. In fact when printing the ASCII values of my char *buffer then seem to increment from the end of the buffer. E.G.:
So what is the best way to do that comparison?
The code, inserted in the loop:
_dbgSerial->println("buffer");
for (int i = 0; i < 32; i++){
_dbgSerial->print(buffer[i], DEC);
_dbgSerial->print(",");
}
_dbgSerial->println("");
prints:
buffer
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,10,13,10,0,0,0,0,
Here is the function readChar():
size_t Debugwifi::readChar(char *endChar, char *buffer, size_t length) {
if (length < 1) return 0;
size_t index = 0;
while (index < length) {
int c = timedRead();
if (c < 0 ) break;
//buffer[index] = (char)c;
*buffer++ = (char)c;
_dbgSerial->println("buffer");
for (int i = 0; i < 32; i++){
_dbgSerial->print(buffer[i], DEC);
_dbgSerial->print(",");
}
_dbgSerial->println("");
if (strcmp(buffer,endChar)==0) {
break;
_dbgSerial->println("brk");}
index++;
}
return index;
}
As Rickard has explained, *buffer++ = (char)c; is how you assign a character to the memory a pointer points at, and then increment the pointer.
However, your function has a lot of problems - you keep comparing unset memory with *endChar. I suggest:
size_t Debugwifi::readChar(const char * const endStr, // const pointer to const.
char * const buffer, const size_t length) {
if (length < 1) return 0;
const size_t endLen = strlen(endStr);
for (size_t index = 0; index < length; index++) {
const int c = timedRead();
if (c < 0 ) break;
buffer[index] = (char)c;
// Debug
_dbgSerial->println("buffer");
for (size_t i = 0; i < length; i++){ // Better to use size_t here,
// and compare against length not 32
_dbgSerial->print(buffer[i], DEC);
_dbgSerial->print(",");
}
_dbgSerial->println("");
// Finished?
if (index >= endLen) {
if (memcmp(&buffer[index-endLen], endStr, endLen)==0) {
_dbgSerial->println("brk"); // Must do this *before* "break"!
break;
}
}
}
return index;
}
I have added a lot of consts. It's hard to have too many.
The important point is that once you have read enough characters, to start comparing the last characters you have read to the end marker.
Note that this function does not remove the end marker, and if you pass a 32-byte zero-filled array and it reads 32 characters, the result will NOT be zero terminated.
Finally, I changed the argument name to endStr because I had expected endChar to be a pointer to a single character - not a NUL-terminated string.
*buffer++ = (char) c;
First writes the value of c to what buffer is currently pointing to.
Then it increments the buffer
This is also why your loop to print buffer doesn't work.
You start printing from the position after what was just filled.
This is also why your strcmp doesn't work. It doesn't actually compare what you have filled your buffer with. It compares the content beyond what have been filled.
If you want your printing code to work you should save the initial value of buffer before the loop;
const char *buffer_start = buffer;
Then use that in your printing code instead of buffer.
My title is a bit confusing, but I'm trying to write a loop that will change the value in 81 arrays with different names. I want to either initiated the array with a value or an array of values. This is part of my sudoku solver code since I don't think I'm explaining it well.
int cell1[], cell2[9], cell3[9],cell4[9]......cell81[9]; // <-- this will make 81 cells with an array that can hold a possible of 9 candidates
cout << "User input: << endl; // lets use ...1.5...14....67..8...24...63.7..1.9.......3.1..9.52...72...8..26....35...4.9...
// as an example
Let's assume I store that input into a Char Array and I'm going to use a loop to decide whether to initiate the given value or '.' as an empty value.
For empty values, I'm looking to initialize the array with 1-9 values. I can do this easily with this code.
If( ( (int)charArray[ 0 ] - 48) > 0 ) { // type cast to int. Neg = initialize array with 1-9
// pos = initialize with original value
cell1[ 0 ] = (int)charArray[ 0 ] - 48;
} else {
cell1[ 9 ] = { 1,2,3,4,5,6,7,8,9};
}
I want to avoid writing this code 81 times for 81 cells ( Considered as writing junk code ). I can't figure out how to write the loop. I'm open to suggestions on how I can code this different using classes, functions, and etc. Thanks in advance.
Create the cell array as a 2-dimensional array, with 81 rows and 9 columns.
int cell[81][9];
Now you can loop through them using the syntax cell[r][c]. For instance,
for( i = 0; i < 81; ++i ) {
cell[i][0] = 1;
// ...
cell[i][8] = 9;
}
If you'd prefer to avoid 2-D arrays, you can declare the array as a 1-dimensional array, and just index into it appropriately.
int cell[81 * 9];
for( i = 0; i < 81; ++i ) {
cell[i + 0*81] = 1;
// ...
cell[i + 8*81] = 9;
}
int a1[9],a2[9],a3[9],a4[9],...
void init_array(int *ptr, const char *chr, int len, int start){
for(int i = start; i < len; i++){
if(chr[i] == '.')continue;
ptr[i] = chr[i]-'0';//converts character to integer.
}
}
int main(int argc, char **argv)
{
std::string str;
cin >> str;
init_array(a1,str.c_str(),9,0); init_array(a2,str.c_str(),9,9/*increment by 9*/);...
//..
return 0;
}
Write a function called init_array() that accepts an integer pointer and initializes the array for you. You can avoid duplicating the code this way.