C++ Initializing a 2D array of structs - c++

I'm writing a program that allows the user to play sudoku puzzles. Each puzzle is a 2d array of structs, but I'm unable to intialize the array in one go. Is there any way to do this without needing to intialize each part of the structure for each element of the array? Here is some of my code.
class Sudoku {
protected:
struct Puzz {
int Ans;
// IsClue is used further in the program so the user cannot change the clues given.
bool IsClue;
};
Puzz Puzzle[9][9]; // I chose a 2D array so represent the 9 rows and columns of a sudoku puzzle
};
I've tried some of the following:
Puzzle[][9] = {{0, false, 8, true, 0, false, 6, true, 7, true, 0, false, 0, false, 0, false, 0, false}, //...etc, going on for each row
Puzzle[9][9] = // etc, etc, but I get an error saying "Expected an expression
Is the only way to initialize each element to do
Puzzle[x][y].Ans = x; Puzzle[x][y].IsClue = true/false;?

The problem is that you try to assign to an array, and it's simply not possible. You need to actually initialize it, which is something completely different.
If you don't mind writing lot of lines that are almost the same, then you could use a constructor member initializer list. Like e.g.
Sudoku::Sudoku()
: Puzzle{{...}, {... }, ... }
{}
If only a few structures in the arrays differ from some default values, then you could set all entries in the arrays to the same default value, using e.g. std::fill and then change those few entries that have special values.

Here's an example showing how a 2 x 2 array can be initialized. I'll leave it to you to extend that to a 9 x 9 array.
class Sudoku {
Sudoku();
protected:
struct Puzz {
int Ans;
bool IsClue;
};
Puzz Puzzle[2][2];
};
Sudoku::Sudoku() : Puzzle{{{0, false}, {1, true}}, {{2, false}, {3, true}}}
{
}
Having said that, it might be easier to create a default constructor of Puzz which does the sensible thing for most objects and then modify only a select elements of Puzzle in Sudoku's constructor.
class Sudoku {
Sudoku();
protected:
struct Puzz {
int Ans;
bool IsClue;
Puzz() : Ans(0), IsClue(false) {}
};
Puzz Puzzle[2][2];
};
Sudoku::Sudoku()
{
// Change specific elements of Puzzle
}

You can initialize with an initializer list in c++11 or higher:
Puzz Puzzle[2][2] = {
{ {8, true}, {9, false} },
{ {10, true}, {11, false} }
};
If in a class you can default construct it the same way:
class PuzzHolder {
PuzzHolder() : Puzzle{
{ {8, true}, {9, false} },
{ {10, true}, {11, false} }
} { }
Puzz Puzzle[2][2];
};
Although, if it is very big, this might get pretty messy. So the final approach could be to initialize it in a loop.
Live example.

To initialize a two-dimensional array, use a for-loop. As in:
for (int i = 0; i < row_count; i++) {
for (int j = 0; j < col_count; j++) {
item[i][j] = initial_value;
}
}
If you want to use an initializer list like in the above, you can do so by simply wrapping each instance of the struct in brackets. However, initializing your sodoku board with a literal is not a particularly scalable approach to generating a sodoku board for the player. That is, instead of:
Puzzle puzz[9][9] = {{0, false, 8, true, 0, false, 6, true, 7, true, 0, false, 0, false, 0, false, 0, false, ...}};
You would use:
Puzzle puzz[9][9] = {
{ // First row
{0, false}, // first column
{8, true}, // second column
...
},
{ // Second row
...
},
...
};
Since this is C++, though, you may wish to consider representing your board using nested vectors (that is vector<vector<Cell>>). While this may be overkill, especially for a Sodoku board (especially if it is always 9x9), that will allow you to more easily represent non-standard board sizes, and the vector data structure comes with the ability to default initialize/fill all members with a pre-specifed value. As #paddy aptly notes in the comments, however, there are also other functions (like std::fill that can be used with ordinary arrays to handle common use cases.

Related

Unite elements that share the same value of a variable in a vector of structs

For example, I have this struct :
struct Time
{
char Day[10];
int pay;
int earn;
}
And suppose that the vector of this Time struct has the following elements:
vector<Time> mySelf = ({"Monday", 20, 40}, {"Tuesday", 15, 20}, {"Monday", 30, 10}, {"Tuesday", 10, 5});
So is there any algorithm to unite the data so that elements with the same day name will appear once and the other variables of those elements will combine together to form a new vector like this :
vector<Time> mySelf = ({"Monday", 50, 50}, {"Tuesday", 25, 25});
You can try to insert your elements to unordered_map, and then reconstruct a vector. Search and insertion to the map have constant-time complexity, so all the operation will be O(n), because we need to iterate over a vector twice.
std::unordered_map<std::string, Time> timeMap;
for (const auto& t : mySelf)
{
if (timeMap.count(t.day) == 0)
{
timeMap[t.day] = t;
}
else
{
timeMap[t.day].pay += t.pay;
timeMap[t.day].earn += t.earn;
}
}
or shorter version, since insert already checks if the element exists and will not overwrite it:
for (const auto& t : mySelf)
{
timeMap.insert({t.day, {t.day,0,0}});
timeMap[t.day].pay += t.pay;
timeMap[t.day].earn += t.earn;
}
and then the vector reconstruction:
std::vector<Time> result;
result.reserve(timeMap.size());
for (const auto&[key, val] : timeMap)
{
result.push_back(val);
}
Alternatively you could use std::unordered_set but then you need some hash function for your struct. Probably you could improve it further with move semantics.
live demo

Comparing arrays with comparison operator

This is just a small question, more aimed at understanding the usage of arrays than at solving a difficult problem.
I currently have an array of four integers (Neighbors) that I'd like to compare against a set of other arrays (which don't exist anywhere else - I have no need to store them). I want to know which of the four arrays Neighbors is identical to. As somebody who doesn't know any better, my first attempt was to do this:
if (Neighbors == {1, 1, 0, 0})
{
//code...
}
else if (Neighbors == {0, 1, 1, 0})
{
//code...
}
else if (Neighbors == {0, 0, 1, 1})
{
//code...
}
else if (Neighbors == {1, 0, 0, 1})
{
//code...
}
As you can see, the order of the integers is important. However, the above returned compiler errors about expecting primary expressions before curly-brace tokens.
So instead, I tried this:
int Sets[4][4] = { {1, 1, 0, 0}, {0, 1, 1, 0}, {0, 0, 1, 1}, {1, 0, 0, 1} };
if (Neighbors == Sets[0])
{
//code...
}
else if (Neighbors == Sets[1])
{
//code...
}
else if (Neighbors == Sets[2])
{
//code...
}
else if (Neighbors == Sets[3])
{
//code...
}
What happens here is that even when Neighbors = {0, 1, 1, 0} (for example), Neighbors == Sets[1] returns false.
Now, after doing this and wondering why, I remembered that the array variable is basically a pointer to the first element in a sequence. Right? So I think I get why the above code doesn't work - I'm comparing two memory addresses, rather than two arrays. So instead I wrote this code, which works fine:
for (int ii = 0; ii < 4; ++ii)
{
bool Same = true;
for (int jj = 0; jj < 4; ++jj)
{
if (Neighbors[jj] != Set[ii][jj])
{
Same = false;
}
}
if (Same == true)
{
//code...
}
}
What I want to know is whether there is a way to compare arrays like this without going through two for-loops. It seems like something that ought to be simpler than this. I know a for-loop isn't especially intensive when you've only got 4 values, but I still would have thought it'd be easier to determine whether two arrays contain identical information. If each array is a contiguous block of memory, I would have thought you could just look at those two blocks and check whether they are identical (which is basically what the for-loops are doing, although that requires doing it manually).
So is there a way to compare the content of arrays directly, preferably with a single line of code? If not, why not? I'd like to understand the science behind this issue.
You have tagged the question C++. Which means you should be using std::vector. It has overloaded operator== that does what you want (for two vectors).
You can also use std::equal or std::lexicographical_compare for anything you have iterators for, which includes primitive arrays.
Of course you can also overload the operator== for other things. Unfortunately you can't overload it for primitive arrays, because overloading operators is only allowed if at least one argument is a class (or struct) type. But you could override it to compare vector with array. Something like:
template<typename T, typename Alloc, size_t S>
bool operator==(std::vector<T, Alloc> v, const T (&a)[S])
{
return v.size() == S && std::equal(v.begin(), v.end(), a);
}
(this takes reference to array not degraded to pointer to check it's declared size first and is therefore safe)
Of course all these methods have a loop hidden inside that compares the elements one by one. But you don't have to write it.
The C++iest way to do this is with std::equal :
#include <algorithm>
With C++11:
if (std::equal(begin(Neighbors), end(Neighbors), begin(Sets[0]))
{ /* then they're equal */ }
With C++03:
if (std::equal(Neighbors, Neighbors + 4, Sets[0]))
{ /* then they're equal */ }
You can use memcmp function. If arrays are equal it returns 0. Here is a description: http://www.cplusplus.com/reference/clibrary/cstring/memcmp/

C++ Class, Assigning values during Constructor initialization

I have a 2D array which I declared part of the classes private members. When I call the constructor, I start assigning values to the 2D array. But every time I do so, I'm hit with an error C2059. To make sure nothing else was causing that error I commented out that line and the compiler finished putting together a binary file.
tried:
Variable[row] = { 0, 1, 2, 3};
Variable[row][] = { 0, 1, 2, 3};
Variable[row][4] = { 0, 1, 2, 3};
No luck, any clues. Thanks in advance.
This syntax is only to be used for the creation of the object.
int array[4] = {1, 2, 3, 4};
Once the array is created, you have to use a loop to assign values to it.
Here's a short example :
class A
{
int array[4];
public:
A()
{
// Here, array is already created
// You can _assign_ values to it
}
};
If you want to give it values when it's instantiated in the constructor, the only way is to use initialization lists. Unfortunatly, you can't do this with a static array.
See this this thread.
Unfortunately, we can't yet properly initialize arrays that are members of classes. I don't know exactly how yours is declared, but here's an example of what to do:
class X
{
int Variable[3][4];
public:
X()
{
const int temp[][4] = { { 1, 2, 3, 4}, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
const int sz = sizeof(Variable)/sizeof(**Variable);
std::copy(*temp, (*temp) + sz, *Variable);
}
};
Since your question is not clear enough, all I can do is demonstrating a simple example.
2D array is initialized as,
//you may "optionally" provide the size of first dimension
int arr[][4] = {
{1,2,3,4},
{11,12,13,14},
{21,22,23,24}
};
And is acessed as,
for ( int i = 0 ; i < 3 ; ++i )
{
for ( int j = 0 ; j < 4 ; ++j )
{
cout << arr[i][j] << endl;
}
}
Online demonstration at ideone : http://www.ideone.com/KmwOg
Are you doing similarly?

Can I cause a compile error on "too few initializers"?

I am using an aggregate initializer to set up a block of static data for a unit test.
I would like to use the array size as the expected number of elements, but this can fail if too few initializers are provided:
my_struct_type expected[14] =
{
{ 1.234, 0, 'c' },
{ 3.141, 1, 'z' },
{ 2.718, 0, 'a' }
};
This gives no compiler error in Visual Studio 2008.
I would like to be able to use it as such:
const unsigned expected_size = sizeof(expected) / sizeof(my_struct_type);
BOOST_CHECK_EQUAL(points.size(), expected_size);
for( int i = 0; i < expected_size; i++ )
{
BOOST_CHECK_EQUAL(points[i].value, expected[i].value);
BOOST_CHECK_EQUAL(points[i].count, expected[i].count);
BOOST_CHECK_EQUAL(points[i].sym, expected[i].sym);
}
but because I don't have a compile-time guarantee of 14 points, this runs off the end of the array end of the provided values and into the default-initialized values.
Can I somehow enforce the number of aggregate array initializers at compile-time?
Actually it won't run off the end of the array, because the compiler will default-initialize all the elements of the array that you didn't initialize yourself.
If you're trying to make sure that you have a specific number of configured initializers, I'm not sure how to do that.
If you just want to make sure the array is the number of items you have:
my_struct_type expected[] =
{
{ 1.234, 0, 'c' },
{ 3.141, 1, 'z' },
{ 2.718, 0, 'a' }
};
Will do the trick. Then just use sizeof(expected) / sizeof(expected[0]) to get the total number of array elements.
First: There might be a warning for this. Have you tried compiling at the highest warning level?
Then: If you swap which value is calculated and which is literal, you could raise a compile-time error:
my_struct_type my_array[] = // <== note the empty []
{
{ 1.234, 0, 'c' },
{ 3.141, 1, 'z' },
{ 2.718, 0, 'a' }
};
BOOST_STATIC_ASSERT( sizeof(my_array)/sizeof(my_array[0]) == 14 );
Just for the sake of a non-Boost answer…
You can add an initialization requirement by modifying my_struct_type.
template< typename T >
struct must_be_initialized {
T value;
must_be_initialized( T const &v ) : value( v ) {}
// no default constructor!
operator T& () { return value; }
operator T const& () const { return value; }
};
struct my_struct_type {
must_be_initialized< double > f;
int i;
char c;
};
my_struct_type expected[14] =
{
{ 1.234, 0, 'c' },
{ 3.141, 1, 'z' },
{ 2.718, 0, 'a' }
// error: no default constructor exists
};
my_struct_type is still an aggregate, but it is not POD.
ISO/IEC 14882 (First edition 1998-09-01) in p. 8.5.1.7 states the following:
If there are fewer initializers in the
list than there are members in the
aggregate, then each member not
explicitly initialized shall be
default-initialized (8.5). [Example:
struct S { int a; char* b; int c; }; S
ss = { 1, "asdf" }; initializes ss.a
with 1, ss.b with "asdf", and ss.c
with the value of an expression of the
form int(), that is, 0. ]
Simply, the answer to your question is no.
According to the msdn, if fewer initializers are specified, the remaining elements are initialized with 0, so the code should work nonetheless.

What is the easiest way to set the value of an entire array?

My current project requires me to fill an array based upon some other values. I know there's the shortcut:
int arr[4][4] = { {0,0,0,0} , {0,0,0,0} , {0,0,0,0} , {0,0,0,0} };
But in this case, I need to fill the array after its declaration. I currently have my code formatted like this:
int arr[4][4];
if(someothervariable == 1){
arr = { {1,1,1,1},
{1,2,3,4},
{2,,3,4,5},
{3,4,5,6} };
}
But it won't compile. Is there a way to make use of the mentioned shortcut in my case? If not, whats the best fix available? I'd appreciate a way to set it without explicitly assigning each element? ie: arr[0][0] = ...
How about using std::copy() ?
int arr[4][4];
if(someothervariable == 1){
const static int a2[4][4] = { {1,1,1,1},
{1,2,3,4},
{2,3,4,5},
{3,4,5,6} };
std::copy(&a2[0][0], &a2[0][0]+16, &arr[0][0]);
}
No, array initialization syntax is for array initialization. Although, you can use memset if all the values are the same byte.
The boost.assign library adds some interesting syntax for modifying/filling collections, but AFAIK it doesn't support C style arrays (only C++ and Boost containers).
In the current version of C++ language the only way to do it is to copy it from some original
int arr[4][4];
if (someothervariable == 1)
{
const int SOURCE[4][4] = // make it `static` if you prefer
{
{1, 1, 1, 1},
{1, 2, 3, 4},
{2, 3, 4, 5},
{3, 4, 5, 6}
};
assert(sizeof arr == sizeof SOURCE); // static assert is more appropriate
memcpy(&arr, &SOURCE, sizeof arr);
}
The source "constant" can be declared as static in order to avoid re-initialization, if the compiler is not smart enough to optimize it by itself.
In the future version of the language a feature similar to C's compound literals is planned, which will provide support for immediate initialization (basically what you tried to do in your original post).
If you wish to fill the array with a single value:
#include<algorithm>
#include<vector>
// ...
std::vector<int> arr;
std::fill(arr.begin(), arr.end(), VALUE); // VALUE is an integer
If you wish to calculate the value for each element:
struct get_value {
int operator()() const { /* calculate and return value ... */ }
};
std::generate(arr.begin(), arr.end(), get_value());
If you are setting everything to the same value (such as zero), you may be able to get away with ...
memset (arr, 0, sizeof (arr));
Note that this is fraught with perils. You have to know your type sizes and all that jazz.
However, it appears that that will not suffice for you. If you want to fill the array with different values, I can only only think of two ways of doing this.
Method #1. (Can be a pain the butt)
arr[0][0] = 1;
...
arr[0][3] = 1;
arr[1][0] = 1;
...
arr[1][3] = 4;
arr[2][0] = 2;
...
arr[2][3] = 5;
arr[3][0] = 3;
...
arr[3][3] = 6;
Method #2.
Predefine a set of arrays and switch between them using a pointer;
int arr1[4][4] = {
{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{0,0,0,0} };
int arr2[4][4] = {
{1,1,1,1},
{1,2,3,4},
{2,,3,4,5},
{3,4,5,6} };
int *arr[4];
Now you only have the four (4) values of *arr[] to set instead of setting everything. Of course, this really only works if your arrays will be filled with predetermined constants.
Hope this helps.
I'm not sure if I like this solution or not, but C/C++ will give you assignment convenience if you wrap the array inside a struct with the minor cost of then having to use the struct name to get at the array:
typedef struct {
int data[4][4];
} info_t;
info_t arr;
if (someothervariable == 1){
static const info_t newdata = {{ // since this is static const, there generally
// won't be a copy - that data will be 'baked'
// into the binary image (or at worst a
// single copy will occur)
{1,1,1,1},
{1,2,3,4},
{2,3,4,5},
{3,4,5,6}
}};
arr = newdata; // easy to assign new data to the array
}
int somethingelse = arr.data[1][2]; // a tiny bit less convenient to get
// to the array data
int arr[4][4];
if (someothervariable == 1) {
int tmp[4][4] = { {1, 1, 1, 1}, {1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6} };
arr = tmp;
}