How to programmatically assign class/struct attributes? - c++

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.

Related

Use initializer list to initialize new vector

So I have this big map of vectors, you can see I use an initializer list to initialize the vectors.
However, although this works it really doesn't seem like it should, if you create an empty vector and try to use the = operator and then assign it to an initializer list you get the error "too many values in initializer list" but for some reason when you try to do the same with a map of vectors it just... works. However the issue is of course these are allocated on the stack and quickly causes a stack overflow so the goal would be to find a way to use initializer list (or something of similar brevity) to initialize these vectors on the heap.
Example code causing stack over flow below
#include <map>
#include <vector>
#include <string>
struct IVector2 {
int x;
int y;
};
struct animation_frame_t {
IVector2 position;
IVector2 size;
};
std::map<std::string, std::vector<animation_frame_t>> animations;
void initAnimations() {
animations["boss_andromeda_attack_"] = { {{303,101},{100,100}}, {{606,101},{100,100}}, {{606,0},{100,100}}, {{505,909},{100,100}}, {{505,808},{100,100}}, {{505,707},{100,100}}, {{505,606},{100,100}}, {{505,505},{100,100}}, {{505,404},{100,100}}, {{505,303},{100,100}}, {{505,202},{100,100}}, {{505,101},{100,100}}, {{505,0},{100,100}}, {{404,909},{100,100}}, {{404,808},{100,100}}, {{404,707},{100,100}}, {{404,606},{100,100}}, {{404,505},{100,100}}, {{404,404},{100,100}}, {{404,303},{100,100}}, {{404,202},{100,100}}, {{404,101},{100,100}}, {{404,0},{100,100}} };
animations["boss_andromeda_breathing_"] = { {{303,909},{100,100}}, {{303,808},{100,100}}, {{303,707},{100,100}}, {{303,606},{100,100}}, {{303,505},{100,100}}, {{303,505},{100,100}}, {{303,404},{100,100}}, {{303,303},{100,100}}, {{303,202},{100,100}}, {{606,202},{100,100}} };
animations["boss_andromeda_death_"] = { {{303,0},{100,100}}, {{202,909},{100,100}}, {{202,808},{100,100}}, {{202,707},{100,100}}, {{202,606},{100,100}}, {{202,505},{100,100}}, {{202,404},{100,100}}, {{202,303},{100,100}}, {{202,202},{100,100}}, {{202,101},{100,100}}, {{202,0},{100,100}}, {{101,909},{100,100}}, {{101,808},{100,100}} };
animations["boss_andromeda_hit_"] = { {{303,0},{100,100}}, {{202,909},{100,100}}, {{202,808},{100,100}} };
animations["boss_andromeda_idle_"] = { {{101,707},{100,100}}, {{101,606},{100,100}}, {{101,505},{100,100}}, {{101,404},{100,100}}, {{101,303},{100,100}}, {{101,202},{100,100}}, {{101,101},{100,100}}, {{101,0},{100,100}}, {{0,909},{100,100}}, {{0,808},{100,100}} };
animations["boss_andromeda_run_"] = { {{0,707},{100,100}}, {{0,606},{100,100}}, {{0,505},{100,100}}, {{0,404},{100,100}}, {{0,303},{100,100}}, {{0,202},{100,100}}, {{0,101},{100,100}}, {{0,0},{100,100}} };
animations["boss_antiswarm_attack_"] = { {{393,1310},{130,130}}, {{917,655},{130,130}}, {{917,524},{130,130}}, {{917,393},{130,130}}, {{917,262},{130,130}}, {{917,131},{130,130}}, {{917,0},{130,130}}, {{786,1834},{130,130}}, {{786,1703},{130,130}}, {{786,1572},{130,130}}, {{786,1441},{130,130}}, {{786,1310},{130,130}}, {{786,1179},{130,130}}, {{786,1048},{130,130}}, {{786,917},{130,130}}, {{786,786},{130,130}}, {{786,655},{130,130}}, {{786,524},{130,130}}, {{786,393},{130,130}}, {{786,262},{130,130}}, {{786,131},{130,130}}, {{786,0},{130,130}}, {{655,1834},{130,130}}, {{655,1703},{130,130}}, {{655,1572},{130,130}}, {{655,1441},{130,130}}, {{655,1310},{130,130}}, {{655,1179},{130,130}}, {{655,1048},{130,130}}, {{655,917},{130,130}}, {{655,786},{130,130}}, {{655,655},{130,130}}, {{655,524},{130,130}}, {{655,393},{130,130}}, {{655,262},{130,130}}, {{655,131},{130,130}}, {{655,0},{130,130}}, {{524,1834},{130,130}}, {{524,1703},{130,130}}, {{524,1572},{130,130}}, {{524,1441},{130,130}}, {{524,1310},{130,130}}, {{524,1179},{130,130}} };
animations["boss_antiswarm_breathing_"] = { {{524,1048},{130,130}}, {{524,917},{130,130}}, {{524,786},{130,130}}, {{524,655},{130,130}}, {{524,524},{130,130}}, {{524,393},{130,130}}, {{524,262},{130,130}}, {{524,131},{130,130}}, {{524,0},{130,130}}, {{393,1834},{130,130}}, {{393,1703},{130,130}}, {{393,1572},{130,130}}, {{393,1441},{130,130}}, {{917,786},{130,130}} };
animations["boss_antiswarm_death_"] = { {{393,1179},{130,130}}, {{393,1048},{130,130}}, {{393,917},{130,130}}, {{393,786},{130,130}}, {{393,655},{130,130}}, {{393,524},{130,130}}, {{393,393},{130,130}}, {{393,262},{130,130}}, {{393,131},{130,130}}, {{393,0},{130,130}}, {{262,1834},{130,130}}, {{262,1703},{130,130}}, {{262,1572},{130,130}}, {{262,1441},{130,130}}, {{262,1310},{130,130}}, {{262,1179},{130,130}}, {{262,1048},{130,130}}, {{262,917},{130,130}}, {{262,786},{130,130}}, {{262,655},{130,130}}, {{262,524},{130,130}}, {{262,393},{130,130}}, {{262,262},{130,130}}, {{262,131},{130,130}}, {{262,0},{130,130}}, {{131,1834},{130,130}}, {{131,1703},{130,130}} };
animations["boss_antiswarm_hit_"] = { {{131,1572},{130,130}}, {{131,1441},{130,130}}, {{131,1310},{130,130}} };
//40k more lines of code

Serializing a vector of objects with FlatBuffers

I have a vector of objects, let's call them Plumbuses, that I want to serialize with FlatBuffers. My schema for this example would be
namespace rpc;
struct Plumbus
{
dinglebopBatch:int;
fleeb:double;
}
table PlumbusesTable {
plumbuses:[Plumbus];
}
root_type PlumbusesTable;
since the root type can't be a vector. Calling flatc --cpp on this file generates plumbus_generated.h with functions such as CreatePlumbusesTableDirect.
The generated GeneratePlumbusesTableDirect function expects an argument const std::vector<const Plumbus *> *plumbuses. My idea was to simply take the addresses of the objects in the vector pbs and store them in another vector, pbPtrs. Since the buffer is created and sent away before pbs goes out of scope, I thought this would not be a problem.
#include <vector>
#include <iostream>
#include "plumbus_generated.h"
void send_plumbus(std::vector<rpc::Plumbus> pbs) {
std::vector<const rpc::Plumbus *> pbPtrs;
pbPtrs.push_back(&(pbs[0]));
pbPtrs.push_back(&(pbs[1]));
flatbuffers::FlatBufferBuilder buf(1024);
auto msg = CreatePlumbusesTableDirect(buf, &pbPtrs);
buf.Finish(msg);
void *msg_buf = buf.GetBufferPointer();
// here, I'd normally send the data through a socket
const rpc::PlumbusesTable *pbt = rpc::GetPlumbusesTable(msg_buf);
auto *pbPtrs_ = pbt->plumbuses();
for (const auto pbPtr_ : *pbPtrs_) {
std::cout << "dinglebopBatch = " << pbPtr_->dinglebopBatch() << ", fleeb = " << pbPtr_->fleeb() << std::endl;
}
}
int main(int argc, char** argv) {
rpc::Plumbus pb1(1, 2.0);
rpc::Plumbus pb2(3, 4.0);
std::vector<rpc::Plumbus> pbs = { pb1, pb2 };
send_plumbus(pbs);
}
Running this, instead of 1, 2.0, 3, and 4.0, I get
$ ./example
dinglebopBatch = 13466704, fleeb = 6.65344e-317
dinglebopBatch = 0, fleeb = 5.14322e-321
Why does it go wrong?
This looks like this relates to a bug that was recently fixed: https://github.com/google/flatbuffers/commit/fee9afd80b6358a63b92b6991d858604da524e2b
So either work with the most recent FlatBuffers, or use the version without Direct: CreatePlumbusesTable. Then you call CreateVectorOfStructs yourself.

Get the variable values at runtime using reflection in Dlang

Is it possible to get the class/struct/other variables value during runtime in dlang to get/set its value? If yes how to do that please provide example.
And also is it possible to get the runtime variable value?
Ex:
class S{ int svariable = 5;}
class B { int bvariable = 10;}
void printValue(T, T instanceVariable, string variableName) {
writeln("Value of ", variableName, "=", instanceVariable.variableName);
}
Output:
Value of svariable = 5;
Value of bvariable = 10;
There is a library named witchcraft that allows for runtime reflection. There are examples of how to use it on that page.
I'd first recommend trying a reflection library like #mitch_ mentioned. However, if you want to do without an external library, you can use getMember to get and set fields as well as invoke functions:
struct S {
int i;
int fun(int val) { return val * 2; }
}
unittest {
S s;
__traits(getMember, s, "i") = 5; // set a field
assert(__traits(getMember, s, "i") == 5); // get a field
assert(__traits(getMember, s, "fun")(12) == 24); // call a method
}

C++ How to use less conditional statements?

For my assignment, I'm storing user login infos. I'm taking in a string which is the command. The command can be create, login, remove, etc. There are 10 total options, i.e 10 different strings possible. Can anyone explain a more efficient way to write this instead of 10 if and else if statements? Basically how should I format/structure things besides using a bunch of if (string == "one"), else if (string == "two"). Thank you
I expect that your lecturer would like you to extract function to another re-usable function:
string action;
command = CreateAction(action);
command.Do(...);
Ofcourse, inside you CreateAction class you still need to have the conditionals that determine which commands need to be created.
AbstractCommand CreateAction(action)
{
if (action == "login")
return LoginCommand();
else if (action == "remove")
return RemoveCommand();
..... etc etc
}
And if you really want to get rid of all the conditionals than you can create some self-registering commands but that involves a lot more code and classes......
You should look up things like Command Pattern and Factory Pattern
You can use function pointers and a lookup table.
typedef void (*Function_Pointer)(void);
void Create(void);
void Login(void);
void Remove(void);
struct Function_Option_Entry
{
const char * option_text;
Function_Pointer p_function;
};
Function_Option_Entry option_table[] =
{
{"one", Create},
{"two", Login},
{"three", Remove},
};
const unsigned int option_table_size =
sizeof(option_table) / sizeof(option_table[0]);
//...
std::string option_text;
//...
for (i = 0; i < option_table_size; ++i)
{
if (option_text == option_table[i].option_text)
{
option_table[i].p_function();
break;
}
}
Use a switch, and a simple hash-function.
You need to use a hash-function, because C and C++ only allow switching on integral values.
template<size_t N> constexpr char myhash(const char &x[N]) { return x[0] ^ (x[1]+63); }
char myhash(const string& x) { return x.size() ? x[0] ^ (x[1]+63) : 0; }
switch(myhash(s)) {
case myhash("one"):
if(s != "one") goto nomatch;
// do things
break;
case myhash("two"):
if(s != "two") goto nomatch;
// do things
break;
default:
nomatch:
// No match
}
Slight adjustments are needed if you are not using std::string.
I would recommend you to create a function for every specific string. For example, if you receive a string "create" you will call function doCreate(), if you receive a string "login" then you call function doLogin()
The only restriction on these function is that all of them must have the same signature. In an example above it was smh like this:
typedef void (*func_t) ();
The idea is to create a std::map from strings to these functions. So you wouldn't have to write 10 if's or so because you will be able to simple choose the right function from the map by the name of a specific string name. Let me explain it by the means of a small example:
typedef void (*func_t) ();
void doCreate()
{
std::cout << "Create function called!\n";
}
void doLogin()
{
std::cout << "Login function called!\n";
}
std::map<std::string, func_t> functionMap;
void initMap()
{
functionMap["create"] = doCreate;
functionMap["login"] = doLogin;
}
int main()
{
initMap();
std::string str = "login";
functionMap[str](); // will call doLogin()
str = "create";
functionMap[str](); // will call doCreate()
std::string userStr;
// let's now assume that we also can receive a string not from our set of functions
std::cin >> userStr;
if (functionMap.count(userStr))
{
functionMap[str](); // now we call doCreate() or doLogin()
}
else
{
std::cout << "Unknown command\n";
}
return 0;
}
I hope it will help you in someway=)
You can use a map which does the comparison for you.
Something like this:
Initialise map:
std::map<std::string, std::function<void(std::string&)>> map;
map["login"] = std::bind(&Class::DoLogin, this, std::placeholders::_1);
map["create"] = std::bind(&Class::DoCreate, this, std::placeholders::_1);
Receive message:
map.at(rx.msg_type)(rx.msg_data);
Handler:
void Class::DoLogin(const std::string& data)
{
// do login
}
Maybe you can create a std::map<std::string, int> and use map lookups to get the code of the command that was passed - you can later switch on that number. Or create an enum Command and have a std::map<std::string, Command> and use the switch.
Example:
enum Command
{
CREATE,
LOGIN,
...
};
std::map<std::string, Command> commandNameToCode;
// fill the map with appropriate values
commandNameToCode["create"] = Command::CREATE;
// somehow get command name from user and store in the below variable (not shown)
std::string input;
// check if the command is in the map and if so, act accordingly
if(commandNameToCode.find(input) != commandNameToCode.end())
{
switch(commandNameToCode[input])
{
case CREATE:
// handle create
break;
...
}
}

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;
}