4

I need a 2d character array for use in a trash API that absolutely requires use of arrays and NOT vectors (much emphasis on this because all of my searching just had answers "use a vector". I wish I could).

I figured the way to do it would be to allocate an external array of size rows * character length, instead of doing:

char** arr;
arr = new char*[100];
// for loop that allocates the internal arrays

But I'm not sure what method I would need to use to make it contiguous? Do I need to allocate a massive 1D array first, then assign the 1D array to the 2D array in chunks?

user1324674
  • 384
  • 1
  • 3
  • 12
  • 2
    Are each dimensions known at compile time ? just one ? None ? – Jarod42 Sep 21 '17 at 22:48
  • > Do I need to allocate a massive 1D array first, then assign the 1D array to the 2D array in chunks? | Seems like a good option – Charles Sep 21 '17 at 22:48
  • @Jarod42 I can at BEST know the "row" dimension, but I would like to have both dimensions be dynamic. If one is known, how would I go about it? – user1324674 Sep 21 '17 at 22:49
  • Can you show prototype of the function ? – Jarod42 Sep 21 '17 at 22:49
  • Obviously, you *don't* do that! Instead, you use a `std::vector` of appropriate size, a `std::vector` of appropriate and parcel out the pointers there, and finally use `vdata()` on the out array to satisfy some API... – Dietmar Kühl Sep 21 '17 at 22:49
  • @Charles any tips on doing that? I'm not sure if I'm doing it correctly – user1324674 Sep 21 '17 at 22:49
  • @DietmarKühl unfortunately OCCI doesnt work like that. Correct me if I'm wrong but I've tried as many solutions I can think of to get it to work. It NEEDS an continuous chunk of an array in order to use the array fetch/writes. More info here- https://docs.oracle.com/cd/B19306_01/appdev.102/b14294/performance.htm – user1324674 Sep 21 '17 at 22:51
  • Well, the chunk of memory *is* contiguous! Also, which of the many functions you want to use? The code in the answer should be fine for something like `executeArrayUpdate()`. – Dietmar Kühl Sep 21 '17 at 22:52
  • You need to decide what you really need: a contiguous 2D aray or a jagged `char **` 2D array. While it is possible to kinda mate the two, i.e. make a jagged `char **` 2D array to store its data in a contiguous block of memory, it is not clear why you nedd it and what exactly should be contiguous. – AnT stands with Russia Sep 21 '17 at 22:56
  • From what I see in doc, you may use something like: `std::vector v(row * col); stmt->setDataBuffer(row, v.data(), type, col, output.data());` – Jarod42 Sep 21 '17 at 23:00
  • @Jarod42 I'll give that a shot and report back. Would it not need to be vector since i need a "string" ? – user1324674 Sep 21 '17 at 23:07
  • @user1324674 [Possible duplicate](https://stackoverflow.com/questions/21943621/how-to-create-a-contiguous-2d-array-in-c) – PaulMcKenzie Sep 22 '17 at 02:27
  • @PaulMcKenzie that was one of the examples I was referring to that used a vector, which I wasn't sure will work. – user1324674 Sep 22 '17 at 15:21
  • @Jarod42 I tried using the setDataBuffer with the vector as you suggested. It pulls data but the data appears to be incorrect- there are some extra values attached to some columns. – user1324674 Sep 22 '17 at 16:01
  • I might have swap `row` and `col` of might be adjusted depending of `type`... – Jarod42 Sep 22 '17 at 16:18
  • Possibly, I'd have to dig into a it a bit more but it looks like Bitwize's solution below might work out. Going to give that one a shot. Thanks for your input, when I get some extra time I'll try to figure out the vector solution as well, I'm sure I'll need it in the future. – user1324674 Sep 22 '17 at 16:56

3 Answers3

7

As other answers have said: allocate n * m entries to create the contiguous data, and then it can be wrapped in pointers to create a 2d array.

... absolutely requires use of arrays and NOT vectors ...

I'm not sure if vector is a constraint based on the API being used, or requirements -- but it's worth noting that vector can be used for the memory management of the implementation -- while still using the raw data (which can be accessed by &vec[0] or vec.data(), which returns a pointer to the first element of the array, and can be used with functions accepting raw pointers).

Since this question is about c++, one option is to wrap an array of n * m in a class that acts like a 2-d array while actually being contiguous.

A simple example could be:

class array_2d
{
public:

   array_2d( std::size_t rows, std::size_t columns )
     : m_rows(rows), m_cols(columns), m_array( new char[rows * columns] )
   {
   }

   ~array_2d()
   {
       delete [] m_array;
   }

   // row-major vs column-major is up to your implementation
   T& operator()( std::ptrdiff_t row, std::ptrdiff_t col )
   {
      // optional: do bounds checking, throw std::out_of_range first

      return m_array[row * m_cols + col];
      // alternatively:
      // return m_array[col * m_rows + row];
   }

   // get pointer to the array (for raw calls)
   char* data()
   {
     return m_array;
   }

private:

   char* m_array; 
   std::size_t m_rows;
   std::size_t m_cols;
};

(Ideally char* would be std::unique_ptr<char[]> or std::vector<char> to avoid memory-leak conditions, but since you said vector is not viable, I'm writing this minimally)

This example overloads the call operator (operator()) -- but this could also be a named function like at(...); the choice would be up to you. The use of such type would then be:

auto array = array_2d(5,5); // create 5x5 array
auto& i01 = array(0,1); // access row 0, column 1

Optionally, if the [][] syntax is important to behave like a 2d-array (rather than the (r,c) syntax), you can return a proxy type from a call to an overloaded operator [] (untested):

class array_2d_proxy
{
public:
   array_2d_proxy( char* p ) : m_entry(p){}

   char& operator[]( std::ptrdiff_t col ){ return m_entry[col]; }

private:

   char* m_entry;
};

class array_2d
{
  ...
  array_2d_proxy operator[]( std::ptrdiff_t row )
  {
    return array_2d_proxy( m_array + (row * m_cols) );
  }
  ...
};

This would allow you to have the 'normal' 2d-array syntax, while still being contiguous:

auto& i00 = array[0][0];
Human-Compiler
  • 11,022
  • 1
  • 32
  • 59
  • This is a very clean solution and appears to work. "Columns" however, is the column size for 1 column, so technically I'll be needing a 3-D array (however, I'll wrap this inside of another class that manages that). Thank you very much, I'll run some tests and report back. – user1324674 Sep 22 '17 at 16:54
2

This is a good way to do it:

void array2d(int m, int n) {
    std::vector<char>  bytes(m * n);
    std::vector<char*> arrays;
    for (int i = 0; i != m * n; i += n) {
        arrays.push_back(bytes.data() + i);
    }
    char** array2d = arrays.data();
    // whatever
}
Charles
  • 1,384
  • 11
  • 18
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 1
    Interesting, I'll try it out. But are vectors contiguous? – user1324674 Sep 21 '17 at 23:05
  • 1
    @user1324674: yes, they are guaranteed to be contiguous since C++11 and there is no popular pre-C++11 implementation which is non-contiguous. – Dietmar Kühl Sep 21 '17 at 23:07
  • 1
    From what I [read](https://stackoverflow.com/questions/849168/are-stdvector-elements-guaranteed-to-be-contiguous#comment657541_849190), C++03 guarantees that. – Jarod42 Sep 21 '17 at 23:27
  • 2
    @Jarod42: yes, you are right. It was not guaranteed for C++98 but already fixed for C++03. Well, these versions somewhat blend together. I found the [proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1998/n1162.pdf) which was done in 1998. – Dietmar Kühl Sep 21 '17 at 23:37
  • I think even in C++98 the various requirements meant that in practice it had to be contiguous, even if that was never actually spelled out until 2003 – Daniel H Sep 22 '17 at 02:24
  • @DanielH: I cannot find a C++98 working draft, but your statement is highly doubtful given that ``deque`` has almost the same requirements. These are slightly stricter which means it cannot guarantee contiguous memory for all allocators. But the only guarantee that ``vector`` has over ``deque`` (e.g. in C++11) is precisely contiguous storage. So it seems that the committee intentionally *allowed* vector to be contiguous in C++98 but did not require it. – Arne Vogel Sep 22 '17 at 09:03
0

The main problem in C++ with "continuous 2d arrays with variable column length" is that an access like myArray[r][c] requires the compiler to know the column size of the type of myArray at compile time (unlike C, C++ does not support variable length arrays (VLAs)).

To overcome this, you could allocate a continuous block of characters, and additionally create an array of pointers, where each pointer points to the begin of a row. With such a "view", you can then address the continuous block of memory indirectly with a myArray[r][c]-notation:

int main() {

    // variable nr of rows/columns:
    int rows = 2;
    int columns = 5;

    // allocate continuous block of memory
    char *contingousMemoryBlock = new char[rows*columns];

    // for demonstration purpose, fill in some content
    for (int i=0; i<rows*columns; i++) {
        contingousMemoryBlock[i] = '0' + i;
    }

    // make an array of pointers as a 2d-"view" of the memory block:
    char **arr2d= new char*[rows];
    for (int r=0; r<rows;r++) {
        arr2d[r] = contingousMemoryBlock + r*columns;
    }

    // access the continuous memory block as a 2d-array:
    for (int r=0; r<rows; r++) {
        for (int c=0; c<columns; c++) {
            cout << arr2d[r][c];
        }
        cout << endl;
    }
}
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58