Initializing static global data using function call (at compile time) - c++

I am trying to save compute time by computing sequences of numbers at compile time and storing them as static vectors (but I might settle for computation once at the beginning of runtime for now). A simple (not compiling) example of what I am trying to do would be:
#include <vector>
using namespace std;
static vector<vector<int> > STATIC_THING(4, vector<int>(4));
void Generator(int x, int y, vector<int> *output) {
// Heavy computing goes here
for(int i=0; i < 4; ++i)
(*output)[i] = x * y;
return;
}
static void FillThings() {
for(int x=0; x < 4; ++x)
for(int y=0; y < 4; ++y)
Generator(x, y, &STATIC_THING[x]);
}
FillThings();
int main() {
}
Is there a way other than precomputing and hardcoding my sequences into arrays to get the compiler to do the lifting on this? I feel like there should be a way to at least get this done upon the first #include of the header this will live in, but I have only seen it done with classes. I can use arrays instead of vectors if it will facilitate computation at compile-time.
EDITS:
Although template metaprogramming was suggested, my actual generator algorithm is far too complex to lend itself to this technique.
Using a Lookup Table seems to be my only other option that will allow me to avoid runtime computation; I will fall back on this if performance continues to be an issue in the future.

Do this:
static int FillThings() {
for(int x=0; x < 4; ++x)
for(int y=0; y < 4; ++y)
Generator(x, y, &STATIC_THING[x]);
return 9087;
}
static int q = FillThings();

If you can't initialize from actual literals via a brace initializer, then you could do something like this:
typename std::vector<std::vector<int>> my_vector;
static my_vector make_static_data()
{
my_vector result;
// ... populate ...
return result;
}
static const my_vector static_data = make_static_data();

Not that easy: std::vector is a dynamic structure. It is not "fillable" ar "compile time". It can be filled in at startup by initializing a static variable with the return of an invoked function, or lambda, that actually fills-up the vector.
this can be a way.
But a proper "compile time vecotr" should look like a template whose "index" is an int given as a parameter, like
template<unsigned idx>
struct THING
{
static const int value = .... //put a costant expression here
};
to be used as THING<n>::value.
The "constant expression" can be a function(THING<idx-1>::value), recursively down to a specialized
temnplate<>
struct THING<0U> {};
That stops the compiler recursion.
There are, however, some limitations: the expression that defines the value static member must be a constexpr (so, only integer types, built-in oerations and no <cmath>, and just function declared with constexpr), and the value used as idx must be itself a constant (not a variable).

Related

Is it possible to pass an array into a function as a parameter without creating a variable for that array?

So I made a function that takes arrays as parameters and I've tried calling the function by passing arrays that have not been defined as variables into said function (like {0,0,0,0}). However, I am given an error which says "too many initializer values."
Say we have a function defined as:
int func(int values[]) {
int average = 0;
for(int x = 0; x < values.size(); x++) {
average += values[x];
}
return average / values.size();
}
And we want to call it without defining an array to pass in like this: func({1,6,7,2});
Is there any way to do something like this or would I have to define an array and pass it into the function that way?
You cannot do that using built-in arrays. The fact that Arrays are neither Assignable nor Copy-able. Also They are not classes so they don't have member functions like size() or they take Initializer-list.
You can achieve that through using std::array if the size is constant or using std::vector if the size if dynamic.
#include <array>
int func(const std::array<int, 5>& values) {
int average = 0;
for (size_t x{}, sz{ values.size() }; x != sz ; ++x)
average += values[x];
return average / values.size();
}
int main() {
auto ret{
func({ 1, 6, 7, 2 })
};
std::cout << ret << std::endl;
}
Also don't mix Unsigned with Signed in calculations like in your loop:
for(int x = 0; x < values.size(); x++) // x is int while values.size() is unsigned int.
int func(const std::array<int, 5>& values): pass by reference to avoid the copy especially if the size is big. Also pass by const as long as the function doesn't intend to change the parameter also another benefit of using const reference is you can pass literals instead of an object.
N.B: I recommend to also to use range-based for because it is really relevant in your example as long as you want to iterate over all the elements and not intending to insert nor to delete elements:
int average = 0;
for (const auto& e : values)
average += e;
Another version of func as #M.M pointed out is to use std::accumalate to do the job for you:
int func(const std::array<int, 5>& values) {
return std::accumulate(values.begin(), values.end(), 0) /
values.size();
}
Using a vector, yes:
#include <vector>
using namespace std;
void f( const vector <int> & v ) {
}
int main() {
f( {1,2,3,4} );
}
Arrays don't work like that. When you pass an array to a function, the address of the first element gets passed like a pointer, and inside the function there is no more information about the size of the array. (Before the compiler itself could infer the size because the array was declared in the scope, but a function can be called from any number of places)
If you want to do something like that you would either have to use a container class, such as a vector, or you could pass a second argument into the function stating the size of the array. Another way is to have some sort of end point in your array, such as is the case with c-strings, for example a null value.

Dynamic, multidimensional, rectangular, numeric arrays in C++ (as in NumPy or Matlab)

In Matlab or NumPy it's very easy to create numerical arrays which are rectangular, multidimensional and dynamic. Those classes also have nice indexing functionality. Furthermore, they have data stored in one linear buffer.
I'm looking for something similiar in C++, syntax could be for example:
DoubleArray arr(size_x, size_y);
arr[x][y] = 5;
double * ptr = arr.getRawData() // returns the underlying linear storage
I think C++ does not offer anything built-in to do so. The only library I know is Eigen, but it has the drawback that matrices/arrays are always 2-dimensional.
Is there a good and easy way to achieve what I want? Most important is that I do not have to mess around manually with indexing, and that data is stored in one buffer (vs. vector of vectors).
I would guess that C++ does not offer a built-in multidim array, because it is rather easy to write one but how it should be implemented depends on your requirements. I was curious on how one could get such a multidimensional array and came up with this:
template <int DIMS>
struct multidimarray {
typedef int value_type;
value_type* data;
int* dimensions;
multidimarray(int* dims,value_type* d) : dimensions(dims),data(d) {}
multidimarray<DIMS-1> operator[](int index){
int s = 1;
for (int i=1;i<DIMS;i++){ s *= dimensions[i];}
return multidimarray<DIMS-1>(dimensions+1,data+s*index);
}
};
template <> struct multidimarray<1> {
typedef int value_type;
value_type* data;
int* dimensions;
multidimarray(int* dims,value_type* d) : dimensions(dims),data(d) {}
value_type operator[](int index){ return *(data+index); }
};
It is not the most efficient implementation, at least the size of the subarrays should not be computed for each access. Also it would be more convenient to use, if a wrapper was added that handles creation and deletion of the data. However, it seems to work (no guarantee, not more tested than with the following code):
#include <vector>
#include <iostream>
int main(){
int imax = 4;
int jmax = 4;
int kmax = 3;
std::vector<int> d;
d.push_back(imax); d.push_back(jmax);d.push_back(kmax);
std::vector<int> data;
for (int i=0;i<100;i++){data.push_back(i);}
multidimarray<3> md = multidimarray<3>(&d[0],&data[0]);
for (int i=0;i<imax;i++){
for (int j=0;j<jmax;j++){
for (int k=0;k<kmax;k++){
std::cout << md[i][j][k] << std::endl;
}
}
}
}
Sorry for the lack of auto and brace initialization, but this is pre-C++11.
Oh well, and I just realized that the multidimarray<1>::operator[] should of course return a reference instead of a value.
As I mentioned above, requirements may wildly differ for your specific application. Nevertheless, I hope this helps ;)

How to have a 2-dimensional array as a private variable in a class, and then set it in the constructor

In my platformer game which I'm writing in Visual C++, each level will initially be stored as a 2-dimensional array of ints. I decided it would make more sense to store this array in a class, so I created a class called Level. It looks like this:
class Level {
private:
int map[20][30];
public:
Level(int a[20][30]) {
map = a;
}
int getcell(int row, int column) {
return map[row][column];
}
};
As far as I can see - from looking up tutorials on class constructors, and passing 2-dimensional arrays as parameters, this should work, so I really don't understand why it doesn't.
On the line where I do map = a, I get an error: Error: expression must be a modifiable lvalue. I've looked this error up on stackoverflow, but I can't find any answers which relate to my problem.
So, how can I fix this error?
This doesn't really have anything to do with a constructor. You cannot assign arrays in C++. Whether in the constructor, or anywhere else.
There are two ways to work around it. The first way is the brute force way. Instead of
map = a;
write a loop to copy the contents of the array from the constructor's parameter into the class member array.
The second way is to stuff the array into an intermediate class:
class Level {
public:
struct level_map {
int map[20][30];
};
private:
level_map map;
public:
Level(const level_map &initial_map) : map(initial_map)
{
}
int getcell(int row, int column) {
return level_map.map[row][column];
}
};
This may or may not be practical, and introduces a little bit more complexity.
But the real answer here is to use std::vectors instead of plain arrays, which will solve all of these problems.
Others have already mentioned the real reason: you cannot assign an array to another using = operator. My two cents about your class:
map is not a good name, it may get conflict with std::map if using namespace std; or using std::map was specified somewhere.
The constant array sizes make this class non-reusable. Class should be flexible to allow any N*M sized 2D array. For this, better to use vector<vector<int>>.
getcell should be a const method, and it should do error checking with row and column numbers passed.
If you want this class to have static-sized array sizes and compile time, you may use class templates with row and column sizes as non type template arguments.
template<size_t row, size_t column>
class Level
{
int _map[row][column];
public:
Level(int src[row][column])
{
memcpy(_map, src, sizeof(_map)); // why not simply 'memcpy' ?
}
};
int main()
{
int source[10][2] = { {1, 2}, {3,4} };
Level<10, 2> ten_by_2(source);
}
Here the map is a constant value, which could not been assigned as an lvalue. This could be fixed by iterating the element of the array, and assign a[i][j] to map[i][j].
class Level {
private:
int map[20][30];
public:
Level(int a[20][30]) {
for(int i = 0; i < 20; ++i)
for(int j = 0; j < 30; ++j)
map[i][j] = a[i][j];
}
int getcell(int row, int column) {
return map[row][column];
}
};

How to solve the issue passing an unknown-sized 2D array to a function?

I have a program in which the object array's size is determined during the runtime, so it's dynamically allocated (2D array, read from file). I also have a function which takes these objects as parameters. The problem is if the function parameters are 2D arrays that are passed to the function the 2nd dimension should be determined. However, in my case it is not. My program won't compile since the prototype does not have the 2nd dimension mentioned.
Here is what I tried:
//global variables
int residentCount=0;
int hospitalCount=0;
Resident** residents;
Hospital** hospitals;
bool readFromFiles(const string, const string, const int); //sizes are determined in here
void print(Hospital*[hospitalCount], Resident*[residentCount]); //declaration issue
How can I solve this?
You are programming in C++, so you should:
avoid dynamic allocation and handling memory management on your own always when it's possible
take advantage of objects with automatic storage duration instead, follow RAII idiom
avoid using C-style arrays and actually avoid writing C code that is just compilable as C++ in general
use great features that C++ provides, especially those bundled within STL
avoid using global variables when local equivalents suffice
This is how it could look like:
typedef std::vector<Resident> Residents;
typedef std::vector<Hospital> Hospitals;
// passing by const reference:
void print(const std::vector<Hospitals>&, const std::vector<Residents>&);
int main()
{
std::vector<Hospitals> hospitals;
std::vector<Residents> residents;
...
} // <-- lifetime of automatics declared within main ends here
Note that hospitals and residents will be objects with automatic storage duration, usable in similar manner than your C-style 2D arrays. When the execution goes out of the scope of main, these vectors are destructed and memory, where their elements (including elements of their elements) resided before is automatically cleaned up.
Also note that I suggest you to pass by const reference, i.e. const std::vector<Hospitals>&, which prevents the copy of passed object being created and const keyword explicitely tells to the caller: "Although you pass this object by reference, I will not change it."
Just pass a pointer to the first element of the array and the dimensions, that's enough, example:
void PrintHospitals(Hospital* Hospitals, size_t HospitalRows, size_t HospitalColumns)
{
size_t i, j;
Hospital* hospital;
for (i = 0; i < HospitalRows; i++)
for (j = 0; j < HospitalColumns; j++)
{
hospital = Hospitals + HospitalColumns * i + j;
PrintHospital(hospital);
}
}
int main()
{
Hospital hospitals[10][20];
// ...
PrintHospitals(&hospitals[0][0], 10, 20);
return 0;
}
Here is a solution using templates to create two-dimensional array wrappers for existing data:
template<typename T>
class Array2d {
public:
int Rows;
int Cols;
T** Data;
Array2d(int rows, int cols, T** data) :
Rows(rows),
Cols(cols),
Data(data) { }
};
void print(Array2d<Hospital> h, Array2d<Resident> r) {
for (int i = 0; i < h.Rows; i++) {
for (int j = 0; j < h.Cols; j++) {
//Print Data[i][j] element here
}
}
// Other print code
}
int main()
{
Resident** residents;
Hospital** hospitals;
//Init data arrays
Array2d<Hospital> h(10, 10, hospitals);
Array2d<Resident> r(10, 10, residents);
print(h, r);
}

C++ metaprogramming with templates versus inlining

Is it worth to write code like the following to copy array elements:
#include <iostream>
using namespace std;
template<int START, int N>
struct Repeat {
static void copy (int * x, int * y) {
x[START+N-1] = y[START+N-1];
Repeat<START, N-1>::copy(x,y);
}
};
template<int START>
struct Repeat<START, 0> {
static void copy (int * x, int * y) {
x[START] = y[START];
}
};
int main () {
int a[10];
int b[10];
// initialize
for (int i=0; i<=9; i++) {
b[i] = 113 + i;
a[i] = 0;
}
// do the copy (starting at 2, 4 elements)
Repeat<2,4>::copy(a,b);
// show
for (int i=0; i<=9; i++) {
cout << a[i] << endl;
}
} // ()
or is it better to use a inlined function?
A first drawback is that you can't use variables in the template.
That's not better. First of all, it's not really compile time, since you make function calls here. If you are lucky, the compiler will inline these and end up with a loop you could have written yourself with much less amount of code (or just by using std::copy).
General rule: Use templates for things known at compile time, use inlining for things known at run time. If you don't know the size of your array at compile time, then don't be using templates for it.
You shouldn't do this. Templates were invented for different purpose, not for calculations, although you can do it. First you can't use variables, second templates will produce vast of unused structures at compilation, and third is: use for (int i = start; i <= end; i++) b[i] = a[i];
That's better because you control and enforce the loop unrolling by yourself.
A loop can be unrolled by the compiler depending on optimizing options...
The fact that copying with copy is almost the best is not a good general answer because the loop unrolling can be done whatever is the computation done inside...