Another way to refer members in a structure - c++

Now, I am learning as to how to use structures properly in C++.
Is there another way to refer members in a structure.
As an example, below is my code.
I want to know if I can do something like test.b to refer name member in the structure.
Is there any incredible way to do so?
#include <iostream>
using namespace std;
struct A
{
string name = "Test";
};
int main()
{
A test;
string b = "name";
cout << test.name;
return 0;
}

If you don't need to use a string to reference the member then the way to do this is called "pointer to member":
struct A
{
int name;
int value;
};
main()
{
int A::* b = &A::name; // assign "name" to the variable called b
struct A test = {1,2}; // make a structure and fill it in
return test.*b; // use the variable called b to reference test.name
}
If you do need to refennce the items with a string the other way mentioned in the contents is to use a map. That can be useful if all your members are the same type.
#include <iostream>
#include <map>
main()
{
std::map<std::string,int> test; // make something that can be keyed by a string
test["name"]=1; // put something called "name" in the map with a value of 1
test["value"]=2; // put something called "value" in the map with a value of 2
std::cout << test["name"] << std::endl;
return 0;
}

What you are referring to is called Reflection (function/attribute access by name). C++ by default doesn't have reflection. So probably you need to look for libraries/frameworks for that. Google "C++ Reflection" for that. Boost is one of the solution out there for C++ reflection/serialization.

Related

Add a variable to a struct at runtime C++

I have a struct:
struct MyStruct{
}
I want to be able to add a pointer to a variable at runtime
std::string myString = "Hello";
MyStruct my_struct;
As an example if there was a function that did this it would be like this
std::add_to_struct(&my_struct, &myString);
and you can retrieve that variable using:
std::get_struct_variable<std::string>("myString");
Adding fields to structs or classes at runtime isn't allowed, by design. More dynamic languages like JavaScript support this because the runtime "knows" which fields each object has, and what their names are. In C++ (ignoring debug info), this info is only known at compile time. At runtime, an instance of MyStruct is just a piece of memory of length sizeof(MyStruct). There is no room to add additional fields. Even if MyStruct instances could be made longer, any code complied to handle a MyStruct wouldn't know how to interpret the additional bytes, because that info wasn't available at compile time.
If you want a dictionary-like object that can associate arbitrary string names with values, use std::map like so:
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string,int> dict;
dict["hello"] = 100;
dict["good bye"] = 200;
std::cout << dict["hello"] << std::endl;
return 0;
}
In this example, the values are ints. If you need values of different types in the same data structure, there are a few options. You could change the values from int to void*:
int main() {
std::map<std::string,void*> dict;
dict["hello"] = new int(100);
std::cout << *static_cast<int*>(dict["hello"]) << std::endl;
return 0;
}
Instead of storing the values directly in the map, we instead create values of any type on the heap, and only store their pointers in the map. This simple, but error-prone, and I don't recommend it. void* circumvents C++'s type safety by making all types interchangeable. Notice that when we retrieve our data from dict, the retrieving code has to know to cast the pointer back to int. If you cast back to the wrong type, you'll get garbage data.
Also notice that this program leaks memory! You could be diligent about individually freeing each item from each map when you're done with it, or you could used std::unique_ptr to do that automatically. But in either case you're going to find that the code which frees the elements needs to know what type each element is, and how to properly free that particular type. What if one of your types contains pointers to yet more objects that must be freed first?
You could alternatively make the values unions:
typedef union {
int i;
char c;
} IntChar;
int main() {
std::map<std::string,IntChar> dict;
IntChar ic;
ic.i = 100;
dict["hello"] = ic;
std::cout << dict["hello"].i << std::endl;
return 0;
}
This restores some partial type-safety, since you can only interpret the data as one of the types pre-defined in the union. But it has the drawbacks that you must pre-define all possible types in the union, there are limits on what types you can put in a union, and each instance of the union will be sized to accommodate the largest type inside it, which may waste memory if most of your instances use the smaller types.
Union's limitations may be acceptable to you. If not, then the most type-safe and extensible solution, at the expense of more code, is to make map values pointers to a base class type, and then create derived classes for each type you want to put in the dictionary:
#include <iostream>
#include <map>
#include <memory>
#include <string>
class Base {
public:
virtual ~Base() = default;
virtual std::string to_string() = 0;
};
class DerivedInt : public Base {
public:
int i;
DerivedInt(int i_) : i(i_) {}
std::string to_string() override { return std::to_string(i); }
};
class DerivedChar : public Base {
public:
char c;
DerivedChar(int c_) : c(c_) {}
std::string to_string() override { return std::string(&c, 1); }
};
int main() {
std::map<std::string,std::unique_ptr<Base>> dict;
dict["hello"] = std::make_unique<DerivedInt>(100);
dict["good bye"] = std::make_unique<DerivedChar>('z');
std::cout << dict["hello"]->to_string() << std::endl;
std::cout << dict["good bye"]->to_string() << std::endl;
return 0;
}
This way, each derived class is responsible for its own particular fields, and the calling code need not worry about their differences. If any derived type needs to be freed in a particular way, it can implement its own destructor to do so.
This is all assuming, of course, that your use-case actually needs to associate arbitrary names with arbitrary data. If you're just trying to port a scripting language coding style over to C++ by using dictionaries instead of classes and structs, I'd recommend instead that you embrace the strengths and limitations of C++ while working in C++. That means deciding ahead of time what classes and fields you need, and declaring them up front so the compiler can help spot your mistakes.

Set and get member functions manipulation of data members

So I'm an newbie to programming and I have encountered a
case for which I suppose qualifies as an authentic question
in this awesome forum. Is there a way to write statements inside my get functions so that I can obtain all the changed data member values without having to create multiple get functions
for each data member?
Regards
I am practicing building programs which are easy to maintain by localizing the effects to a class's data members by accessing and manipulating the data members through their get and set functions. In this regard I have two data members for which I wish to change. After compiling, the set functions works well by changing the values but the get functions can only return one of the data member values at a time.
class GradeBook
{
public:
void setCourseName(string code,string name)
{
CourseCode = code;
CourseName = name;
}
string getCourseName()
{
return CourseCode;
return CourseName;
}
void displayMessage()
{
cout<<"Welcome to the GradeBook for: \n" <<getCourseName()
<<endl;
}
private:
string CourseName;
string CourseCode;
};//end class GradeBook
After compiling and running the program, the program outputs the CourseCode but the CourseName doesn't get displayed. I had to create two get functions each to obtain the two data members. I don't want to have 2 get functions to obtain the data member values. I just want to use one get function to keep the code at minimum.I wish to use one get function to return two values for each data member. I have already tried using one return statement and separated the data members with a comma.
Your idea of using return twice cannot work, the first return will return control to the caller and the second will never be executed. You should have got warning about it from your compiler.
I believe that an initial solution could be to use std::pair (docs: https://en.cppreference.com/w/cpp/utility/pair), see snippet below.
NOTE: using namespace std; (which is most likely what you are doing in the code you do not show), is a bad practice, consider using the fully qualified name
#include <string>
#include <utility>
#include <iostream>
//Bad practice, I added it only to keep differences with OP code small
using namespace std;
class GradeBook
{
public:
void setCourseName(string code,string name)
{
CourseCode = code;
CourseName = name;
}
std::pair<string, string> getCourseName()
{
return {CourseCode, CourseName};
}
void displayMessage()
{
//only in C++17
auto [code, name] = getCourseName();
cout<<"Welcome to the GradeBook for: \n" << code << " - " << name
<<endl;
}
private:
string CourseName;
string CourseCode;
};//end class GradeBook
Note that auto [code, name] is a feature called structured binding, available only in C++17, if you have an older compiler, you have to return a std::pair<std::string, std::string> and access its elements using the member variables first and second.
Now, std::pair is good for this contrived example, but, for your case, you might want to consider doing something a bit more readable, because the elements of the pair have the same type so the user of your library will have difficulties remembering what is the first and second element. So you might want to use a custom-made struct with some more meaningful names.
#include <string>
#include <utility>
#include <iostream>
//Bad practice, I added it only to keep differences with OP code small
using namespace std;
struct CourseCodeAndName{
std::string code;
std::string name;
};
class GradeBook
{
public:
void setCourseName(string code,string name)
{
CourseCode = code;
CourseName = name;
}
CourseCodeAndName getCourseName()
{
return {CourseCode, CourseName};
}
void displayMessage()
{
auto codeAndName = getCourseName();
cout<<"Welcome to the GradeBook for: \n" << codeAndName.code << " - " << codeAndName.name
<<endl;
}
private:
string CourseName;
string CourseCode;
};//end class GradeBook
See this example. Alternatively you can use std::tuple.
class GradeBook
{
/* ... */
public:
std::pair<std::string, std::string> get(){
return std::make_pair(CourseName, CourseCode);
}
};
int main()
{
GradeBook book1("Hello","World")
auto result = book1.get();
cout << result.first << result.second;
}
If you write:
return x,y;
or:
return x;
return y;
You should know that in first case you get the last value (you get y), and in second case you get the value of first return (you get x, because as soon as compiler see return, function will return the value, and then function will go in epilogue state (cleaning of stack memory assigned to function, both inline and non-inline function).
And about the use of get function it's normal. If you want to use the value to do something of logic (not to display), yes you should use a lot of get function. Instead if you want to display the values, use a void function, for example "void printData();", and inside it write code to print data. You probably setted the class variables as private (following the encapsulation rules) so you will have access to them inside the print function.

Filing a vector outside of a function in a class

Fairly simple question here, whats the best way to fill a vector outside of a function in a class .cpp file? currently i'm attempting the following which is not working:
std::vector<Player> midfielder(8);
midfielder.at(0) = Midfielder("Default ",0,"Midfielder");
midfielder.at(1) = Midfielder("David Armitage ",1,"Midfielder");
midfielder.at(2) = Midfielder("Tom Rockliff ",2,"Midfielder");
midfielder.at(3) = Midfielder("Gary Ablett ",3,"Midfielder");
midfielder.at(4) = Midfielder("Dyson Heppel ",4,"Midfielder");
midfielder.at(5) = Midfielder("Scott Pendlebury",5,"Midfielder");
midfielder.at(6) = Midfielder("Michael Barlow ",6,"Midfielder");
midfielder.at(7) = Midfielder("Jack Steven ",7,"Midfielder");
To provide context, 'Midfielder' is a class that inherits from the 'Player' class.
TeamManagment.h
#ifndef TEAMMANAGEMENT_H
#define TEAMMANAGEMENT_H
#include <vector>
#include "Player.h"
#include "Midfielder.h"
#include <string>
class TeamManagement
{
public:
TeamManagement();
void Display_Players();
};
#endif // TEAMMANAGEMENT_H
TeamManagement.cpp
#include <iostream>
#include <string>
#include <vector>
#include "Player.h"
#include "Midfielder.h"
#include "TeamManagement.h"
using namespace std;
TeamManagement::TeamManagement()
{
}
std::vector<Player> midfielder(8);
//errors start occurring on line below: 'midfielder' does not name a type
midfielder.at(0) = Midfielder("Default ",0,"Midfielder");
midfielder.at(1) = Midfielder("David Armitage ",1,"Midfielder");
midfielder.at(2) = Midfielder("Tom Rockliff ",2,"Midfielder");
midfielder.at(3) = Midfielder("Gary Ablett ",3,"Midfielder");
midfielder.at(4) = Midfielder("Dyson Heppel ",4,"Midfielder");
midfielder.at(5) = Midfielder("Scott Pendlebury",5,"Midfielder");
midfielder.at(6) = Midfielder("Michael Barlow ",6,"Midfielder");
midfielder.at(7) = Midfielder("Jack Steven ",7,"Midfielder");
//errors stop occurring here
void TeamManagement::Display_Players(){
cout<<"Position Name ID"<<endl;
for (int i=1;i<8;i++)
{
cout<<midfielder[i].Player_Details()<<" "<<midfielder[i].Get_player_id()<<endl;
}
}
The first problem is that you cannot perform assignment like that outside of a function. You must use construction or initialization.
With C++98 you cannot populate/initialize a vector outside of a function.
With C++11/14 you can populate one using initializer syntax:
#include <iostream>
#include <vector>
struct Thing {
int m_i, m_j;
Thing(int i, int j) : m_i(i), m_j(j) {}
};
std::vector<Thing> things {
{ 1, 2 }, { 2, 3 }
};
int main() {
std::cout << "things[0].m_j = " << things[0].m_j << '\n';
}
But std::vector won't like you trying to put "Midfielder"s into a vector of Player. Lets use an SSCCE to reconstruct the damage you're doing:
#include <iostream>
struct Base {
int i;
};
struct Derived : public Base {
int j;
};
int main() {
std::cout << "Base size = " << sizeof(Base) << '\n';
std::cout << "Derived size = " << sizeof(Derived) << '\n';
}
This tells us that Base and Derived have a different size. But you're trying to put these two objects into the same container because they're related. Round peg and square peg are related... They won't fit into the same hole, and this is the problem we have now.
The vector creates space in memory for your elements based on the type you supply, and then it requires you to pass it exactly that type to populate those spaces with, or a type that has a conversion mechanism to the storage type.
If you want to have a container of different types, you'll need to use pointers, but then you're going to run into the problem that what you get back will be a pointer to the base type and you will need to provide yourself with a way to distinguish different player types.
See Store derived class objects in base class variables for the C++98 approach. In modern C++ (11 and 14) you should use smart pointers, e.g.
std::vector<std::unique_ptr<Base>>
std::vector<std::shared_ptr<Base>>
Presumably default constructing a Midfielder doesn't make a lot of sense, so you can reserve the memory, then emplace_back into the vector.
std::vector<Player> midfielder {};
midfielder.reserve(8);
midfielder.emplace_back("Default ",0,"Midfielder");
midfielder.emplace_back("David Armitage ",1,"Midfielder");
midfielder.emplace_back("Tom Rockliff ",2,"Midfielder");
midfielder.emplace_back("Gary Ablett ",3,"Midfielder");
midfielder.emplace_back("Dyson Heppel ",4,"Midfielder");
midfielder.emplace_back("Scott Pendlebury",5,"Midfielder");
midfielder.emplace_back("Michael Barlow ",6,"Midfielder");
midfielder.emplace_back("Jack Steven ",7,"Midfielder");
midfielder.at(0) = Midfielder("Default ",0,"Midfielder"); is a statement. You've put that and similar statements in (global) namespace scope. That's your bug. Only declarations may be in namespace scope. You must put your statements inside a function.
The error message stems from the fact that declarations which don't start with a keyword start with a type name. Since midfielder is not a keyword, the compiler expects it to be a type name but it isn't one, so you get the error.

C++ - How to Make Static Dictionary to Lookup Matrix

I am trying to write a C++ class that allows me to access certain matrix elements by a string lookup. I wanted to create a 'static' class that can do this, such as:
#include <unordered_map>
namespace Mine {
static double AA[3][4] = {
{5.04964676394959,-0.693207030363152,0.0422140829479668,-0.000968959310672217},
{2.6044054979329,0.288475262243944,-0.0208805589126506,0.000380899394040856},
{-4.32707864788065,1.07090008760872,-0.0777874445746693,0.00165150952598117}
};
static unordered_map<std::string, double[3][4]> Mine::parameter_store = { {"AA", AA}};
With the idea being that I would have several matrices, and could look them up based on a key. However, this seems to totally and utterly fail with the following error:
error: object expression of non-scalar type 'double [3][4]' cannot be used in a pseudo-destructor expression
Is it possible to build a lookup table this way in C++?
#include <unordered_map>
#include <vector>
namespace Mine{
template<class T>
using Matrix = std::vector<std::vector<T>>;
Matrix<double> AA = {
{5.04964676394959,-0.693207030363152,0.0422140829479668,-0.000968959310672217},
{2.6044054979329,0.288475262243944,-0.0208805589126506,0.000380899394040856},
{-4.32707864788065,1.07090008760872,-0.0777874445746693,0.00165150952598117}
};
static std::unordered_map<std::string, Matrix<double>* > parameter_store = { {"AA", &AA}};
}
#include <iostream>
int main()
{
std::cout << (*Mine::parameter_store["AA"])[0][0] << std::endl;
std::cout << (*Mine::parameter_store["AA"])[0][1] << std::endl;
std::cout << (*Mine::parameter_store["AA"])[1][2] << std::endl;
}
output
5.04965
-0.693207
-0.0208806
The Matrix<> template used here causes each row to store its length even though that's redundant. You can avoid this by used a std::array (but then you're locked into each matrix having equal dimensions since that's part of the type information) or using some library like Boost that provides a multidimensional array. That's an extremely small inefficiency though and unless you know you need to it might be best to not worry about that.
You can try wrapping double[3][4] in a structure/class
structure myMatrix {
double arr[3][4];
//if you want to initialize it
myMatrix(double[3][4] p){
//copy matrix here
}
};

C++ Pointers help?

I need a little bit of help with using pointers in C++. Sorry to seem beginner but I really can't quite understand them. I have read the tutorial on pointers on the cplusplus.com website, so please don't suggest that.
I basically have a variable which holds the name of another variable, and I wish to access that variable through the holder one. I believe I need to use pointers, correct me if I'm wrong though.
E.g.
int a;
string b;
a = 10;
b = "a";
I need to access the variable "a" through the contents of variable "b".
Just to put this into better perspective, this is how I am using it:
int a;
a = 20;
void getVar(string name) {
cout << name;
}
getVar("a");
But as you can see, on the fifth line, that will just cout the value of name, in this case "a", but I want it to cout the value of the variable which name contains, so I want it to output "20".
Any help here would be much appreciated.
If you need to associate a name with a value, consider associative arrays otherwise known as dictionaries and maps. The Standard Template Library has std::map that you can use to associate text with a value:
#include <map>
#include <string>
std::map<std::string, int> my_map;
my_map["A"] = 20;
cout << my_map["A"] << endl;
What you are thinking of is called (Reflection) which C++ does not support. You can however use pointers to access what is in a variable it points to:
int a = 5; //int variable that stores 5
int *b = &a; //int pointer that stores address of a
(*b) = 10; //stores 10 into address that b points to (a)
cout << a; //prints 10
What you are trying to achieve is not possible in a compiled language (not considering reflection). You might accomplish something similar using a map data structure.
theMap["a"] = 20;
and a corresponding
void getVar(string key){
cout << theMap[key];
}
that can be called with
getVar("a");
Note that in this extremely simple sample theMap has to be in scope for the function, like in a class or a namespace.
If you use pointers you are just using a level of indirection not at all suited for your example. See Chads answer for instance.
Theres no real way for you to access variables by name like that unless you create some kind of container class that has a name member that you look up by. I'm not sure what this has to do with pointers though.
What you're asking for is called "reflection" or "introspection" - the ability to use design-time names for your program's objects (classes, variables, functions, etc) in run time. C++ does not support that out of the box - the design-time names are stripped upon compilation.
There are some libraries that provide that capability in C++; but there are also languages where reflection is is part of the language. Python or JavaScript, for example.
Maybe this could suit you:
int a = 5;
class b {
public:
b(int &x) { ref_ = x; }
int operator()(void) { return ref_; }
private:
int &ref_;
}
b my_b(a);
my_b() /* -> 5 */;
Your code does not use pointers. you're trying to convert a string into an identifier and print it's result, I don't know whether that's possible or not. If you intended using pointer your code should've looked like this:
int a = 20;
int* b = &a;
cout << *b;
quick fix for outputting integers only:
int a;
a = 20;
void getVar(int name) {
cout << name;
}
getVar(a);
If you need the function to work for any type of variable, maybe think about some template function.
Edit: Here is the code for the template program:
#include <iostream>
#include <string>
using namespace std;
template <class T>
void getVar(T name){
cout<<name<<endl;
}
int main()
{
string x="hee";
int y=10;
getVar(x);//outputs hee
getVar(y);//outputs 10
return 0;
}