c++ working with 2d arrays - c++

Currently I'm learning C++ and I've been trying to create a simple image processing library for learning purposes.
One of my features is eroding an image. As input it has two 2d arrays, and it returns another 2d array. All these arrays are of variable size. Let's give you an example of what I want to achieve.
int image[5][5] =
{
{'0', '0', '0', '0','0'},
{'0', '1', '1', '1','0'},
{'0', '0', '1', '0','0'},
{'0', '1', '1', '1','0'},
{'0', '0', '1', '0','0'},
};
int kernel[3][3] =
{
{'0', '1', '0'},
{'1', '1', '1'},
{'0', '1', '0'},
};
Then I want to pass them to my function (this doesn't compile, but it serves as an example of what I want).
int** erode(int image[][], int kernel[][]);
So far, I've read quite a bit about this. All I've read is that the columns can be of variable length, but the rows can't. So I should be change it to the following:
int** erode(int image[][5], int kernel[][3]);
But that's not really want I want either, because well, the image can be 10*10 and the kernel could be 5*5. So this isn't optimal in this situation either.
Then what I've read is creating a class, that internally stores the image as a 1d array and makes it look like a 2d array. Also I've read about using the Boost.MultiArray class to do this. But well I'm not too happy about that either. Because then I'm forcing the people that use it to also use those classes. And I think it's creating a lot of complexity for something that seems really simple (at least it is in C#)
To be honest, I can't imagine there isn't an easier way to do this. Optimally I'd say only use classes/methods from the standard C++11 library. How would you solve this problem?
Timo

see if you can use templates, eg:
template<int N>
void f(int (&arr)[N][N]);
if arrays have compile time bounds.
but really you should consider boost, armadillo, or tvmet

I would pass the array as a pointer and also pass another parameter which defines the size of the array.
int** erode(int* image, size_t imageSize, int* kernel, size_t kernelSize);
An array is just a list of consecutive variables when in memory and the array variable such as image[5][5] just points to the first element of the array. To pass the arrays you have shown into the function you would use.
int** ret = errode(image, 5, kernel, 3);
Be careful when using this however because it could easily cause a segmentation fault but when used correctly, it will be fine. Also this assumes that the array is square, to overcome this limitation just pass in another parameter which describes the other dimension of the array.
Update
I see Anycorn has posted a very nice solution. His is a safer solution however will require you knowing the size of the array at compile time. If this is the case I would recommend you use what he has said.

Returning a 2D vector is less complicated.
#include <vector>
#include <iostream>
using namespace std;
typedef std::vector<std::vector< int> > vector2D;
int image[5][5] =
{
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0},
};
int kernel[5][5] =
{
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 1, 0, 0},
};
vector2D erode(int image[5][5], int kernel[5][5])
{
vector2D image_process_data ;
//create 2D vector array
image_process_data.resize(5);
for(int i = 0; i < 5; i++)
{
image_process_data[i].resize(5);
}
//perform calculations
for (int ix = 0; ix < 5; ix++)
{
for (int iy = 0; iy < 5; iy++)
{
image_process_data[ix][iy] = image[ix][iy] + kernel[ix][iy];
}
}
//return the 2D array
return image_process_data;
}
int main( )
{
vector2D new_image;
new_image = erode(image, kernel);
//display new_image
for (int ix = 0; ix < 5; ix++)
{
for (int iy = 0; iy < 5; iy++)
{
cout<<new_image[ix][iy]<<" ";
}
cout<<"\n";
}
return 0;
}
How to set up a 2D vector array:
#include <vector>
#include <iostream>
using std namespace;
#define HEIGHT 5
#define WIDTH 3
int main() {
vector<vector<double> > array2D;
// Set up sizes. (HEIGHT x WIDTH)
array2D.resize(HEIGHT);
for (int i = 0; i < HEIGHT; ++i)
array2D[i].resize(WIDTH);
// Put some values in
array2D[1][2] = 6.0;
array2D[3][1] = 5.5;
return 0;
}

What you wrote about internally storing 1D array is essentially the best solution. Using 2-dimensional array is usually dangerous and can easily lead to errors. If you don't want to create whole class, consider:
const int SizeX = 10, SizeY = 10;
int Array[SizeX*SizeY];
inline int& Get (int x, int y) { return Array[SizeX*y+x]; }
Of course, it is the very simple example, but usually solves a lot of errors.
struct MyOwnArray
{
int SizeX, int SizeY;
int* Data;
inline int& Get (int x, int y) { return Data[SizeX*y+x]; }
}
The code above will allow you to pass references to this struct in a convenient way. And it's only 6 lines of code!
Of course, the code above would need some more code; for example for allocating memory. However, if you carefully program all these parts, you will be pretty safe to use it and it should be well-protected from bugs.

Related

Iterating the creation of objects in C++

I want to be able to create N skyscrapers. Using an inputdata string, I would like to give them coordinate values of their X and Y positions. My main function I used "i" to demonstrate that I am trying to create as many skyscrapers as I can using the input data. Essentially, I would like to create N/3 skyscrapers and assign the input to coordinates for each.
#include <iostream>
#include <vector>
#include <string>
#include <math.h>
using namespace std;
vector<int> inputData = {1, 4, 10, 3, 5, 7, 9, 10, 4, 11, 3, 2, 14, 5, 5};
int N = inputData.size();
class Buildings{
public:
int yCoordinateLow;
int yCoordinateHigh;
int xCoordinateLeft;
int xCoordinateRight;
};
int main(){
for(int i=0; i<N; i=i+3){
Buildings skyscraper;
skyscraper.xCoordianteLeft = inputData.at(i);
skyscraper.yCoordianteLow = 0;
skyscraper.yCoordinateHigh = inputData.at(i+1);
skyscraper.xCoordinateRight = inputData.at(i+2);
}
return 0;
}
Jeff Atwood once said: use the best tools money can buy. And those aren't even expensive: Visual Studio community edition is free. Such a proper IDE will tell you that the skyscraper is unused except for the assignments.
Since you probably want to do something with those skyscrapers later, you should store them somewhere, e.g. in another vector.
int main() {
vector<Buildings> skyscrapers;
for (int i = 0; i < N; i = i + 3) {
Buildings skyscraper{};
skyscraper.xCoordinateLeft = inputData.at(i);
skyscraper.yCoordinateLow = 0;
skyscraper.yCoordinateHigh = inputData.at(i + 1);
skyscraper.xCoordinateRight = inputData.at(i + 2);
skyscrapers.push_back(skyscraper);
}
return 0;
}
Other than that, I'd say the loop works fine as long as there are N*3 coordinates in the original vector.
If you e.g. implement a game, you would probably not hard code the skyscraper coordinates in a vector but rather read that data from a file, potentially per level.
Instead of doing all the error-prone coding, maybe you want to initialize the skyscrapers immediately
vector<Buildings> skyscrapers = {{1,0,4,10}, {3,0,5,7}, {9,0,10,4}, {11,0,3,4}, {14,0,5,5}};

Easy way to create multiple-dimension arrays in C++ with new operator

I need to create a lot of small 2-dimension arrays in C++ code.
The problem is that it's a lot of work to create even a simple array:
new int* [2]{
new int[2]{9, 9},
new int[2]{25, 19}
};
Is there any better way how to do that?
I wanted to avoid writing "new int[]..." every time.
If the dimensions are not decided at runtime, and all the inner arrays have the same dimensions, you do not need dynamic allocation here.
Just declare an array:
int myArray[2][2] = {
{9, 9},
{25, 19}
};
That's it!
I recommend allocating as a single dimension array. You can then treat the 1D array as a 2D array:
const unsigned int MAX_ROWS = 2U;
const unsigned int MAX_COLUMNS = 5U;
int example_array[MAX_ROWS * MAX_COLUMNS];
// Get value at [row][column]:
unsigned int one_dim_index = (row * MAX_COLUMNS) + column;
int value = example_array[one_dim_index];
For small array sizes, this would be more efficient since the processor can fit the entire contiguous array in the data cache. With your solution, an array of pointers, you have no idea where the sub-arrays are located and they may not be contiguous (thus requiring a refetch into the cache).
Edit 1: Initializing
You can initialize the array by making the rows and columns pretty:
int example_array[MAX_ROWS * MAX_COLUMNS] =
{
/* row 0 */ 1, 2, 3, 4, 5,
/* row 1 */ 6, 7, 8, 9, 10,
};
Maybe you can use nested for loops to do the task
const int ARRAY_SIZE = 2;
int **create_array() {
int **array = new int*[ARRAY_SIZE];
if (array == nullptr) { return nullptr; }
for (int i=0; i<ARRAY_SIZE; i++) {
array[i] = new int[ARRAY_SIZE];
if (array[i] == nullptr) { return nullptr; }
}
return array;
}
If you want to assing the values you can do it directly in here. But they should come from a function of i. If you want some really specific values it will need to be a manual job

C++ Problem filling structs with values from another struct

I'm having an issue trying to figure out why I am not getting the correct functionality with a piece of code. I have looked around to try and find a solution however, I haven't been able to do so. Below is an example of my code:
//Structs
typedef struct
{
int gene[60];
int fitness;
} individual;
typedef struct
{
int cond[5];
int out;
}rule;
//Array of individuals
individual population[P]
int function(individual solution){
int k = 0;
//Array of rules
rule rulebase[10]
for (int i = 0; i < 10; i++){
for (int j = 0; j < 5; j++){
rulebase[i].cond[j] = solution.gene[k++];
}
rulebase[i].out = solution.gene[k++];
}
for (int i = 0; i < 5; i++){
cout << rulebase[0].cond[i];
}
The solution that is passed into the function is the first individual in 'population' and the gene array contains only binary numbers, for example:
gene = [0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1] //There will be 60 in total
The desired functionality is to fill the rule structures in the rulebase with the values found in the solution. For example, using the example above the first rule in the rulebase will have the values below in the 'cond' array:
[0, 0, 1, 0, 1]
and the 'out' will be the next integer in the solution:
[1]
Then the next rule will be filled with the next values in the solution the same way.
The problem that I am having is the code seems to be filling the 'cond' array of each rule with all of the values in the solution, as oppose to the desired way described above. For example, when I print the genes in 'rulebase[0]' I get:
[0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1]
As oppose to:
[0, 0, 1, 0, 1]
I can't seem to figure out why I am getting this problem as the code looks to me like it should work? Any help would be greatly appreciated as I am seriously struggling!
A rule contains only 5 values in cond, not 10 as you show. Its just your code that prints the values of rulebase[0] that is wrong, i.e. it exceeds array bounds and prints - in addition to the cond-values of rulebase[0] - the values of out and cond of the next rule, which - in memory - come next.

Initializing std::vector with a repeating pattern

I'm working with OpenGL at the moment, creating a 'texture cache' which handles loading images and buffering them with OpenGL. In the event an image file can't be loaded it needs to fall back to a default texture which I've hard-coded in the constructor.
What I basically need to do is create a texture of a uniform colour. This is not too difficult, it's just an array of size Pixels * Colour Channels.
I am currently using a std::vector to hold the initial data before I upload it OpenGL. The problem I'm having is that I can't find any information on the best way to initialize a vector with a repeating pattern.
The first way that occurred to me was to use a loop.
std::vector<unsigned char> blue_texture;
for (int iii = 0; iii < width * height; iii++)
{
blue_texture.push_back(0);
blue_texture.push_back(0);
blue_texture.push_back(255);
blue_texture.push_back(255);
}
However, this seems inefficient since the vector will have to resize itself numerous times. Even if I reserve space first and perform the loop it's still not efficient since the contents will be zeroed before the loop which means two writes for each unsigned char.
Currently I'm using the following method:
struct colour {unsigned char r; unsigned char g; unsigned char b; unsigned char a;};
colour blue = {0, 0, 255, 255};
std::vector<colour> texture((width * height), blue);
I then extract the data using:
reinterpret_cast<unsigned char*>(texture.data());
Is there a better way than this? I'm new to C/C++ and I'll be honest, casting pointers scares me.
Your loop solution is the right way to go in my opinion. To make it efficient by removing repeated realloc calls, use blue_texture.reserve(width * height * 4)
The reserve call will increase the allocation, aka capacity to that size without zero-filling it. (Note that the operating system may still zero it, if it pulls the memory from mmap for example.) It does not change the size of the vector, so push_back and friends still work the same way.
You can use reserve to pre-allocate the vector; this will avoid the reallocations. You can also define a small sequence (probably a C style vector:
char const init[] = { 0, 0, 255, 255 };
and loop inserting that into the end of the vector:
for ( int i = 0; i < pixelCount; ++ i ) {
v.insert( v.end(), std::begin( init ), std::end( init ) );
}
this is only marginally more efficient than using the four push_back in the loop, but is more succinct, and perhaps makes it clearer what you're doing, albeit only marginally: the big advantage might be being able to give a name to the initialization sequence (eg something like defaultBackground).
The most efficient way is the way that does least work.
Unfortunately, push_back(), insert() and the like have to maintain the size() of the vector as they work, which are redundant operations when performed in a tight loop.
Therefore the most efficient way is allocate the memory once and then copy data directly into it without maintaining any other variables.
It's done like this:
#include <iostream>
#include <array>
#include <vector>
using colour_fill = std::array<uint8_t, 4>;
using pixel_map = std::vector<uint8_t>;
pixel_map make_colour_texture(size_t width, size_t height, colour_fill colour)
{
// allocate the buffer
std::vector<uint8_t> pixels(width * height * sizeof(colour_fill));
auto current = pixels.data();
auto last = current + pixels.size();
while (current != last) {
current = std::copy(begin(colour), end(colour), current);
}
return pixels;
}
auto main() -> int
{
colour_fill blue { 0, 0, 255, 255 };
auto blue_bits = make_colour_texture(100, 100, blue);
return 0;
}
I would reserve the entire size that you need and then use the insert function to repeatedly add the pattern into the vector.
std::array<unsigned char, 4> pattern{0, 0, 255, 255};
std::vector<unsigned char> blue_texture;
blue_texture.reserve(width * height * 4);
for (int i = 0; i < (width * height); ++i)
{
blue_texture.insert(blue_texture.end(), pattern.begin(), pattern.end());
}
I made this template function which will modify its input container to contain count times what it already contains.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename Container>
void repeat_pattern(Container& data, std::size_t count) {
auto pattern_size = data.size();
if(count == 0 or pattern_size == 0) {
return;
}
data.resize(pattern_size * count);
const auto pbeg = data.begin();
const auto pend = std::next(pbeg, pattern_size);
auto it = std::next(data.begin(), pattern_size);
for(std::size_t k = 1; k < count; ++k) {
std::copy(pbeg, pend, it);
std::advance(it, pattern_size);
}
}
template<typename Container>
void show(const Container& data) {
for(const auto & item : data) {
std::cout << item << " ";
}
std::cout << std::endl;
}
int main() {
std::vector<int> v{1, 2, 3, 4};
repeat_pattern(v, 3);
// should show three repetitions of times 1, 2, 3, 4
show(v);
}
Output (compiled as g++ example.cpp -std=c++14 -Wall -Wextra):
1 2 3 4 1 2 3 4 1 2 3 4

How to initialize 3D array in C++

How do you initialize a 3d array in C++
int min[1][1][1] = {100, { 100, {100}}}; //this is not the way
The array in your question has only one element, so you only need one value to completely initialise it. You need three sets of braces, one for each dimension of the array.
int min[1][1][1] = {{{100}}};
A clearer example might be:
int arr[2][3][4] = { { {1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4} },
{ {1, 2, 3, 4}, {1, 2, 3, 4}, {1, 2, 3, 4} } };
As you can see, there are two groups, each containing three groups of 4 numbers.
Instead of static multidimensional arrays you should probably use one-dimensional array and calculate the index by multiplication. E.g.
class Array3D {
size_t m_width, m_height;
std::vector<int> m_data;
public:
Array3D(size_t x, size_t y, size_t z, int init = 0):
m_width(x), m_height(y), m_data(x*y*z, init)
{}
int& operator()(size_t x, size_t y, size_t z) {
return m_data.at(x + y * m_width + z * m_width * m_height);
}
};
// Usage:
Array3D arr(10, 15, 20, 100); // 10x15x20 array initialized with value 100
arr(8, 12, 17) = 3;
std::vector allocates the storage dynamically, which is a good thing because the stack space is often very limited and 3D arrays easily use a lot of space. Wrapping it in a class like that also makes passing the array (by copy or by reference) to other functions trivial, while doing any passing of multidimensional static arrays is very problematic.
The above code is simply an example and it could be optimized and made more complete. There also certainly are existing implementations of this in various libraries, but I don't know of any.
Here's another way to dynamically allocate a 3D array in C++.
int dimX = 100; int dimY = 100; int dimZ = 100;
int*** array; // 3D array definition;
// begin memory allocation
array = new int**[dimX];
for(int x = 0; x < dimX; ++x) {
array[x] = new int*[dimY];
for(int y = 0; y < dimY; ++y) {
array[x][y] = new int[dimZ];
for(int z = 0; z < dimZ; ++z) { // initialize the values to whatever you want the default to be
array[x][y][z] = 0;
}
}
}
Everyone seems to forget std::valarray. It's the STL template for flat multidimensional arrays, and indexing and slicing them.
http://www.cplusplus.com/reference/std/valarray/
No static initialization, but is that really essential?