How to access C++ struct property value using index? - c++

struct student {
string name;
int age;
};
int main() {
student a1;
cout << a1[0] << endl; //Access the first variable of the struct
cout << a2[1] << endl; //Access the first variable of the struct
}
How could I access and retrieve value from the C++ struct using index instead of using "a1.name" ??

One way to do this is by creating a tuple from the member variables and using std::tie to get at the member by index. The index would have to be known at compile time however. You could wrap this inside a member function of your struct:
#include <tuple>
#include <iostream>
struct student {
std::string name;
int age;
template<size_t I>
auto& get() {
return std::get<I>(std::tie(name, age));
}
};
int main() {
student boy{ "Paul", 12 };
std::cout << "Name: " << boy.get<0>() << " Age: " << boy.get<1>() << std::endl;
//Change members
boy.get<0>() = "John";
boy.get<1>() = 14;
std::cout << "Name: " << boy.get<0>() << " Age: " << boy.get<1>() << std::endl;
}
Demo
(Requires at least C++14)
In C++11, since it doesn't have automatic return type deduction unless specified, you could use std::tuple_element to specify the return type instead:
#include <tuple>
#include <iostream>
struct student {
std::string name;
int age;
template<size_t I>
using T = typename std::tuple_element<I, std::tuple<std::string, int>>::type;
template<size_t I>
T<I>& get()
{
return std::get<I>(std::tie(name, age));
}
};
int main() {
student boy{ "Paul", 12 };
std::cout << "Name: " << boy.get<0>() << " Age: " << boy.get<1>() << std::endl;
//Change members
boy.get<0>() = "John";
boy.get<1>() = 14;
std::cout << "Name: " << boy.get<0>() << " Age: " << boy.get<1>() << std::endl;
}
Demo

You can't. At least not in the direct manner you want to do it and without partially redefining what a structure is. I will split my answer into two parts the first one explaining possible ways to get at least close to what you want and the second one explaining what you actually should do:
Getting down and dirty
There are two ways (that I can currently come up with) that might give you something to think about:
Use a wrapper class - while C++ does increase the flexibility of structure it doesn't change their purpose of a simple heterogeneous data container. It does however allow operator overloading including the [] operator. So if you create a class that contains the structure as its member (that is it wraps around the structure), you can expose the structure's data using []. This comes as close to what you want to do as possible. It does however defeat the whole purpose of using a struct since you can do that with just plain non-sturct class members but I have actually seen it not so long time ago when I was going through a C++ library that was wrapping a previous C-based version of itself in order to provide more modern features without the need of completely rewriting the C code.
Use pointer with an offset - using indexing generally suggest that the underlying container has a consistency when it comes to the blocks of data it contains. The problem is that a structure doesn't necessarily obey this since it can contain (just like in your case) multiple types of data. If you can sacrifice the heterogeneity of your structure and stick with a single data type (for example one or more doubles), you can safely use (up to the point that you have to always remember the number of members the structure has) a pointer and an increasing/decreasing offset to access its members. Just like with any sort of data when you create a standard reference (aka pointer) to something, that reference points at the address of the beginning of the memory this data is using. It is a common practice to use pointers to iterate through arrays and it works exactly like that - create a reference to your structure and the add +1, +2, ... (as many members that struct has). This makes things overly complicated though and is prone to error. As mentioned it also requires using the same type of data inside your structure. However you can also create a set of functions that handle (internally) the offsets. But this idea is similar to the class wrapper I have proposed above.
The alternatives ...
From what you have given as information I think you are looking for a completely different type of data - a dictionary, map or a list that contains some sort of custom generic data container that can hold any type of data but also stores that data's type in order to allow recasting it to its original state. Many libraries provide such containers for example Qt has the QVariant (part of the core module), boost has the boost::variant, std::tuple (or even better - named tuples) provided with your standard C++ (since C++11) and so on. I can speak about Qt in greater detail since I have more experience with it. It offers the QVariantList (a typedef for QList<QVariant>) which allows indexing. Of course all this requires you to 1)abandon your structure-thing and 2)use some more advanced containers that may or may not introduce huge drawbacks on whatever you are working on including licensing issues, memory overhead, larger binaries, handling a lot of extra library files etc.

How to access C++ struct property value using index?
You can not. C++ language has no feature that would allow this. This could be possible in a language that supports (static) reflection.
You could choose to use a std::tuple instead, which does allow indexed member access, but that's a step down in readability since you don't get to name the members.

I tried to stay as close to your example as possible but I did have to convert the age from int to string. This works and I have found it useful in one application.
struct student
{
std::string name, age;
std::string *elemtnPtr[10];
student()
{
int i=0;
elemtnPtr[i++] = &name;
elemtnPtr[i++] = &age;
}
};
void demo()
{
student a1;
a1.name = "This Works";
a1.age = "99";
std::cout << *a1.elemtnPtr[0] << std::endl;
std::cout << *a1.elemtnPtr[1] << std::endl;
}

You cannot.
Not until reflection has been introduced in C++, which should (I hope) be the case in C++20.
Some projects introduce tuples enhanced with names, but it still not real structs.

Related

Accessing active union member

Is there a simple way to understand which union member is active?
An example:
union Read_Value{
char ch;
float number;
string str;
}
Suppose a void function reads from an input file stream and initialise Read_Value on the base of the type of the variable read. Assuming I don't know how the function works, how can I understand which of the three member is active?
A bare union cannot tell you which is the active element. You have to keep track of that yourself.
Since C++17 std::variant is the "modern union". It has a index() method that tells you which is the active index. Example from cppreference:
#include <variant>
#include <string>
#include <iostream>
int main()
{
std::variant<int, std::string> v = "abc";
std::cout << "v.index = " << v.index() << '\n';
v = {};
std::cout << "v.index = " << v.index() << '\n';
}
Possible output:
v.index = 1
v.index = 0
Is there a simple way to understand which union member is active?
In general, using a tagged (or discriminated) union. This means storing some metadata (tracking which member is active) alongside the raw union.
The modern solution is indeed std::variant, which does all this for you.
If you don't have C++17 support, don't have it in your standard library's experimental or tr extensions, and for some reason can't use the Boost.variant precursor ... you can still write an old-style tagged union yourself.
I wouldn't call it simple though, at least not to do well.

Converting std::any into an unknown type

If I have a std::any of an std::string or an int, how could I cast this into the type that's contained?
std::any has type on it, however I can't use this type to cast.
Example:
#include <any>
#include <iostream>
#include <string>
int main(void) {
std::any test = "things";
std::any test2 = 123;
// These don't work
std::string the_value = (std::string) test;
int the_value2 = (int) test2;
std::cout << the_value << std::endl;
std::cout << the_value2 << std::endl;
}
You use any_cast to do the trick. For example
auto a = std::any(12); 
std::cout << std::any_cast<int>(a) << '\n';
You can find more details from cppreference
If you want to dynamically cast the value inside a std::any, you can try
if (a.type() == typeid(int)) {
cout << std::any_cast<int>(a) << endl;
} else if (a.type() == typeid(float)) {
cout << std::any_cast<float>(a) << endl;
}
If you do not have a list of types among which the any holds one, you cannot convert the any to its type and operate on it as its real type.
You can store a type in an any, and an operation in that type as a function pointer on that any. But this must be done at the moment of storage or when you do have a list (possibly with 1 element) of the possible types stored in the any.
C++ does not store sufficient information within an any to permit arbitrary code to be compiled on that type when it stores the value in the any. C++ does not permit full "reification" at runtime.
Type erasing type erasure, `any` questions? Q&A by a stackoverflow user of ill repute gives an example of how to remember some operation on the contents of the any while still forgetting the type stored.
If you do have such a list of possible types, consider using variant. any exists in the narrow window where you do not know the types stored at container design time, but do at both insert and removal.
In that narrow window, you can do runtime tests based off the typeid stored and cast to the known type using any_cast.

Generic way to generate random struct for testing

I've been given the task to refactor a bunch of C++ code that has a lot of math and not an explanation of what it does.
In order to do that I've setup a bunch of automated test that given random data compare old and new code results.
The thing is that, while it is simple to generate random vector of any size I have a lot of "struct" with many public fields (> 20) I'm a bit tired of writing custom function to fill them.
One can think of using some kind of script to parse the definition and autobuild the corresponding generator function.
Do you think this is a good idea ?
Is there anything like that already done?
If you have only Plain Old Data, a struct is, roughly, merely a blob of memory with some meaning to the compiler.
This means you can treat it as such, and simply fill it with random bytes, using a union:
struct a {
int i;
char c;
float f;
double d;
};
union u {
char arr[sizeof(a)];
a record;
};
char generateRandomChar(); // implement some random char generation
int main() {
u foo;
for (char& c : foo.arr) {
c = generateRandomChar();
}
std::cout << "i:" << foo.record.i
<< "\nc:" << foo.record.c
<< "\nf:" << foo.record.f
<< "\nd:" << foo.record.d;
}
See it live!
Technically, this is Undefined Behavior. In practice, it is well defined in most compilers.

Cast an object value without pointers

Let's assume that A and B are two classes (or structures) having no inheritance relationships (thus, object slicing cannot work). I also have an object b of the type B. I would like to interpret its binary value as a value of type A:
A a = b;
I could use reinterpret_cast, but I would need to use pointers:
A a = reinterpret_cast<A>(b); // error: invalid cast
A a = *reinterpret_cast<A *>(&b); // correct [EDIT: see *footnote]
Is there a more compact way (without pointers) that does the same? (Including the case where sizeof(A) != sizeof(B))
Example of code that works using pointers: [EDIT: see *footnote]
#include <iostream>
using namespace std;
struct C {
int i;
string s;
};
struct S {
unsigned char data[sizeof(C)];
};
int main() {
C c;
c.i = 4;
c.s = "this is a string";
S s = *reinterpret_cast<S *>(&c);
C s1 = *reinterpret_cast<C *>(&s);
cout << s1.i << " " << s1.s << endl;
cout << reinterpret_cast<C *>(&s)->i << endl;
return 0;
}
*footnote: It worked when I tried it, but it is actually an undefined behavior (which means that it may work or not) - see comments below
No. I think there's nothing in the C++ syntax that allows you to implicitly ignore types. First, that's against the notion of static typing. Second, C++ lacks standardization at binary level. So, whatever you do to trick the compiler about the types you're using might be specific to a compiler implementation.
That being said, if you really wanna do it, you should check how your compiler's data alignment/padding works (i.e.: struct padding in c++) and if there's a way to control it (i.e.: What is the meaning of "__attribute__((packed, aligned(4))) "). If you're planning to do this across compilers (i.e.: with data transmitted across the network), then you should be extra careful. There are also platform issues, like different addressing models and endianness.
Yes, you can do it without a pointer:
A a = reinterpret_cast<A &>(b); // note the '&'
Note that this may be undefined behaviour. Check out the exact conditions at http://en.cppreference.com/w/cpp/language/reinterpret_cast

How can I store objects of differing types in a C++ container?

Is there a C++ container that I could use or build that can contain, say, int and string and double types? The problem I'm facing is that whenever I try to populate, say, a map, vector or list with, say, the following:
int x;
string y;
double z;
I'm restricted with the format:
list<int> mycountainer;
vector<string> mycontainer;
which forces mycontainer to only consist of one type.
Before anyone suggest generics, that wouldn't work either since the standard vector and list containers that come with C++ are already generic - they can be container for any types but cannot contain multiple types.
I would like to avoid using Boost also if at all possible - I'd prefer it if there is a simple way I could code this myself.
You could use (or re-implement) boost::any and store instances of boost::any in a container. That would be the safest, since boost::any has probably dealt with much of the edge cases and complexity involved in solving this kind of problem in the general case.
If you want to do something quick and dirty, create a structure or perhaps a union containing members of all potential types along with an enumeration or other indicator of which type is 'active' in the object. Be especially careful with unions as they have some interesting properties (such as invoking undefined behavior if you read the wrong union member, only one of the members can be 'active' at a time, the one that was most recently written to).
I'm curious what you're doing that you need such a construct, though.
Well, the first question would be: Why do you think you need to store objects of different, totally unrelated types in the same container? That seems fishy to me.
If I had the need, I'd look into boost::variant or boost::any.
What you want is called a "hetrogenious container". C++ doesn't technically support them in the STL, but Boost does.
Given that, I think you'll find your answer in this question: how-do-you-make-a-heterogeneous-boostmap
You can use either structures, or classes or std::pair.
[edit]
For classes and structs:
struct XYZ {
int x;
string y;
double z;
};
std::vector<XYZ> container;
XYZ el;
el.x = 10;
el.y = "asd";
el.z = 1.123;
container.push_back(el);
For std::pair:
#include <pair>
typedef std::pair<int, std::pair<string, double> > XYZ;
std::vector<XYZ> container;
container.push_back(std::make_pair(10, std::make_pair("asd", 1111.222)));
You could use a struct that contains all three.
struct Data
{
int intVal;
std::string stringVal;
double doubleVal;
};
Then you could just declare list mycontainer<Data> and use the appropriate value, provided you know what the value type is. If not, add an addition field to the struct that tells you which of the three data types is in use.
struct Data
{
enum DATATYPE { DT_INT, DT_STRING, DT_DOUBLE } type;
int intVal;
std::string stringVal;
double doubleVal;
};
If you're worried about memory usage, you could probably use a union, though I tend to avoid using them. It might be needless paranoia on my part though.
The simplest method is of course to define a struct or class that has members of each of the types you wish to store. Josh's answer suggests Boost.Any, which will hold pretty much anything. If you want to restrict values to only those of types int, double, and std::string, then the better choice would be Boost.Variant.
If you simply don't want to use Boost, then I suggest you get over your hang-ups and use it anyway. "Not Invented Here" is a self-destructive policy. But if you can't use Boost, then you can write your own variant class instead. Andrei Alexandrescu wrote a three-part series on that (part 1, part 2, part 3) a few years ago, and its design inspired the one Boost uses.
What I have for this question is not what I hoped would work. By what I think that you would like, is a container that stores multiple value types, that you can access at will.
However, as such, a container would have to specify what value it holds, so you could have a class with 500 data types in it, with a correlating constructor for each data type, however, that would be super memory inefficient.
Here is my proposed suggestion, I have worked on for a day, And I hope it meets your criteria:
#include <iostream>
#include <vector>
using namespace std;
enum class type: unsigned int {int_t, unsigned_int_t, string_t, double_t, float_t, bool_t, unipointer_t, vector_int_t, vector_unipointer_t};//just add item types here and in the switch statement to hold more void_ps in unipointer...
class unipointer {
void* obj;//the pointer to the data. any type of pointer.
type objtype;//the object type, kept as an enum class.
struct void_p {//template magic... ;D
void* void_ptr;
template<typename T>//when object is initialized, it converts the the void* pointer to the output value.
operator T() {
return reinterpret_cast<T&>(void_ptr);
}
void_p(void* val): void_ptr(val) {};
};
public:
unipointer(void_p ptr, type ptrtype) : obj(ptr), objtype(ptrtype) {}
type get_type(void) {//Once you call this function, you know the type of data stored, and can call other functions accordingly.
return objtype;
}
template<typename T>//With a temlate, get any value through a pointer to it.
T get_ptr(void){
return reinterpret_cast<T&>(obj);
}
template<typename T>//With a temlate, get any value, as an object
T get_object(void) {
return *get_ptr<T*>();
}
void_p get_auto_pointer(void) {//get any pointer to value, can't be assigned to "auto*"!
return unipointer::void_p(obj);
}
void_p get_auto_object(void) {//get any value, can't be assigned to "auto"!
return *(void_p*)get_auto_pointer();
}
};
void process_stuff(unipointer& thing, unsigned int num_of_tabs);
int main() {
double initialization = 1.2345;
float even_another = 3.14159f;
unipointer items(new vector<unipointer>{//one thicc object instance
//Initialization examles:
unipointer(new int(-12345), type::int_t),
unipointer(new unsigned int(4'294'967'295), type::unsigned_int_t),
unipointer(new string("That is how I store my items."), type::string_t),
unipointer(&initialization, type::double_t),
unipointer(&even_another, type::float_t),
unipointer(new bool(1), type::bool_t),
unipointer(new unipointer(new unipointer(new unipointer(new string("OMG! NESTING!"), type::string_t), type::unipointer_t), type::unipointer_t), type::unipointer_t),
unipointer(new vector<int>{ 1,2,3 }, type::vector_int_t),
unipointer(new vector<unipointer>{
unipointer(new string("That is how I store my nested items."), type::string_t),
unipointer(new vector<int>{4,5,6}, type::vector_int_t),
unipointer(new string("Is your head brimming with ideas yet?"), type::string_t)
} , type::vector_unipointer_t)
}, type::vector_unipointer_t);
cout << "What is in the \"items\" unipointer:" << endl;
process_stuff(items, 1);
system("pause");
}
void process_stuff(unipointer& thing, unsigned int num_of_tabs) {
//declare variables & lamda for interpretaion methods, using variable assignment with "get auto object/pointer"
unsigned int* test = 0;
double test_2 = 0;
auto tab_to_current = [num_of_tabs]() {
for (unsigned int i = 0; i < num_of_tabs; ++i) {
cout << "\t";
}
};
//format the thing.
tab_to_current();
//look through and do stuff
switch (thing.get_type()) {//just add item types here and in the enum class to hold more void_ps in unipointer...
case type::int_t:
cout << "The integer: " << *thing.get_ptr<int*>() << "." << endl;//one way of getting object back from class
break;
case type::string_t:
cout << "The string: \"" << thing.get_object<string>() << "\"." << endl;//another way
break;
case type::unsigned_int_t:
test = thing.get_auto_pointer();//another way
cout << "The unsigned integer: " << *test << "." << endl;//don't forget to de-reference it!
delete test;
break;
case type::double_t:
test_2 = thing.get_auto_object();
cout << "The double: " << test_2 << "." << endl;//even another way!
break;
case type::float_t:
cout << "The float: " << float(thing.get_auto_object()) << "." << endl;//even another way!
break;
case type::bool_t:
cout << "The boolean: " << *(bool*)thing.get_auto_pointer() << "." << endl;//even another way!
break;
case type::unipointer_t:
cout << "A unipointer, and in it:" << endl;
process_stuff(*&thing.get_object<unipointer>(), num_of_tabs+1);
tab_to_current();
cout << "[End of unipointer]" << endl;
break;
case type::vector_int_t:
cout << "A vector of integers, and in it:" << endl;
for (unsigned int i = 0; i < thing.get_object<vector<int>>().size(); ++i) {
tab_to_current();
cout << "\tItem " << i << ": " << thing.get_object<vector<int>>().at(i) << endl;
}
tab_to_current();
cout << "[End of vector of integers]" << endl;
break;
case type::vector_unipointer_t:
cout << "A vector of unipointers, and in it:" << endl;
for (unsigned int i = 0; i < thing.get_object<vector<unipointer>>().size(); ++i) {
process_stuff(*&thing.get_object<vector<unipointer>>().at(i), num_of_tabs + 1);
}
tab_to_current();
cout << "[End of unipointer vector]" << endl;
break;
}
}
The "unipointer" class should be initialized with a pointer to any object type, and also the type of object. The class can return, through a function, your data, although it is not very safe, and could be called with the wrong type of data.
This is just an example of what could work, I sure hope that you take inspiration from it.
And, to answer your original question, you would set up a list, or vector with the following format:
vector/list:
|
|unipointer(*double)
|
|unipointer(*int)
|
|unipointer(*string)
|
...
|
end
PS: I am a beginner with objects and templates, so this might be messy. Many apoligies.
If you have a finite number of items you need to store, put them in a class or structure.
If there is no limit to the items you would need to store in this container then look at a different way of doing things because the only way of doing it is by storing them as an object, and then casting them to their own type when you need to access them.
However, if any item could potentially be in the container, then you have no way of knowing what type specific items in the container are, and therefore will not be able to cast them.
If C++ contained reflection, there would possibly be a way to do this, but C++ doesn't have reflection.