Copy Dynamic Struct Array into another C++ - c++

I'm not that good in English, that's why my Question is probably wrong. But I have a problem and I don't know how to solve it or if it's even possible to do.
I have 2 Structs defined:
typedef struct
{
UINT16 ScriptNumber;
std::string ScriptText;
} StepStruct;
typedef struct
{
std::string SequenceName;
std::string DisplayName;
StepStruct SequenceSteps;
} SequenceStruct;
As you can see, the first Struct is a Member of the second struct. So I want both structs to by dynamical. So I created 2 Dynamic Arrays from the Type StepStruct and 1 dynamic Array from the Type SequenceStruct.
The two dynamical Arrays for of the Type StepStructs are defined as follows:
StepStruct gsFirstSkript[] =
{
{ 1 , "SkriptText One"},
{ 2 , "SkriptText Two"},
{ 45, "SkriptText Three"}
}
StepStruct gsSecondSkript[] =
{
{ 48, "SkriptText One"},
{ 2 , "SkriptText Two"},
{ 45, "SkriptText Three"}
}
Those to Structs are of the Type StepStruct. Now I want to do the Same with a SequenceStruct Type, but I want to assign the two Arrays I already have to it under the Struct Member SequenceSteps. I mean this as follows:
SequenceStruct gsSequenceList[] =
{
{ "FirstScript", "Test One", gsFirstSkript},
{ "SecondScript", "Test Two", gsSecondSkript}
}
If I now want to Read the Member gsSequenceList, I can not access any information under the SequenceSteps Index of it! What means, that the Data is not copied! I tried it with Pointers
but had no success.
UINT16 lTestVal = gsSequenceList[0].SequenceSteps[2].ScriptNumber;
So Can I mangage that this works, and lTestVal contains the Value 45?

typedef struct
{
std::string SequenceName;
std::string DisplayName;
StepStruct* SequenceSteps;
} SequenceStruct;
This will allow the code to compile and the test fragment you've shown will work.
However this will not copy the data. If you change gsFristSkript it will change in gsSequenceList as well. If you want to make a copy of the data you can either do that explicitly, have a constructor or just use vector<>.
Here's the solution with vector:
#include <vector>
...
typedef struct{
std::string SequenceName;
std::string DisplayName;
vector<StepStruct> SequenceSteps;
} SequenceStruct;
vector<StepStruct> gsFirstSkript =
{
{ 1 , "SkriptText One"},
{ 2 , "SkriptText Two"},
{ 45, "SkriptText Three"}
}
vector<StepStruct> gsSecondSkript =
{
{ 48, "SkriptText One"},
{ 2 , "SkriptText Two"},
{ 45, "SkriptText Three"}
}
SequenceStruct gsSequenceList[] =
{
{ "FirstScript", "Test One", gsFirstSkript},
{ "SecondScript", "Test Two", gsSecondSkript}
}

Related

How to programmatically assign class/struct attributes?

I am attempting to translate my Python program to C++, but because I am new to C++ I am encountering some problems. The input file is first parsed (works, not shown) to create the INITIAL_VALUES dict/map, which I then want to use to assign the Parameters class/struct attributes using the DEST_DICT_PARAMS dict/map.
I was able to achieve this in Python code with:
import dataclasses
INITIAL_VALUES = {
"BULK": {
"MAGMA": {
"M0": 1.0,
"T0": 1320.0,
},
"ASSIM": {
"M0": 0.0,
"T0": 600.0,
},
}
}
DEST_DICT_PARAMS = {
'M0': {"MAGMA": 'Mm0', "ASSIM": 'Ma0'},
'T0': {"MAGMA": 'Tm0', "ASSIM": 'Ta0'},
}
#dataclasses.dataclass
class Parameters:
Mm0: float = None
Ma0: float = None
Ta0: float = None
Tm0: float = None
class ParametersReader:
def __init__(self):
self.parameters = Parameters()
self._assignParameters()
def _assignParameters(self):
for param_fam, dest in DEST_DICT_PARAMS.items():
for component, param in dest.items():
value = INITIAL_VALUES["BULK"][component][param_fam]
setattr(self.parameters, param, value)
params = ParametersReader()
print(params.parameters)
Output:
Parameters(Mm0=1.0, Ma0=0.0, Ta0=600.0, Tm0=1320.0)
So I wrote the corresponding C++ code:
#include <iostream>
#include <map>
using std::map;
using std::string;
map<string, map<string, map<string, float> > > INITIAL_VALUES = {{
"BULK", {
{"MAGMA", {
{"M0", 1.0},
{"T0", 1320.0},
}},
{"ASSIM", {
{"M0", 0.0},
{"T0", 600.0},
}},
}
}};
map<string, map<string, string> > DEST_DICT_PARAMS = {{
{"M0", {{"MAGMA", "Mm0"}, {"ASSIM", "Ma0"}}},
{"T0", {{"MAGMA", "Tm0"}, {"ASSIM", "Ta0"}}},
}};
struct Parameters {
float Mm0;
float Ma0;
float Ta0;
float Tm0;
} parameters;
class ParametersReader {
public:
void assignParameters_() {
map<string, map<string, string> >::iterator itr0;
map<string, string>::iterator itr1;
for (itr0 = DEST_DICT_PARAMS.begin(); itr0 != DEST_DICT_PARAMS.end(); itr0++) {
for (itr1 = itr0->second.begin(); itr1 != itr0->second.end(); itr1++) {
parameters.itr1->second = INITIAL_VALUES["BULK"][itr1->first];
}
}
}
};
int main() {
ParametersReader params;
params.assignParameters_();
}
But I'm getting an error at the line
parameters.itr1->second = INITIAL_VALUES['BULK'][itr1->first] saying "no member named 'itr1' in 'Parameters'". That error makes total sense because the code is literally trying to interpret 'itr1' as an attribute name and not the whole 'itr1->second' as the name. I think this comes down to the fact that I can't seem to find a C++ equivalent to Python's setattr(obj, name, val) function that takes an object and its attribute name and assigns it a value. Is there a C++ solution to what I am attempting?
Perhaps my entire approach is incompatible with C++. If so, would you kindly suggest an alternative approach? I would like to keep the input file format the same between the Python and C++ versions.
C++ does not have runtime reflection like Python. You cannot look up a class member by name using a runtime string because class member names do not exist at runtime.
What you can do is look up a class member via a pointer to member. This is an offset into the object calculated at compile time by the compiler:
std::map<std::string, std::map<std::string, float Parameters::*> > DEST_DICT_PARAMS = {{
{"M0", {{"MAGMA", &Parameters::Mm0}, {"ASSIM", &Parameters::Ma0}}},
{"T0", {{"MAGMA", &Parameters::Tm0}, {"ASSIM", &Parameters::Ta0}}},
}};
class ParametersReader {
public:
void assignParameters_() {
for (auto& [param_fam, dest] : DEST_DICT_PARAMS) {
for (auto& [component, param] : dest) {
parameters.*param = INITIAL_VALUES["BULK"][component][param_fam];
}
}
}
};
Demo
Note I've also used range-based for loops and structured bindings to clean up your assignParameters_ function.
C++ has no equivalent to Pythons setattr(self.parameters, param, value). If you want to have a mapping between strings and members you need to write it yourself.
You can use pointers to members to do something along the line of:
#include <map>
#include <iostream>
struct foo {
float a = 0.0f;
float b = 0.0f;
};
// list all members and their string represenation
std::map<std::string,float foo::*> mmap {
{ "a", &foo::a },
{ "b", &foo::b }
};
int main() {
foo f;
std::map<std::string,float> values {
{"a", 0.1},
{"b", 0.2}
};
for (const auto& val : values) {
// lookup the member pointers via the strings from mmap
// use the found function pointer to assing to corresponding member in f
f.*mmap[val.first] = val.second;
}
std::cout << f.a << " " << f.b << "\n";
}
Note that the code assumes that all strings present in values are also present in mmap. If not, it will fail horribly. To fix that mmap.find should be used instead and the case of not found string handleted appropriately.
This works, though there is no way to get that mapping implicitly from the class definition only. On the other, hand I can imagine libraries to exist that can help with that.

Read from json file value in a vector

a want to read in a vector of a struct element from a json.
JSON:
A: [
{ "list" :[
"a" : 2,
"b" : 4,
"c" : 9
]
}
my vector is : std::vector< structE > vec;
structE{ "a", "b", "c" }
if (A[i].isMember("list")) // &&
{
auto const list= A[i]["list"];
for (auto i2 = 0u; i2 < list.size(); i2++)
{
vec.push_back(list[i2]["list"]);
}
}
I have this error: matching function for call to 'std::vector ::push_back(const Json::Value&)'
I have this error: matching function for call to 'std::vector ::push_back(const Json::Value&)'
From your source code, it appears that you might be using JsonCpp to handle Json in your C++ code, when dealing with Json::Value objects, for basic types such as int and std::string, you can use Json::Value::asInt() and Json::Value::asString() respectively, however, for structs, you need to manually extract the values and construct an object of the struct with those values:
struct E {
int a, b, c;
};
// ...
E createEFromJsonValue(Json::Value const& obj) {
// Create an object of struct E with values taken from the JSON object
return {
obj.get("a", Json::nullValue).asInt(),
obj.get("b", Json::nullValue).asInt(),
obj.get("c", Json::nullValue).asInt(),
};
}
Then you can do:
// ...
vec.push_back(createEFromJsonValue(list[i2]["list"]));
// ...

Arduino compile error while using reference to a struct in another struct

I'm on prgramming a menu for an 16x2 LCD for a small arduino project.
I'm nearly finished, but i do not understand the last small issue.
The following (simplified) code generates the problem:
int var1=0;
int var2=0;
typedef struct {
unsigned int item_value;
char item_name[17];
} Select_Item;
typedef struct {
char item_name[17];
int* variable;
Select_Item* list;
} Menu_Item;
Select_Item sel_one = { 0, "Selection 1" };
Select_Item sel_two = { 1, "Selection 2" };
Select_Item* sel_list[2] = { &sel_one, &sel_two };
Menu_Item menu_item1 = { "Item 1", &var1, NULL };
Menu_Item menu_item2 = { "Item 2", &var2, &sel_list };
Menu_Item* menu_list[2] = { &menu_item1, &menu_item2 };
It ends up with the following error:
sketch_feb08a.ino:24:53: error: cannot convert 'Select_Item* (*)[2]' to 'Select_Item*' in initialization
In the code i'm accessing the values from the variables and show it in the display and once edited i can write it back to the variable. That was not the problem as long as i had just numbers to show/edit.
Now for ease of use i wanted to add some kind of option menu, where the user can choose from the options. The item_name should be displayed, instead of the raw value, but of course the item_value should be used behind the scene.
That is why i introduced the Select_Item struct.
I don't understand the error message. What's wrong here?
I think what you're trying to do is something like this:
int var1=0;
int var2=0;
typedef struct {
unsigned int item_value;
char item_name[17];
} Select_Item;
typedef struct {
char item_name[17];
int* variable;
Select_Item* list;
} Menu_Item;
Select_Item sel_one = { 0, "Selection 1" };
Select_Item sel_two = { 1, "Selection 2" };
Select_Item sel_three = {2, "Selection 3" };
Select_Item sel_four = {3, "Selection 4" };
Select_Item* sel_list_1[] = { &sel_one, &sel_two };
Select_Item* sel_list_2[] = { &sel_three, &sel_four };
Menu_Item menu_item1 = { "Item 1", &var1, sel_list_1[0] }; // Note the syntax here
Menu_Item menu_item2 = { "Item 2", &var2, sel_list_2[0] }; // Note the syntax here
Menu_Item* menu_list[2] = { &menu_item1, &menu_item2 };
// Added for testing
int main()
{
printf("Menu item '%s'\n", menu_list[0]->item_name);
printf(" %d - %s\n", menu_list[0]->list[0].item_value, menu_list[0]->list[0].item_name);
printf(" %d - %s\n", menu_list[0]->list[1].item_value, menu_list[0]->list[1].item_name);
printf("Menu item '%s'\n", menu_list[1]->item_name);
printf(" %d - %s\n", menu_list[1]->list[0].item_value, menu_list[1]->list[0].item_name);
printf(" %d - %s\n", menu_list[1]->list[1].item_value, menu_list[1]->list[1].item_name);
return 0;
}
Note the syntax for the initialization of menu_item1 and menu_item2.
I added main() just so I could test this with a normal C compiler (since I don't have an Arduino with an LCD handy). The output from my testing looks like this:
Menu Item 'Item 1'
0 - Selection 1
1 - Selection 2
Menu Item 'Item 2'
3 - Selection 3
4 - Selection 4
And I think that's what you're trying to achieve with your data structures. You'll just need to adapt this to your Arduino code and use these data structures to manage the menu selections on your LCD.

c++ set value of a class with a pointer of another class

I am trying to implement the game Deal or no deal, i have two classes, Box & Player. in box is stored the vector game_box and in Player I want to store the box and the money that the player has to keep till the end of the game.
I have tried to implement it in this way. it runs correctly, without no errors, but when i try to verify if the values were stored into the setter, it just give me that the setter is empty. I really dont understand why! does anybody knows why?
class Box
{
vector<Box> game_box;
float pound_contained;
int box_number;
public:
Box(int box_number, float pound_contained);
Box();
int getbox_number();
float getpound_contained();
};
class Player :public Box
{
int player_box;
float player_money;
public:
Player();
~Player();
float getplayer_money();
int getplayer_box();
void setplayer_money(float);
void setplayer_box(int);
};
void Player::setplayer_money(float)
{
player_money = player_money;
}
void Player::setplayer_box(int)
{
player_box = player_box;
}
float Player::getplayer_money()
{
return this->player_money;
}
int Player::getplayer_box()
{
return this->player_box;
}
int main()
{
vector<Box> game_box;
srand(time(0));
int n;
Player money;
Box oper;
float myArray [22][2] = { { 0.01, 0 }, { 0.10, 0 }, { 0.50, 0 },
{ 1, 0 }, { 5, 0 }, { 10, 0 },
{ 50, 0 }, { 100, 0 }, { 250, 0 },
{ 500, 0 }, { 750, 0 }, { 1000, 0 },
{ 3000, 0 }, { 5000, 0 }, { 10000, 0 },
{ 15000, 0 }, { 20000, 0 }, { 35000, 0 },
{ 50000, 0 }, { 75000, 0 }, { 100000, 0 },
{ 250000, 0 }
};
//random assignation of pound value to the 22 boxes
for (int e = 1; e <22; e++)
{
int pos;
bool op = true;
while (op)
{
pos = rand() % 22 + 1;
if (myArray[pos][1] == 0)
{
myArray[pos][1] = 1;
op = false;
}
}
Box b(e, myArray[pos][0]); //creating the class game
game_box.push_back(b); //function of the vector to insert a data in it
}
// random assignment of a box to the player
int i = rand() % 22 + 1;
Box* boxes = &game_box[i];
cout << "Your box is going to be the box number: "<< boxes->getbox_number()<<endl;
////////////////////////////////////////////////////////////////////setter not working
money.setplayer_money(boxes->getpound_contained());
money.setplayer_box(oper.getbox_number());
cout << money.getplayer_box() << " " << money.getplayer_money() << endl << endl;
game_box.erase(game_box.begin()+i);
return 0;
}
There are not "setters" or "getters" in C++ that are distinct from other method calls. So you write them just as you would any other method.
To elaborate on the problem pointed out by #maniek, you wrote:
void Player::setplayer_money(float)
{
player_money = player_money;
}
C and C++ have the ability to specify method and function arguments without names--just types. This might seem a little strange as there is no way to access that parameter (at least not a way that is guaranteed to work in all compilers or optimization levels, you can possibly do it by messing with the stack). So what you are doing here is just setting the member player_money to itself.
(Note: If you are wondering why it allows you to specify a method argument without a name, it has a few uses...one is that not naming it suppresses warning messages that you aren't using that parameter. So it is a way of marking something as not being used at the current time, yet still required--perhaps for legacy reasons or perhaps because you might use it in the future.)
You can give a new name to the parameter that doesn't overlap with the name of the member variable:
void Player::setplayer_money(float new_player_money)
{
player_money = new_player_money;
}
That's one way of avoiding ambiguity. Because in terms of what value is in scope, the parameter will win over the member variable. So this would be another do-nothing operation, that would assign the parameter value to itself:
void Player::setplayer_money(float player_money)
{
player_money = player_money;
}
(Note: Since player_money is passed by value and not by reference, that wouldn't change the parameter's value at the calling site. In particular, how could it change the value, if you passed in a constant like 10.20.)
What #maniek suggested is that a way to disambiguate in that case is to use this->player_money when you mean the member variable and player_money when you mean the argument to the method. Another thing some people do is name their member variables specially--like start them with m_ as in m_player_money:
void Player::setplayer_money(float player_money)
{
m_player_money = player_money;
}
(Note: You can also just use an underscore prefix with no m as long as the next character is lowercase, but some people consider that too dangerous as underscores followed by capital letters are reserved for compiler internal usages.)
As a final thought--if the class name is Player then it's already implicit whose money you are setting (the player's) so you could just call it set_money. Furthermore, I'm not a fan of underscores in names (more common in C than C++) so I'd probably call it setMoney.
How about:
void Player::setplayer_money(float player_money)
{
this->player_money = player_money;
}
?

C++ Minecraft2D Block Type?

I was working on a Minecraft2D kind of game in Java and I decided to create the same game in C++ to enhance my C++ abilities. But I have a problem. I had a BlockType enum in Java which contained that BlockType's image location and hardness (how long it takes to mine it). I figured out that in C++ enums are different than the ones in Java. How can I implement this in C++?
BlockType.java:
public enum BlockType {
STONE("res/blocks/stone.png",3),
COAL("res/blocks/coal.png", 2),
AIR("res/blocks/air.png",0),
GRASS("res/blocks/grass.png",1),
DIRT("res/blocks/dirt.png",1),
DIAMOND("res/blocks/diamond.png",5),
REDSTONE("res/blocks/redstone.png",3),
COBBLE("res/blocks/cobble.png",3),
BRICK("res/blocks/brick.png",4),
IRON("res/blocks/iron.png",4),
GOLD("res/blocks/gold.png",5);
public final String location;
public final int hardness;
BlockType(String location, int hardness){
this.location = location;
this.hardness = hardness;
}
}
I'll go with something similar to SingerOfTheFall answer:
enum blocks
{
STONE,
COAL,
GOLD
};
struct BlockType {
BlockType(std::string loc, int h): location(loc), hardness(h) {}
std::string location;
int hardness;
};
BlockType blockTypes[] = {
BlockType("res/blocks/stone.png", 3), // STONE
BlockType("res/blocks/coal.png", 2), // COAL
BlockType("res/blocks/gold.png", 5) // GOLD
};
// use:
cout << "Location: " << blockTypes[STONE].location << endl;
std::map is a good container, but it uses binary search every time you need to get the value. Indices will be from 0 to n so you can use array instead.
A possibility would be use a std::map, keyed by an enum value and with a value of std::pair<sd::string, int>:
#include <string>
#include <map>
#include <utility>
enum BlockType
{
STONE,
COAL,
GOLD
};
std::map<BlockType, std::pair<std::string, int>> BlockTypes;
BlockTypes[STONE] = std::make_pair(std::string("res/blocks/stone.png"), 3);
BlockTypes[COAL] = std::make_pair(std::string("res/blocks/coal.png"), 2);
BlockTypes[GOLD] = std::make_pair(std::string("res/blocks/gold.png"), 5);
C++ enums work another way indeed.
enum eMyEnum
{
ONE = 15,
TWO = 22
};
is about all you can get from them, basically they just allow you to create 'names' for INT values.
For your case, I would make an enum for the block names:
enum blocks
{
STONE,
SAND,
<...>
};
and then make a map:
< blocks, pair< string, int > >
^ ^ ^ ^
| | | |
| | | hardness
| | path to picture
| |
| the block's attributes: the picture path and hardness
|
the block type from the enum (e.g. SAND)
Or just make a structure to hold three values:
struct block
{
string type;//or int, or your enum type, depending on how do you want to store it.
string picture;
int hardness;
}
I would combine both the answers here and make a mapping of enum to block struct
struct Block
{
block(path, str) : strength(str), path(path) {}
int str;
std::string path;
};
enum BlockType
{
STONE,
COAL,
ETC
}
std::map<BlockType, Block> blocks;
blocks[STONE] = Block("c:/block/bla.png", 1);
blocks[STONE].str; // 1
blocks[STONE].path; // "c:/block/bla.png"
Why use and std::map whan an array will do? (Which can be initialized at compile time)
using namespace std;
struct BlockType {
enum {
STONE = 0,
COAL,
LAST
};
BlockType(string location, int hardness) : location(location), hardness(hardness) {}
const string location;
const int hardness;
static const BlockType Blocks[LAST];
};
const BlockType BlockType::Blocks[] = {
BlockType("res/blocks/stone.png", 3),
BlockType("res/blocks/coal.png", 2)
};
int main() {
cout << BlockType::Blocks[BlockType::STONE].location << `\n`;
return 0;
}