0

I'm attempting to use a C API that returns a void** (an array of data arrays) that may consist of any number of each different type (double, long, short, custom struct ... ) The API works okay for all but arrays char arrays.

// a simplified API prototype
void READHISTORY( long id                  // ID in custom database
                , short numFields          // number of fields 
                , const timestruct* oldest // oldest time to look up
                , const timestruct* newest // newest time to look up
                , long maxRetVals          // the size of each data array
                , const short DataTypes[]  // types[] length numFields
                , void* theData[]          // pointers to data arrays
                , short* numReturned       // number of returned values 
)

To set up the main read, the database is read to see how many fields and their custom defined types.

#define MAXVALS (2048) 
long id = 20L;
short vals;  // number of vals returned
tstruct oldest = {2017,1,1,0,0,0};
tstruct newest = {2018,1,1,0,0,0}; 
int numFields;
std::vector<short> datatypes;

GETFIELDS(id, &numFields, datatypes);  // API to get the number and types of fields
std::vector<void*> values;

for(int i=0; i<numFields; i++) {
    datatypes.push_back(DataTypes[i]);
    switch (DataTypes[i]) { // want the arrays created dynamically 
       case -1: // time 
          values.push_back(new tstruct[MAXVALS]);break;
       case -2: // short
          values.push_back(new short[MAXVALS]);break;                          
       case -3: // long
          values.push_back(new long[MAXVALS]);break;
       case -4: // double
          values.push_back(new double[MAXVALS]);break;
       default: // positive numbers are char array length
          values.push_back(new char[DataTypes[i]][MAXVALS]);
    }
}

The READHISTORY call is working and returns data, however I'm getting an exception error for the array of char arrays.

// call the API to get data into the individual data arrays
READHISTORY(id, numFields, &oldest, &newest, MAXVALS, datatypes.data(), values.data(), &vals);

for (int i=0; i<vals ; i++) {
  for(int j=0; j< datatypes.size(); j++) {
    switch(datatypes[j]) {
      case -1: tstruct tval = static_cast<tstruct*>(values[1])[i];   break; // works fine           
      case -2: short sval = static_cast<short*>(values[j])[i];       break; // works fine
      case -3: long lval = static_cast<long*>(values[j])[i];         break; // works fine
      case -4: double dval = static_cast<double*>(values[j])[i];     break; // works fine
      ... 

      default:  
        int len = datatypes[j]; // positive datatypes are the char array length
        char* nchar = new char[len+1];

        // attempting to copy the char gets an access violation is here.
        strncpy(nchar, static_cast<char**>(values[j])[i], len); 

        nchar[len+1] = '\0';
        String^ nstr = gcnew String(nchar);
        delete nchar;
        // do something with nstr and so on... 
      }
   }
}

Unhandled exception: System.AccessViolationException: Attempted to read or write protected memory.

What am I doing wrong?

Mgetz
  • 5,108
  • 2
  • 33
  • 51
nimchimpsky
  • 99
  • 11
  • 1
    Possible duplicate of [is std::vector same as array\[number\]?](https://stackoverflow.com/questions/3077748/is-stdvector-same-as-arraynumber) – François Andrieux Jan 16 '18 at 16:19
  • 2
    For any type `T` it have to be that memory allocated and pointed to by a `T*` have to be the same as the memory wrapped by a `std::vector`. Otherwise it would be impossible to get a `T*` from a `std::vector` (which you can). If `T` is a `void*` then it follows that `void**` and `std::vector` have to be equivalent. – Some programmer dude Jan 16 '18 at 16:21
  • Possible duplicate of [Are std::vector elements guaranteed to be contiguous?](https://stackoverflow.com/questions/849168/are-stdvector-elements-guaranteed-to-be-contiguous) – anatolyg Jan 16 '18 at 16:32
  • Your code looks fine (now, after all these changes). If there's a problem, it is in something you are not telling us or not showing us. In the first version `DataTypes` and `numFields` are hardcoded. In the second one you request them from API. Are you sure the results are in sync? – AnT stands with Russia Jan 16 '18 at 17:06
  • Thanks @AnT. The code was right. There was an error with one of the data types. arrays char arrays wasn't being passed correctly. I think it is now, but getting an exception when attempting to copy it. I'd up vote, but don't have the reputation yet. – nimchimpsky Jan 18 '18 at 19:34

1 Answers1

3

You are using the wrong constructor to create your vectors:

std::vector<short> values(numFields);

This creates a vector containing numFields null pointers. You later append dynamically allocated arrays, but the null pointers will still be the first elements which READHISTORY will try to use.

Instead just default-construct empty vectors:

std::vector<short> values;
sth
  • 222,467
  • 53
  • 283
  • 367