I have (constant) data like this:
(index) Width Height Scale Name
0 640 360 1 "SD"
1 1080 720 2 "HD"
2 1920 1080 3 "FHD"
So far - I have created structure like this:
struct Resolution
{
int Width;
int Height;
int Scale;
std::string Name;
};
Now I need an object that will let me to do something like this:
int index = 0;
int width = Resolutions[index].Width; // 360
I need enum or some array that will be constant, and accessible without initialization (static?).
For a start as it is constant data I would not use std::string.
But I would do the following:
struct Resolution
{
int Width;
int Height;
int Scale;
const char * Name;
};
struct Resolution Resolutions[] = {
{640, 360, 1, "SD"},
{ 1080, 720, 2, "HD"},
{ 1920, 1080, 3, "FHD"}
};
but on another note I would use lowercase variation for the variable.
You need either std::vector if the elements in Resolutions are not compile-time-constant, or std::array if they are and the collection doesn't need to grow. For example:
#include <array>
…
const std::array<Resolution, 3> Resolutions =
{{ /* Width Height Scale Name */
{ 640, 360, 1, "SD" },
{ 1080, 720, 2, "HD" },
{ 1920, 1080, 3, "FHD" }
}};
If you want the indices to have meaningful names instead of 0, 1, 2, you can make an enum:
enum ResolutionIndex { SD, HD, FHD };
And use it as the array index:
ResolutionIndex index = SD;
int width = Resolutions[index].Width;
This makes the code safer as you can't now do:
ResolutionIndex index = 4;
which would be an invalid index. The valid index values are hard-coded in the enum and the compiler enforces that. If you used int:
int index = 4;
then the compiler can't help you if you give an invalid index.
You can create a class (it's better in C++), and in your main class, a vector of this class, like that:
class Resolution {
public:
Resolution(unsigned int, unsigned int, unsigned int, std::string const &);
~Resolution();
private:
unsigned int Width;
unsigned int Height;
unsigned int Scale;
std::string Name;
};
And in your main class:
class MainClass {
public:
...
private:
...
std::vector<Resolution *> m_res;
};
And in the cpp file:
MainClass::MainClass() {
this->m_res.push_back(new Resolution(640, 360, 1, SD));
this->m_res.push_back(new Resolution(1080, 720, 2, HD));
this->m_res.push_back(new Resolution(1920, 1080, 3, FHD));
}
You can access to an element like that (Sure, you need the getter):
this->m_res[index].getValue();
Related
Im writing a program to control different lights with DMX protocol. Each light has different channels to control different aspects (like Intensity, Color Temperature, colors etc.)
For that i want to create an easy way to put together a profile for each light.
This is what I came up so far. I'm working with an Arduino Due.
When I print out values after initialization, it just prints out 0 . Could somebody help me explain, what im doing wrong here? Or is there a better way to do this?
#include <vector>
struct Channel {
String name;
String unit;
int minValue;
int maxValue;
int color;
int dmxChannels;
int sliderChannels;
}; Channel AladdinBiColor[2], AladdinRGB[6];
class Profile {
public:
std::vector<Channel> channels;
int dmxChannels;
int sliderChannels;
String name[];
String unit[];
int minValue[];
int maxValue[];
int color[];
Profile(Channel* input, int count){
for (int i=0; i<count; i++){
channels.push_back(*input);
input++;
}
}
};
Profile AladdinBiColor_fixture(AladdinBiColor, 2);
Profile AladdinRGB_fixture(AladdinRGB, 6);
in Setup I call this function:
void setup(){
void initializeProfiles();
Serial.println(AladdinBiColor_fixture.channels[0].maxValue);
}
this prints out 0
which looks like this. It initializes the array.
void initializeProfiles(){
AladdinBiColor[0] = {"INT","%", 0, 100, WHITE,2,2};
AladdinBiColor[1] = {"INT","%", 0, 100, WHITE,2,2};
AladdinRGB[0] = {"INT","%", 0, 100, WHITE,2,2};
AladdinRGB[1] = {"INT","%", 0, 100, WHITE,2,2};
AladdinRGB[2] = {"CF","%", 0, 100, WHITE,2,2};
AladdinRGB[3] = {"RED","%", 0, 100, RED,2,2};
AladdinRGB[4] = {"GREEN","%", 0, 100, GREEN,2,2};
AladdinRGB[5] = {"BLUE","%", 0, 100, BLUE,2,2};
}
So the Problem was that the initialization took place after the Profile objects have been created. I rearranged the order and created an array which points to the objects of Profile so one can access it easily.
#include <vector>
struct Channel {
String name;
String unit;
int minValue;
int maxValue;
int color;
};
Channel AladdinBiColor[2] = {
{"INT","%", 0, 100, WHITE},
{"CCT","K", 0, 100, WHITE}
};
Channel AladdinRGB[6] = {
{"INT","%", 0, 100, WHITE},
{"CCT","K", 0, 100, WHITE},
{"CF","%", 0, 100, WHITE},
{"RED","%", 0, 100, RED},
{"GREEN","%", 0, 100, GREEN},
{"BLUE","%", 0, 100, BLUE}
};
class Profile {
public:
std::vector<Channel> channels;
int dmxChannels;
int sliderChannels;
Profile(Channel* input, int count, int dmxChannelsA){
for (int i=0; i<count; i++){
channels.push_back(*input);
input++;
}
dmxChannels = dmxChannelsA;
sliderChannels = count;
}
};
Profile AladdinBiColor_fixture(AladdinBiColor, 2,2);
Profile AladdinRGB_fixture(AladdinRGB, 6, 5);
Profile *lightProfiles[1][3] = { //index 1 = brand, index 2 = light
{&AladdinRGB_fixture, &AladdinBiColor_fixture, &AladdinRGB_fixture}
};
This way - in my main file - i can access the Profile objects with index numbers:
void setup(){
// first parameter of lightProfiles is the brand, second the light of it
Serial.println(lightProfiles[0][1]->channel[0].maxValue);
}
I'm working on an image renderer in C++ that I wrote from scratch (I don't want to use anything but standard libraries), but I'm having some trouble when trying to store the image. The class I use to store images looks like this:
class RawImage
{
private:
RGB pixels[][][3] = {};
public:
int width = 0;
int height = 0;
RawImage(int width, int height)
{
this->width = width;
this->height = height;
};
RGB GetPixel(int x, int y)
{
if (x < 0 || x > width - 1)
return RGB(0.f, 0.f, 0.f);
if (y < 0 || y > height - 1)
return RGB(0.f, 0.f, 0.f);
return pixels[x][y];
};
int SetPixel(int x, int y, RGB color)
{
if (x < 0 || x > width - 1)
return -1;
if (y < 0 || y > height - 1)
return -1;
this->pixels[x][y] = color;
return 0;
}
};
When I try to compile this code, the g++ compiler gives the following error:
declaration of ‘pixels’ as multidimensional array must have bounds for
all dimensions except the first.
How do I use a multidimensional array of which the 2 first dimensions vary in size, but the third dimension is of a fixed size?
Assuming (as you have confirmed in the comments) that your RGB type is a class or structure with three components, with a constructor of the form used in your GetPixel function, then you actually want a 2D array. However (as also mentioned in the comments), it is generally more efficient to store bitmaps as flattened, one-dimensional arrays of size width × height. The appropriate element in that array can then be indexed using the formula array[y * width + x] (assuming a row-major order and y-ordinates that increase down the bitmap).
You still have the issue of a dimension that is not known at compile time, so you can't use a normal array. But the std::vector container is ideal for this: just resize it in your RawImage constructor, and it can then be used in much the same way as a plain array. Also, the memory used will be automatically freed when an object of the RawImage class is destroyed.
Here is a possible implementation of your class using such a std::vector:
#include <vector>
class RawImage {
private:
std::vector<RGB> pixels;
public:
int width = 0;
int height = 0;
RawImage(int width, int height)
{
this->width = width;
this->height = height;
pixels.resize(width * height);
};
RGB GetPixel(int x, int y)
{
if (x < 0 || x >= width )
return RGB(0.f, 0.f, 0.f);
if (y < 0 || y >= height)
return RGB(0.f, 0.f, 0.f);
return pixels[y * width + x];
};
int SetPixel(int x, int y, RGB color)
{
if (x < 0 || x >= width)
return -1;
if (y < 0 || y >= height)
return -1;
pixels[y * width + x] = color;
return 0;
}
};
Important Note: In order to use the std::vector<RGB> container like this, the RGB class/structure must have a default constructor. I don't know exactly how you have implemented that class, but something like the following would work:
struct RGB {
float r, g, b;
RGB(float fr, float fg, float fb) : r{ fr }, g{ fg }, b{ fb } { }
RGB() : r{ 0 }, g{ 0 }, b{ 0 } { } // Default c'tor required by std::vector
};
Or, for brevity, you could 'merge' your default constructor into the one that takes three float arguments by providing default vales for each of those arguments:
struct RGB {
float r, g, b;
RGB(float fr = 0, float fg = 0, float fb = 0) : r{ fr }, g{ fg }, b{ fb } { }
};
Set the bounds of an array after object initialisation in cpp
The size of an array never changes through its lifetime. It's set upon creation. Technically this isn't a problem for you because you can initialise the array in the constructor.
But, size of an array variable must be compile time constant, so you cannot accept the size as a constructor parameter.
You can use a dynamic array. Most convenient way is to use std::vector.
Arrays are not really first size citizens in C++ language, and multi-dimensional arrays are not at all. There is no way to declare a multi-dimensional array where more than first dimension is not a compile time constant, full stop. The rationale is that plain arrays are low level objects and are intended to only be used in higher level containers. Unfortunately, building true multi-level containers wrapping a multidimensional array whose dimension are only known at compile time is far from trivial because of the way iterators work. A simple way if you can accept it, is to use operator () as an accessor method: pixels(x, y) instead of pixels[x][y] in a container aware of the dynamic dimensions.
Im currently making a voxel game like Minecraft for fun with DirectX11.
Game works with chunk system like any other voxel game, but my current algorithm for generating chunk mesh is not expandable.
Block class has a few attributes like is block full and mesh type.
class Block
{
public:
bool isFull = true;
MeshType type = MeshType::FullBlock;
Vector2i texture = { 9, 1 };
Vector2i topTexture = { 9, 1 };
const char* sound;
Block(){}
Block(bool isFull, MeshType type, Vector2i texture, Vector2i topTexture, const char* sound): isFull(isFull), type(type), texture(texture), topTexture(topTexture), sound(sound){}
Block(bool isFull, MeshType type, Vector2i texture, const char* sound) : isFull(isFull), type(type), texture(texture), topTexture(texture), sound(sound) {}
Block(bool isFull, MeshType type, Vector2i texture) : isFull(isFull), type(type), texture(texture), topTexture(texture) {}
};
Every block is then initialised in a vector
blocks.reserve(64);
Block air(false, MeshType::Empty, {0 ,0});
blocks.emplace_back(air);
Block grass(true, MeshType::FullBlock, { 3, 0 }, { 0, 0 }, "Audio/grass1.ogg");
blocks.emplace_back(grass);
Block stone(true, MeshType::FullBlock, { 1, 0 }, "Audio/stone1.ogg");
blocks.emplace_back(stone);
Block rose(false, MeshType::Cross, { 12 ,0 }, "Audio/grass1.ogg");
blocks.emplace_back(rose);
Block wheat(false, MeshType::Hash, { 8 ,3 });
blocks.emplace_back(wheat);
//and so on...
I have a function that accepts a vector of vertices and chunk data.
That function loops through all the blocks with a few optimisations and emplaces data back into the vector that gets sent to the buffer.
for (int x = 0; x < ChunkWidth; x++)
{
for (int y = 0; y < ChunkHeight; y++)
{
for (int z = 0; z < ChunkWidth; z++)
{
if (IsDrawable[x][y][z] == 1)
{
switch (blocks[chunk->BlockID[x + 16 * y + 16 * 256 * z]].type)
{
case MeshType::FullBlock:
BuildBlock(chunk, vertices, x, y, z);
break;
case MeshType::Cross:
FillCross(vertices, blocks[chunk->BlockID[x + 16 * y + 16 * 256 * z]], x + chunk->x * ChunkWidth, y, z + chunk->z * ChunkWidth);
break;
case MeshType::Hash:
FillHash(vertices, blocks[chunk->BlockID[x + 16 * y + 16 * 256 * z]], x + chunk->x * ChunkWidth, y, z + chunk->z * ChunkWidth);
break;
}
}
}
}
}
With every new mesh type the switch statement gets bigger and I think that is not the way to go.
Im asking if there are better ways of doing this.
I thank you in advance.
I think making different derived classes with a common parent class Block is the way to go here. You add a virtual method in it whose behaviour is overridden in the derived classes. Then you place them in a polymorphic vector of std::shared_ptr<Block> and call them. If you are afraid that for some reason this might be too slow you might replace the virtual functions with the Curiously Recurring Template Pattern (CRTP) to achieve static polymorphism. So something like:
Implementatin of the base class Block: Can stay roughly the same bout you add a virtual method draw(...) which is the common interface for all derived classes:
class Block {
public:
bool isFull = true;
Vector2i texture = { 9, 1 };
Vector2i topTexture = { 9, 1 };
const char* sound;
Block() {
return;
}
Block(bool isFull, Vector2i const& texture, Vector2i const& topTexture, const char* sound)
: isFull(isFull), texture(texture), topTexture(topTexture), sound(sound) {
return;
}
Block(bool isFull, Vector2i const& texture, const char* sound)
: isFull(isFull), texture(texture), topTexture(texture), sound(sound) {
return;
}
Block(bool const& isFull, Vector2i const& texture)
: isFull(isFull), texture(texture), topTexture(texture) {
return;
}
// Virtual method that every derived class should override
// Could contain default behaviour but here I declared it as pure virtual method (therefore the = 0)
// Didn't know the data types for chunk and vertices so I used Chunk and Vertices respectively
virtual void draw(Chunk const& chunk, Vertices const& vertices, int x, int y, int z, int chunkWidth) = 0;
};
The different types of blocks are introduced as derived classes that inherit the constructor (or you might implement a new one as well) and override the behaviour of the draw(...) class. If you are not planning to inherit from this derived class then you can mark it as final or if you won't be overriding draw in a derived class you can mark only draw as final
class Empty: public Block {
public:
using Block::Block; // Use the constructor of the base class
// Overwrite behaviour of the base class here
void draw(Chunk const& chunk, Vertices const& vertices, int x, int y, int z, int chunkWidth) override {
return;
}
};
class FullBlock: public Block {
public:
using Block::Block;
void draw(Chunk const& chunk, Vertices const& vertices, int x, int y, int z, int chunkWidth) override {
// Move contents of BuildBlock here
BuildBlock(chunk, vertices, x, y, z);
return;
}
};
class Cross final: public Block {
public:
using Block::Block;
void draw(Chunk const& chunk, Vertices const& vertices, int x, int y, int z, int chunkWidth) override {
// Move contents of FillCross here! No need to pass blocks[i] or rewrite FillCross to take something else than a Block, e.g. a std::shared_ptr<Block>
FillCross(vertices, *this, x + chunk->x * chunkWidth, y, z + chunk->z * chunkWidth);
return;
}
};
class Hash final: public Block {
public:
using Block::Block;
void draw(Chunk const& chunk, Vertices const& vertices, int x, int y, int z, int chunkWidth) override {
// Same here
FillHash(vertices, *this, x + chunk->x * chunkWidth, y, z + chunk->z * chunkWidth);
return;
}
};
Then you add all the blocks as std::shared_ptr or better std::unique_ptr if the resources are not shared! (a wrapper for a plain pointer from #include <memory>)
// Consider using std::unique_ptr if you are not using the individual objects outside of the std::vector
std::vector<std::shared_ptr<Block>> blocks = {};
blocks.reserve(64);
auto air = std::make_shared<Empty>(false, {0 ,0});
blocks.emplace_back(air);
auto grass = std::make_shared<FullBlock>(true, { 3, 0 }, { 0, 0 }, "Audio/grass1.ogg");
blocks.emplace_back(grass);
auto stone = std::make_shared<FullBlock>(true, { 1, 0 }, "Audio/stone1.ogg");
blocks.emplace_back(stone);
auto rose = std::make_shared<Cross>(false, { 12 ,0 }, "Audio/grass1.ogg");
blocks.emplace_back(rose);
auto wheat = std::make_shared<Hash>(false, { 8 ,3 });
blocks.emplace_back(wheat);
You can call then the implementation of the different derived classes as follows:
for (int x = 0; x < chunkWidth; x++) {
for (int y = 0; y < chunkHeight; y++) {
for (int z = 0; z < chunkWidth; z++) {
if (IsDrawable[x][y][z] == 1) {
blocks[chunk->BlockID[x + 16 * y + 16 * 256 * z]]->draw(chunk, vertices, x, y, z, chunkWidth);
}
}
}
}
Here I put together a simplified working example to play around with in an online compiler.
I'm writing a Snake game in C++, I have a structure for a section of the snake which contains, data such as x position, y position, direction etc.
I have it all working, setting all the data to integers, I just would like to change some of the data types to enum's because it looks a lot neater and easier to understand.
I've tried lots and looked online but I can't seem to find anything.
This is some of the Structure:
struct SnakeSection
{
int snakePosX;
int snakePosY;
int SectionType;
// Tail = 0, Body = 1, Head = 2
int animation;
enum Direction
{
Up = 0,
Right = 1,
Down = 2,
Left = 3
};
};
My attempt at trying to pass one of the Directions to another function:
void PlayerSnake::createSnake()
{
// Parameters are direction, x and y pos, the blocks are 32x32
addSection(SnakeSection::Direction::Right, mStartX, mStartY, 2);
}
Then I tried setting the direction to the one passed in in that function:
void PlayerSnake::addSection(SnakeSection::Direction dir, int x, int y, int type)
{
//Create a temp variable of a Snake part structure
SnakeSection bufferSnake;
bufferSnake.Direction = dir;
bufferSnake.animation = 0;
//is it head tail or what? This is stored in the Snake section struct
//TODO Add different sprites for each section
bufferSnake.SectionType = type;
//assign the x and y position parameters to the snake section struct buffer
bufferSnake.snakePosX = x;
bufferSnake.snakePosY = y;
//Push the new section to the back of the snake.
lSnake.push_back(bufferSnake);
}
error: invalid use of enum SnakeSection::Direction
Thanks
The error on the following line ...
bufferSnake.Direction = dir;
... is reasoned, that besides declaring the enum type, you'll still have to have a class member variable to store it:
struct SnakeSection
{
int snakePosX;
int snakePosY;
int SectionType;
// Tail = 0, Body = 1, Head = 2
int animation;
enum Direction
{
Up = 0,
Right = 1,
Down = 2,
Left = 3
};
Direction direction_; // <<<<<<<<<<<<<< THAT'S WHAT'S MISSING IN YOUR CODE
};
And refer to
bufferSnake.direction_= dir; // <<<<<<<<<<<<<< THAT'S THE MEMBER VARIABLE YOU'LL
// HAVE TO REFER TO!
I'm trying to define a struct in C++ that has properties to return pre-defined values of it's own type.
Like many APIs have for Vectors and Colors like:
Vector.Zero; // Returns a vector with values 0, 0, 0
Color.White; // Returns a Color with values 1, 1, 1, 1 (on scale from 0 to 1)
Vector.Up; // Returns a vector with values 0, 1 , 0 (Y up)
Source: http://msdn.microsoft.com/en-us/library/system.drawing.color.aspx
(MSDN's page of their Color type)
I've been trying to search for hours but I can't for the heart of me even figure out what it's called.
//in h file
struct Vector {
int x,y,z;
static const Vector Zero;
};
// in cpp file
const Vector Vector::Zero = {0,0,0};
Like this?
You can mimic it with static members:
struct Color {
float r, g, b;
Foo(float v_r, float v_g, float v_b):
r(v_r), g(v_g), b(v_b){};
static const Color White;
};
const Color Color::White(1.0f, 1.0f, 1.0f);
// In your own code
Color theColor = Color::White;
This is a static property. Unfortunately, C++ does not have properties of any type. To implement this, you probably want either a static method or a static variable. I would recommend the former.
For the Vector example, you would want something like:
struct Vector {
int _x;
int _y;
int _z;
Vector(int x, int y, int z) {
_x = x;
_y = y;
_z = z;
}
static Vector Zero() {
return Vector(0,0,0);
}
}
You would then write Vector::Zero() to get the zero vector.