C++ Indexing structs - c++
I'm wondering if there is a way to index structs in order to run through the member variables in a for-loop. I'm asking repeatingly for stuff and get input via cin>>:
struct Fruits {
string name;
int weight, diameter, prize;
string colour, smell, taste;
}apple;
cout << "Apple Name?"; cin >> apple.name;
cout << "Apple weight?"; cin >> apple.weight;
.
.
you get the idea. I want sth like
vector<string> questions {"Apple Name?", "Apple weight?", ... };
for (int j = 0; j < "StructureSize of Fruits"; j++)
{
cout << questions[j]; cin >> apple.j; //apple.j is the key point I want to know
}
thank you very much!
you can't.
Arrays access works on the following way.
int A[4];
A[0] = 5;
A[1] = 7;
A[3] = 8;
You know a int (in some archytectures) use 4 bytes. So, the first element of A is in the address A, the next element of A is in the address A+4 what is equal to say A+sizeof(int)
When you do the first assignment A[0] the code is really something like A+sizeof(int)*0, in the next A[1] is something like A+sizeof(int)*1 If you see, any adjacent space of the array sums 4 bytes ( or sizeof(int) ) and goes on. If you see, you know that A have integers inside of it and you know the pattern to access each element the general pattern in a for loop would be A+sizeof(int)*j in your struct you don't know what is inside, you can't do A+sizeof(X)*j becouse X isn't fixed, so, if you want to do that you must implement it yourself, but It would be a pain becouse you have mixed datatypes.
If your struct is fixed, the strings for the user should also be fixed, then why you try to print them from a loop?, The vector need to allocate and that use spaces and gpu, I think is better if you just print each option and then read the user input.
You can.
But not without overkilling it.
#include <string>
#include <iostream>
#include <memory>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/for_each.hpp>
struct Fruit {
std::string name;
int weight, diameter, price;
std::string colour, smell, taste;
};
BOOST_FUSION_ADAPT_STRUCT(
Fruit,
(std::string, name)
(int, weight)
(int, diameter)
(int, price)
(std::string, colour)
(std::string, smell)
(std::string, taste)
)
std::array<char const *const, 7> questions = {
"Name ?",
"Weight ?",
"Diameter ?",
"Price ?",
"Colour ?",
"Smell ?",
"Taste ?"
};
int main() {
Fruit apple;
auto input = [i = std::size_t{0u}](auto &field) mutable {
do {
std::cout << questions[i] << ' ';
} while(!(std::cin >> field));
++i;
};
boost::fusion::for_each(apple, std::ref(input));
std::cout << "----------\n";
auto output = [i = std::size_t{0u}](auto const &field) mutable {
std::cout << questions[i] << ' ' << field << '\n';
++i;
};
boost::fusion::for_each(apple, std::ref(output));
}
I just learned that boost::fusion::for_each can't accept a mutable functor, hence the weird and a bit dangerous use of std::ref()... It's a shame, really.
Output :
Name ? Apple
Weight ? 150
Diameter ? 8
Price ? 1
Colour ? red
Smell ? good
Taste ? sweet
----------
Name ? Apple
Weight ? 150
Diameter ? 8
Price ? 1
Colour ? red
Smell ? good
Taste ? sweet
enum FruitValueProperty { weight=0, diameter, prize, lastValueProperty };
enum FruitStringProperty { name=0, colour, smell, taste, lastStringProperty };
struct Fruit {
int ValueProperty[3];
string StringProperty[4];
int Weight() { return ValueProperty[weight]; }
int Diameter() { return ValueProperty[diameter]; }
int Prize() { return ValueProperty[prize]; }
string Name() { return StringProperty[name]; }
string Colour() { return StringProperty[colour]; }
string Smell() { return StringProperty[smell]; }
string Taste () { return StringProperty[taste ]; }
};
Fruit fruit;
vector<string> questions {"Fruit weight?", ... "Fruit name?", ... };
for (int i=0; i<lastValueProperty; i++) {
cout << questions[j];
cin >> fruit.ValueProperty[i];
}
for (int j=0; i<lastStringProperty; i++) {
cout << questions[lastValueProperty+j];
cin >> fruit.StringProperty[j];
}
int dim = fruit.Diameter();
string sm = fruit.Smell();
If you used enums for the string properties (limiting the scope of what the values could be i.e an enum for names, enum for colors etc) then you could get your result by condensing the two loops down to one.
Alternatively you could have set property for the int values that takes a string and parses it to int and go that route but I honestly thing the above is the simplest and clearest means to almost achieve what you want.
This is my take on the matter using just vanilla c++ and evil macros and casts. Still definitely overkill though.
The basic plan is to provide an operator[] to return a variant-type proxy class which in turn handles the io streams.
This interface allows you the ability to expose the class members as you choose and in the order you want.
In order not to repeat myself and to avoid having the burden of keeping things in sync I declare the fields I want in a separate header and include the header twice but changing the macro between includes. If I now add a new field or remove one then I only have to change one place.
fruit_fields.h
//NO Header Guards desired since we want to include multiple times
DECLARE_FIELD(std::string, Fruit, name)
DECLARE_FIELD(int, Fruit, weight)
DECLARE_FIELD(int, Fruit, diameter)
DECLARE_FIELD(int, Fruit, prize)
DECLARE_FIELD(std::string, Fruit, colour)
DECLARE_FIELD(std::string, Fruit, smell)
DECLARE_FIELD(std::string, Fruit, taste)
Utility Classes
#include <iostream>
#include <string>
#include <assert.h>
#include <vector>
#include <array>
#include <typeinfo>
//TypeId info
namespace FieldType
{
static const size_t String = typeid(std::string).hash_code();
static const size_t Integer = typeid(int).hash_code();
};
/*
Field Proxy is a simple variant class
If you want to support more field types then more code is needed
*/
class FieldProxy
{
public:
FieldProxy(int* value)
: m_typeHash( typeid(int).hash_code() )
, m_intValue(value)
{}
FieldProxy(std::string* value)
: m_typeHash(typeid(std::string).hash_code())
, m_stringValue(value)
{}
operator int() const
{
assert(m_typeHash == typeid(int).hash_code());
return *m_intValue;
}
operator std::string() const
{
assert(m_typeHash == typeid(std::string).hash_code());
return *m_stringValue;
}
friend std::ostream& operator<<(std::ostream& os, const FieldProxy& field);
friend std::istream& operator>>(std::istream& os, FieldProxy& field);
private:
size_t m_typeHash;
union
{
int* m_intValue;
std::string* m_stringValue;
};
};
std::ostream& operator<<(std::ostream& os, const FieldProxy& field)
{
if (field.m_typeHash == FieldType::Integer)
{
os << *(field.m_intValue);
}
else if (field.m_typeHash == FieldType::String)
{
os << *(field.m_stringValue);
}
return os;
}
std::istream& operator>>(std::istream& is, FieldProxy& field)
{
if (field.m_typeHash == FieldType::Integer)
{
is >> *(field.m_intValue);
}
else if (field.m_typeHash == FieldType::String)
{
is >> *(field.m_stringValue);
}
return is;
}
//data to obtain pointer to given field
struct FieldInfo
{
size_t fieldType;
size_t offset;
};
Fruit Class
//The struct we actually care about
struct Fruit
{
public:
static size_t getNumberFields()
{
return m_numberFields;
}
FieldProxy operator[](size_t index);
private:
//First include just declares the class members as you might expect
// string name;
// int weight; etc
#define DECLARE_FIELD(t, c, f) t f;
#include "fruit_fields.h"
#undef DECLARE_FIELD
static FieldInfo m_fields[];
static const size_t m_numberFields;
};
//Work out where the fields are positioned in the class
FieldInfo Fruit::m_fields[] =
{
//second time around - define the macro to generate the data
//needed to build the FieldProxy
#define DECLARE_FIELD( t, c, f) {typeid(t).hash_code(), offsetof(c,f)},
#include "fruit_fields.h"
#undef DECLARE_FIELD
};
//calculate how many fields there are
const size_t Fruit::m_numberFields = sizeof(Fruit::m_fields) / sizeof(Fruit::m_fields[0]);
//Index to a FieldInfo object and use it to create a FieldProxy variant
FieldProxy Fruit::operator[](size_t index)
{
assert(index < m_numberFields);
auto& fieldInfo = m_fields[index];
if (fieldInfo.fieldType == FieldType::Integer)
{
return FieldProxy(reinterpret_cast<int*> (reinterpret_cast<char*>(this) + fieldInfo.offset));
}
else if (fieldInfo.fieldType == FieldType::String)
{
return FieldProxy(reinterpret_cast<std::string*> (reinterpret_cast<char*>(this) + fieldInfo.offset));
}
else
{
assert(false);
return FieldProxy((int*)nullptr);
}
}
Main
You can now index in to the fruit class quite simply:
int main()
{
Fruit apple;
std::array<std::string, 7> questions = {
"Name ?",
"Weight ?",
"Diameter ?",
"Price ?",
"Colour ?",
"Smell ?",
"Taste ?"
};
assert(questions.size() == Fruit::getNumberFields());
for (size_t index = 0; index < Fruit::getNumberFields(); ++index)
{
bool succeeded = false;
do {
std::cout << questions[index] << ' ';
if (!(std::cin >> apple[index]))
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<int>::max(), '\n');
}
else
{
succeeded = true;
}
} while (!succeeded);
}
std::cout << "----------" << std::endl;
for (size_t index = 0; index < Fruit::getNumberFields(); ++index)
{
std::cout << questions[index] << ' ' << apple[index] << std::endl;
}
return 0;
}
OK it is long since the thread was started.
For indexing a Struct with values of same type I have written this code:
struct DataValues
{
int Value1;
int Value2;
int Value3;
int Value4;
int Value5;
int Value6;
int Value7;
int Value8;
// the operator
// return the reference to make it settable.
int& operator[](int n)
{
// the idea, get the pointer of the first element
// and treat it as an array
return (&Value1)[n];
}
};
usage:
int main()
{
DataValues values;
values.Value4 = 6;
values[1] = 3;
values[0] = sizeof(DataValues);
return 0;
}
The downside is that you have to check the input parameter to avoid accessing memory which does not belong to the struct.
Chears
Related
Objects in Array are not being replaced by new Objects
This has been driving me insane for hours - I'm new to C++: I can't figure out why my programs thinks I want it do this. I have a class House class House{ private: int number; std::string family; public: House(int n, std::string f){ this->number = n; this->family = f; } House(){ this->number = 0; this->family = "unassigned"; } void whoLivesHere(){ std::cout<<"The"<<family<<"lives here."<<std::endl; } }; I have another class Neighborhood class Neighborhood{ private: int size; House houses[100]; public: Neighborhood(){ this->size=0; } void addHouse(House h){ this->houses[this->size] = h; this->size++; } void whoLivesHere(){ for(int i=0; i<this->size; i++){ this->houses[this->size].whoLivesHere(); } } }; And this is what is happening on my main. int main(){ Neighborhood n1; House h1(1,"Johnsons"); House h2(1,"Jones"); n1.addHouse(h1); n1.addHouse(h2); n1.whoLivesHere(); return 0; } And what I get on the Terminal is this. The unassigned lives here The unassigned lives here The unassigned lives here Why didn't the new objects replace the first two default objects? Why show three objects? If size should be 1. Thank you tonnes in advance!
You can make short work of this problem by using the tools the C++ Standard Library gives you, like this: #include <string> #include <vector> #include <iostream> int main() { std::vector<House> neighborhood; // emplace_back() forwards arguments to the constructor neighborhood.emplace_back(1, "Johnson"); neighborhood.emplace_back(2, "Jones"); // No need to track size, std::vector does that for you: size(), // but that's not even needed to iterate, you can just do this: for (auto& house : neighborhood) { house.whoLivesHere(); } return 0; } Here I've cleaned up your House implementation: class House { private: int number; std::string family; public: // Tip: Use constructor lists House(int n, const std::string& f) : number(n), family(f) { }; // Useful even for defaults House() : number(0), family("unassigned") { }; // Flag methods that don't modify anything as const void whoLivesHere() const { std::cout << "The " << family << " lives here at number " << number << "." << std::endl; } };
Changing a value of an element in an object that is stored in a vector in another object through an external function in C++
So made a class called ‘Item’, and the object of that class will have a 100% condition at the start, the Player stores items (with name “apple” in this case) whenever I tell him to. In the degradeT function I want to pass the whole vector containing the items that the player has picked up by far and then change the condition of each Item in that vector by -1 through the chCond function. first error: initial value of reference to non-const must be an lvalue second error: 'void degradeT(std::vector<Item,std::allocator<_Ty>> &)': cannot convert argument 1 from 'std::vector<Item,std::allocator<_Ty>>' to 'std::vector<Item,std::allocator<_Ty>> &' #include "pch.h" #include <iostream> #include <string> #include <vector> using std::cout; using std::cin; using std::endl; using std::string; using std::vector; using std::to_string; class Item { private: string name; // Item name float condition; // Item condition bool consumable; // Is the item consumable public: Item() {} Item(string a, float b, bool c) { name = a; condition = b; consumable = c; } Item(string a, bool c) { name = a; condition = 100.f; consumable = c; } string getName() { return name; } float getCond() { return condition; } bool isCons() { return consumable; } void chCond(float a) { // Change Item condition condition += a; } }; //----------------------- class Player { private: vector<Item> plItems; // Item container public: Player() {} void pickUpItem(Item a) { // Adding Items to inventory plItems.push_back(a); cout << a.getName() << " added to inventory!\n"; } void checkItemConds() { // Checking condition of all items for (unsigned int a = 0, siz = plItems.size(); a < siz; a++) { cout << plItems[a].getName() << "'s condition is: " << plItems[a].getCond() << "%\n"; } } Item returnItem(unsigned int a) { // Return a specific Item return plItems[a]; } int getCurInvOcc() { // Get cuurent inventory occupation return plItems.size(); } vector<Item> getPlItems() { // Return the vector (Item container) return plItems; } }; //------------------------- void degradeT(vector<Item>& Itemss); // Degrade item after some time //------------------------- int main() { Player me; // me string inp; // input int num = 1; // apple 1, apple 2, apple 3... while (inp != "exit") { cin >> inp; if (inp == "addApple") { Item apple(("apple " + to_string(num)), true); me.pickUpItem(apple); num++; } if (inp == "checkItemConds") { me.checkItemConds(); } if (inp == "timeTick") { // This doesn't have anything to do with time I just want to test the function manually degradeT(me.getPlItems()); } } system("PAUSE"); return 0; } void degradeT(vector<Item> &Itemss) { for (unsigned int a = 0, siz = Itemss.size(); a < siz; a++) { Itemss[a].chCond(-1); cout << Itemss[a].getName() << endl; } }
I'm not sure what your question is, but your error is related to the function void degradeT(vector<Item> & Itemss). This functions expects a reference but you are passing an r-value. You can either return a reference with getPlItems() or pass an l-value to degradeT.
Completing the body of a function in a separate header file in C++?
I have a homework task where I should complete the body of a function located in a separate file Find.h which should be completed in such a way that the code written below should compile successfully: #include <iostream> #include <string> #include <sstream> #include <vector> #include "Find.h" using namespace std; class Company { std::string name; int id; public: std::string getName() const { return this->name; } int getId() const { return this->id; } friend std::istream& operator>>(std::istream& stream, Company& company); }; std::istream& operator>>(std::istream& stream, Company& company) { return stream >> company.name >> company.id; } std::ostream& operator<<(std::ostream& stream, const Company& company) { return stream << company.getName() << " " << company.getId(); } int main() { using namespace std; vector<Company*> companies; string line; while (getline(cin, line) && line != "end") { istringstream lineIn(line); Company* c = new Company(); lineIn >> *c; companies.push_back(c); } string searchIdLine; getline(cin, searchIdLine); int searchId = stoi(searchIdLine); Company* companyWithSearchedId = find(companies, searchId); if (companyWithSearchedId != nullptr) { cout << *companyWithSearchedId << endl; } else { cout << "[not found]" << endl; } for (auto companyPtr : companies) { delete companyPtr; } return 0; } And here is my incomplete attempt for completion of the Find.h file (the program should output the id and the name of the company that matches the given id): #ifndef FIND_H #define FIND_H #include "Company.h" #include <vector> using namespace std; Company* find(vector<Company*> vc, int id) { for (int i = 0; i < vc.size(); i++) { if (vc[i]->getId() == id) { //I do not know what to write here as to return a pointer //to the required element so as to fulfil the requirement? } } return nullptr; } #endif // !FIND_H
One alternative is to define a functor or function object and use the std::find algorithm: struct Find_By_ID { int id_to_find; bool operator==(const Company& a) { return a->getId() == id_to_find; } } //... std::vector<Company> database; // Note, not a vector of pointers //... Find_By_ID functor; functor.id_to_find = searchId; std::vector<Company>::iterator iter = std::find(database.begin(), database.end(), functor); Edit 1: No new necessary You don't need to use new when building your database: Company c; std::vector<Company> database; while (std::cin >> c) { database.push_back(c); } The std::vector::push_back() method will make a copy and append it to the vector. The vector will allocate memory for the item as necessary. Edit 2: Brute force You could use a custom brute force method instead of a functor: const std::vector<Company>::const_iterator iter_begin(database.begin()); const std::vector<Company>::const_iterator iter_end(database.end()); std::vector<Company>::const_iterator iter; for (iter = iter_begin; iter != iter_end; ++iter) { if (iter->getId() == searchId) { break; } } if (iter != iter_end) { std::cout << "Customer found by ID.\n"; } If you want to modify the found Customer, change the iterator types to: std::vector<Customer>::iterator as appropriate.
For the specific issue in the .h file for loop try: return vc[i]; //vc is a vector of Company pointers, this returns the pointer at vc index i For the output part consider: cout << companyWithSearchedId->getId() << " " << companyWithSearchId->getName() << endl; There are more issues here at as a whole, take your time and work through it.
STXXL Dealing with strings
i am dealing with large dataset. May i ask you how it is possible to store strings in the classes i want to use with stxxl? I have read several discussions and everywhere was said that string is not POD type therefore it cannot be stored in the stxxl::vector, but i am not sure,because i tried it and i checked the data and everything seems to be fine. i have also saw an approach here https://github.com/Project-OSRM/osrm-backend/blob/725b93a961625a7b04d54806d7e0f80252a6bcd0/extractor/extraction_containers.hpp and they use stxxl::vector, so maybe the library got updated to support std::string? class HighWay { private: uint64_t id; string name; int speed; string attributes; //additional attributes of way string edges; //written uint64_t from,uint64_t to, int distance written as string string nodes; //vector<uint64_t> written as string public: HighWay() = default; void setId(uint64_t _id) { id = boost::lexical_cast<string>(_id); } void setName(string _name) { name = _name; } void setSpeed(int _speed) { speed = _speed; } void setAttributes(string _attributes) { attributes = _attributes; } void setEdges(string _edges) { edges = _edges; } void setNodes(vector<uint64_t>refs) { stringstream s; uint64_t i = 0; for (; i < refs.size()-1;i++) { s << boost::lexical_cast<uint64_t>(refs[i]) << " "; } s << boost::lexical_cast<uint64_t>(refs[i]); nodes = s.str(); } uint64_t getId() { return id; } string getName() { return name; } int getSpeed() { return speed; } string getAttributes() { return attributes; } string getEdges() { return edges; } std::vector<int64_t> getNodes() { stringstream s(nodes); uint64_t node; std::vector<int64_t> result; while (s >> node) { result.push_back(static_cast<int64_t>(node)); } return result; } }; I have also created code which stores the strings as POD,storing the string in vector of char and in map remembering lower and upper bound index in the array. But this approach leads to many std::maps used in the application. //class to store in map struct TypeName{ uint64_t start; uint64_t end; }; std::istream& operator >> (std::istream& i, TypeName& entry) { i >> entry.start; i >> entry.end; return i; } std::ostream& operator << (std::ostream& i, const TypeName& entry) { i << entry.start << " "; i << entry.end; return i; } struct PoiCategories{ uint64_t start; uint64_t end; }; std::istream& operator >> (std::istream& i,PoiCategories& entry) { i >> entry.start; i >> entry.end; return i; } std::ostream& operator << (std::ostream& i, const PoiCategories& entry) { i << entry.start << " "; i << entry.end; return i; } //object i want to store struct Poi { Poi() = default; uint64_t id; char type; uint64_t id_in_pois; //id in vector pois void addCategories( vector<int> &kats, //categories to insert stxxl::vector<uint64_t> &categories, //vector to store category std::unordered_map <uint64_t, PoiCategories> &idPoi_categories //index to vector categories to retrieve all categories for Poi ) { size_t start = categories.size(); for (auto & kat : kats) { categories.push_back(kat); } size_t end = categories.size() - 1; idPoi_categories.insert(make_pair(id, PoiCategories{start, end })); } vector<int> getCategories( stxxl::vector<uint64_t> &categories, std::unordered_map <uint64_t, PoiKategorie> &idPoi_categories ) { std::vector<int> result; PoiCategories bounds = idPoi_categories.find(id)->second; for (size_t i = bounds.start; i <= bounds.end; i++) { result.push_back(categories[i]); } return result; } }; Problem in my application is that i am storing a few string data, which are mainly names of streets and POIs. Maybe i am using wrong library. If so,can you recommend me a better approach to store data while preprocessing?
It's indeed banned, but the symptoms of violating the no-POD rule are generally unpredictable. It may very well appear to work as long as the strings all fit in memory, but in that case you didn't need the STXXL anyway.
binary search string 2d arrays
My question is to find the string in 2d array and if it match display binary no. Pience of my cide string inst[37][3]={{"ld","00001","C2"},{"st","00011","C2"},{"la","00101","C2"},{"ldr","00010","C1"}, {"lar","00110","C1"},{"str","00100","C1"},{"add","01100"," "},{"addi","01101","C2"}, {"sub","01110"," "},{"neg","01111"," "},{"or","10110"," "},{"ori","10111","C2"}, {"and","10100"," "},{"andi","10101","C2"},{"not","11000"," "},{"shr","11010","C3"}, {"shra","11011","C3"},{"shl","11100","C3"},{"shc","11101","C3"},{"br","01000","C3"}, {"brl","01001","C3"},{"brlnv","01001"," "},{"brzr","01000"," "},{"brlzr","01001"," "}, {"not","11000"," "},{"brnz","01000"," "},{"brlnz","01001"," "},{"brpl","01000"," "}, {"brmi","01000"," "},{"brlmi","01001"," "},{"nop","00000"," "},{"stop","11111"," "}, {"een","01010"," "},{"edi","01011"," "},{"rfi","11110"," "},{"svi","10000"," "}, {"ri","10001"," "}}; int last=36, initial=0 , mid, index; for(int i = 0; i < icount-1; i++) { //display arrays for(int j = 0; j < 4;j++) { cout << input[i][j] << " "; // this is for check first column that consist inst and then convert to binary code if(j==0) { while(last>=initial) { mid=(last+initial)/2; if(input[i][0]==inst[mid][0]) { index=mid; } else if(input[i][0]>inst[mid][0]) { initial=mid+1; } else last=mid-1; } cout<<" "<<inst[index][1]<<" "; } } it's like output not display the correct binary code. Any kind of help I'm really appreciated. Thanks you. * I don't want to use return mid and create another function
Your search would be soooo much simpler if you reorganized your data: struct Data_Record { string command; string value; string other; // Here's what makes the search work better: bool operator==(const Data_Record& dr) { bool is_equal = false; if (command == dr.command) { if (value == dr.value) { if (other == dr.other) { is_equal = true; } } } } bool operator<(const Data_Record& dr) { return command < dr.command; } }; const Data_Record inst[37] = { /* ... */}; Data_Record const * p_item_found = NULL; Data_Record key = {"and", "", ""}; p_item_found = std::binary_search(&inst[0], &inst[38], key); if (p_item != &instr[38]) { cout << "Found it\n"; } else { cout << "Item not found.\n"; } You can do other cool stuff like overloading operator<< and operator>> for custom output and input. Edit 1: OOP hierarchy Many instruction sets have groups that share commonality. For example, jump or branch instructions all have a destination address. Math instructions may have the same operands. I recommend having a hierarchy of classes (or structures). Research "C++ Factory Design Pattern". class Instruction { public: virtual void Print_Annotated(std::ostream& output) = 0; virtual void Instruction_Counter Execute(void) = 0; protected: std::string opcode; std::string instruction_value; }; class Jump_Instr_Category : public Instruction { protected: unsigned int destination_address; }; class Math_Instr_Category : public Instruction { protected: std::string parameter1; std::string parameter2; }; You use the Factory Pattern to return a pointer to the Instruction base class. The User's program would be a simple as: std::vector<Instruction *> program;