There is a class say Person which has member variables name, email, mobile, gender.
Now, we are getting this information in an array.
std::vector<std::string> a[] = {"XYZ", "xyz#mail.com", "1234567890", "Male"};
Person p;
Now, instead of writing it like:
p.name = a[0]; p.email = a[1]....
I want something like this for dynamic allocation as well as for reducing code lines:-
std::vector<std::string> b[] = {"name", "email", "mobile", "gender"};
int len = a.size();
for (int i=0; i < len ; i++)
{
set_value(p, b[i], a[i]);
}
How to write a function like set_value, or is there any way to do something like this in c++ ?
Right now in above example we have a vector size of 4, but it can be in size of 10 or 50 or more also. In that case if we don't have the way of setting the values dynamically then we might have to write same number of lines for setting every member variable .. .?
I think one of the solutions in which you are talking about is to implement all the fields of the Person class as a map<K, V>, which will store all of yours properties. In this case you will be able to refer to the value by the key of a map.
I would not recommend this solution for you. You can think of set_value function as a way in which the names from array a should refer to the name of the properties or rather how does those names should be linked to the functions which could invoke a proper property-right set method.
If those setting functions you are interested in use the same base type as a parameter, you could create map of strings and function pointers.
#include <map>
#include <vector>
#include <string>
#include <iostream>
using namespace std;
class Person{
std::map<std::string, std::string> _properties;
public:
Person() = default;
~Person() = default;
void set_values(vector<string> & fields, vector<string> & values){
if(fields.size() != values.size()) return;
for(int i = 0; i < fields.size(); ++i){
_properties.insert_or_assign(fields[i], values[i]);
}
}
void print(){
for (auto const &it : _properties)
std::cout << it.first << " => " << it.second << '\n';
}
};
int main(){
Person p;
vector<string> fields = {"email", "name"};
vector<string> values = {"a#b.com", "Andrew"};
p.set_values(fields, values);
p.print();
vector<string> fields2 = {"name"};
vector<string> values2 = {"Tom"};
p.set_values(fields2, values2);
p.print();
return 0;
}
This minimal working example shows what you want to get and it has a lot of constraints, such as it assumes that all of the values are stored as a std::string which can not be a good in every case. It produces a following output after applying the first set of fields and values:
email => a#b.com
name => Andrew
After the second one, the email stays the same but name is updated as follows:
email => a#b.com
name => Tom
I've compiled it using g++ version 7.1 using using following command:
g++ --std=c++1z main.cpp on my Fedora 26.
Well, there are no easy way to set a variable by name... There are library that allows to do something like that (for ex. boost fusion) but in practice, you could also write a simple function if you have only a few functions to write on a few classes.
One simple possibility assuming you want to reuse the code that fill a object:
void set_values_of(Person &p, const std::vector<std::string> &data)
{
assert(data.size() == 4); // put whatever error handling you want...
p.name = data[0];
p.email = data[1];
p.mobile = data[2];
p.gender = data[3];
}
And if you prefer, you might change second argument for another source (for ex. a stream or one line a string). At that point, it really depends on your application.
If your prefer immuable objects, you might also consider having a factory free function:
Person create_from(const std::vector<std::string> &data) { ... }
Related
I'm new to C++ and I'm trying to pass a collection of a nonvariable amount strings to a function that exists in separate class file in an easy to read manner as such:
//main in Caller.cpp
int main()
{
string details[] = {"Name","Height","Weight"};
/* vector<string> detailsV = {"Name","Height","Weight"};
* Would like to use a vector but can't do this because vector cannot be
* initialized to = {...} in C++
*/
Person p = Person();
p.inspectDetails(details);
}
//Person class in Person.cpp
void inspectDetails(string details [])
{
int sz = sizeof(details); // this will result in details = "Name" only
}
I've tried:
//Person class in Person.cpp
<template size_t N>
void inspectDetails(string (&details)[N])
{
int sz = sizeof(details);
}
However, I don't now how to let the main in the Caller class know about the <template size_t N> which might allow me to use an array of a non-explicit amount. I seem to get an error of "no suitable conversion of std:string[3] to std:string" when trying to call inpectDetails this way.
What is the best way to pass a collection of strings of a non-explicit amount to a function outside of the Caller class whilst maintaining the ability to hardcode the collection's contents like so Collection c = {"...", "...", "..."} in C++?
Is there an easier way to pass the full collection of strings to a function with a pointer to a vector or something of that sort?
Use a std::vector< std::string > and pass it by reference if you need to change its contents, const reference if you don't need to change them. This is the simplest, most flexible, and clearest way.
Thus:
void inspectDetails( std::vector< std::string > & details );
std::vector< std::string > details = { "Name","Height","Weight" };
inspectDetails( details );
cf. http://en.cppreference.com/w/cpp/container/vector
Passing by const reference is an option. This is what a possible program might look then:
#include <iostream>
#include <string>
#include <vector>
void inspectDetails( const std::vector<std::string> & details )
{
int v = details.size();
std::cout<<v<<std::endl;
}
int main()
{
std::vector<std::string> details = {"name", "height", "weight"};
inspectDetails(details);
return 0;
}
Rob K answered right to you question. I will add that sometimes it's usefull to use std::array rather than a simple array : http://en.cppreference.com/w/cpp/container/array For example, you can easily know the size of your array.
New to C++, looking to replicate some functionality available in C# involving replacing an object array member with a newly constructed array.
class Car {
int id;
Car() {}
};
class Garage {
Car cars[1];
Garage() {}
void addCars(Car crs[]) {
//...do update here
}
};
In C# I could do something like:
addCars(Car[] crs){
Car[] temp = new Car[cars.Length + crs.Length];
for(int i = 0; i < cars.length; i++){
temp[i] = cars[i];
}
for(int i = 0; i < crs.Length; i++){
temp[i + cars.Length] = crs[i];
}
cars = temp;
}
Or Array.Resize, Array.Copy.
Can I declare an array and replace an existing object instance member array?
If this isn't possible: How practical are arrays in C++? I can see something like Excel utilising them (if they aren't modifiable) but it seems like it would be really limited. I can see why memory allocation might constrain this but obviously I'm from the easier side of the C fence.
Thanks.
As an example, here would be some sort of C++ equivalent to your example. It probably has some vague semblance to the C# equivalent.
#include <initializer_list>
#include <iostream>
#include <ostream>
#include <vector>
using std::vector;
using std::cout;
using std::endl;
using std::ostream;
using std::initializer_list;
struct Car {
int id;
Car(int newId) : id{newId} {}
};
ostream& operator<<(ostream& o, Car const& car) {
o << car.id;
return o;
}
struct Garage {
vector<Car> cars;
Garage() {}
void addCars(initializer_list<Car> l) {
cars.insert(cars.end(), l.begin(), l.end());
}
};
ostream& operator<<(ostream& o, Garage const& garage) {
char const* sep = "";
for (auto const& car : garage.cars) {
o << sep << car;
sep = ", ";
}
return o;
}
int main() {
Garage garage;
garage.addCars({7, 8, 99, 1000});
cout << garage << endl;
return 0;
}
Can I declare an array and replace an existing object instance member array?
No, std::array and type[] array sizes are known at compile time. If the new array has the same size as the existing array, a copy is needed (via e.g., memcpy)
You could theoretically use a pointer instead, e.g.
Car cars* = nullptr;
// ...
cars = new Car[size];
// ...
delete[] cars;
However, this puts the burden of memory tracking on you (I had to manually call delete[]). Instead, use a std::vector, which wraps up all that logic for you.
std::vector<Car> cars;
Car c;
// ...
cars.push_back(c);
How practical are arrays in C++?
Quite practical. A lot of high performance computing relies on arrays whose sizes are known at compile time for performance reasons.
The C++ arrays and std::array are fixed size at compile time. Their size can't change at runtime. You can copy an std::array onto another std::array ("replace"). But you can't do so for the C++ build-in arrays: these need to be copied element by element for example with std::copy().
If you need to dynamically resize your container, you should consider std::vector instead. These are much more flexible. And you can of course copy or move them as a whole.
I have written a function in c++ which receives a struct as a input. The struct object received has two arrays. I need to use both the arrays for different purposes. The array names have been created in a certain format. How to retrieve array names in a string.
struct INFO
{
float fADataLHS[3] = {1,2,3};
float fADataRHS[3] = {4,5,6};
Struct INFO has been defined where two arrays have been defined an initialized. The function useStruct uses both the function for different purposes.
void useStruct(struct *INFO)
{
--------;
--------;
}
int main()
{
struct INFO info;
useStruct(info);
}
I want a method in which I can retrieve the name of the array as for ex. fAdataLHS and store it to a string. The idea is to find the sub-string LHS and RHS from the string names and process then accordingly.
PS: I am quite new to c++.
I will go simple as you're a begginer to C++.
If you want to use both of arrays for different purposes, just doit. For instance:
void use_array_for_different_purposes(INFO *info)
{
// Purpose one, printing values using fADataLHS.
for (int i = 0; i < 3; i++) {cout << info->fADataLHS[i] << endl;}
// Purpose two, computing total sum using fADataRHS.
int acum;
for (int i = 0; i < 3; i++) {acum += info->fADataRHS[i];}
}
As you can see, you don't need to get the arrays names as strings values.
If I understand corectly, your use case is this: you have two (or more) names and each has a float array associated with it. You want to get the array by name and process the data.
Consider this code:
class INFO
{
std::map<std::string, std::vector<float>> vectors;
public:
INFO() : vectors{}
{
vectors["fADataLHS"] = { 1, 2, 3 };
vectors["fADataRHS"] = { 4, 5, 6 };
}
const std::vector<float>& operator[](const std::string& key) const // access vector by key
{
return vectors.at(key);
}
};
void useStruct(const INFO& info) // pass instance by const reference
{
std::cout << info["fADataLHS"][0] << "\n"; // access element 0 from the fADataLHS array
// get the entire array:
const auto& arr = info["fADataRHS"];
// this will throw a std::out_of_bounds
const auto& arr = info["non-existent-key"];
}
EDIT: A few other notes:
in C++ try not to use float - use double instead
if you need to alter the vector contents from client code, add a non-const version of the operator[]
This if for my homework.
I have a class called Student that takes 3 parameters (id, name, class) and I want to store each student in an array called Roster (which can only have 7 students).
The user will provides input to add or remove students. Thus, I have to manage the array by creating or deleting students. So if the user specify the student ID, I have to remove him for the array.
I tried to use a fixed array, but I'm struggling to make it works. Is there a better way to implement this?
I must not use a vector or any STL container.
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <iostream>
#include <string>
static const int SIZE = 7;
class Student {
private:
int student_id;
std::string name;
std::string classification;
public:
Student(int, std::string, std::string); // constructor; initialize the list to be empty
~Student();
void print();
};
#endif
student.cpp
#include <iostream>
#include <string>
#include "student.h"
#define PROMPT "class> "
using namespace std;
Student::Student(int a, string b, string c){
student_id = a;
name = b;
classification = c;
}
Student::~Student(){
//delete Student
}
void Student::print(){
cout<<"Enrolled:"<<endl;
cout<<student_id<<"-"<<name<<"-"<<classification<<endl;
}
main.cpp
#include <iostream>
#include <string>
//#include <sstream>
#include "student.h"
#define PROMPT "class> "
using namespace std;
//**** Implement Error Handling ****\\
enum errorType {
UNKNOWN_ERROR,
INPUT_ERROR,
HANDLER,
NUM_ERRORS
};
// error messages
string errorMessage[NUM_ERRORS] = {
"Unknown Error\n",
"Input Error\n",
};
// error handler
void handleError(errorType err) {
if(err > 0 && err < NUM_ERRORS)
cout<< "Error: "<< errorMessage[err];
else cout<< "Error: "<< errorMessage[UNKNOWN_ERROR];
}
//**** END Error Handling ****\\
void enroll(Student newStudent){
cout<<"test";
Student roster[SIZE];
for(int i=0;i<SIZE;i++){
newStudent->roster[i];
}
}
void handleInput() {
int id; string n, c;
cin>>id>>n>>c;
Student newStudent(id,n,c);
newStudent.print();
enroll(newStudent);
//cout<<"hello3"<<endl;
return;
}
int main() {
//Student newStudent; /* <-- why doesn't this work?!*/
string input = "";
bool finished = false;
cout<<PROMPT; // prompt the user
while(!finished) {
if(input!="") cout<<PROMPT;
cin>>input;
if(input=="enroll") {
cout<<PROMPT<<"Enroll student:"<<endl;
handleInput();
}
else if(input=="drop") {
cout<<PROMPT<<"Enter ID:"<<endl;
}
else if(input=="roster") {
cout<<"This will print formatted list of students"<<endl;
}
else if(input=="quit") {
finished=true;
}
else handleError(errorType(1));
}
}
Since it is a homework, I'd like to point out some mistakes you did because it is important to understand what you are doing in the first place.
You must not program by coincidence, but by trying to understand exactly what's going on. By doing that you will become better and better and the answers should fall in place.
What you've done
So, from what you are describing, the array is fixed. Thus it is a good idea to use a constant as you did (SIZE).
However, as we can see below you a declaring an array of size SIZE in the function. By doing that, your array is like a temporary variable, because its scope is inside the function. Each time you call this function, the array will be declared again and then deleted at the exit. So it should be declared outside.
void enroll(Student newStudent)
{
cout<<"test";
Student roster[SIZE]; // Here 'roster' will be available only inside the function.
for(int i=0;i<SIZE;i++)
{
newStudent->roster[i]; // Here there is few mistakes see my explanation below*
}
}
If we look at this part:
newStudent->roster[i];
First of all, the arrow '->' is used with pointers. The dot '.' is used with objects. In both case, it does the same thing, access to public members of Student.
Since you passed
void enroll(Student newStudent)
you should use '.' instead.
newStudent.SomeOfYourMembers;
If the parameter was a pointer to a Student
void enroll(Student *newStudent)
Then, you'd have to use the arrow '->' like you did.
Back to the original statement:
newStudent->roster[i];
This means, you want to access to 'roster' array at position 'i' inside your Student object (newStudent). As you can see in your code, roster is not declared inside Student (and should not be since you want an array of Students), so that won't work.
Guidelines
As I mentionned, your array should be outside the function, so at a higher scope.
Then, if you need an array of student, basically, 'roster[i]' will give you access to the student 'i'. Thus, if you want to print the student, you would do something like that:
roster[i].print();
This would be valid because 'print()' is defined as public.
In order to store a student inside the array, you can do something like:
roster[i] = new Student(0 /* id*/, "name", "classification");
But don't forget, each time you use new, you have to balance it with a delete. And if you are creating the student like this in a loop, you will have to clean them the same way:
for(int i = 0; i < SIZE; ++i)
{
delete roster[i];
}
Good luck!
Don't hesitate if there is there anything that I could clarify. I hope this helps!
Edit: In reply to your first comment.
Concerning the roster array
No, it is not mandatory to create a class roster you could declare roster in the main.cpp.
The key concept is that by defining
Student roster[SIZE];
the array will contains objects of type Student.
What roster[i].print() means is that you are printing one of the Student of that array, in fact the one at position 'i'.
Concerning the print() function
What is powerfull with Object Oriented language, each object will have the same print() function. So, you do not need to convert the array to string.
However, if you want a string to be printed out (or returned) you can write the code inside the print() function that will do this job.
The advantage of this, is that if further on you need to change your array in some ways, your print() function will always work.
Concerning the Delete
When you are doing something like this on an array that contains objects:
delete roster[i];
It will delete the object at the position 'i'. Thus, the destructor of that Student 'i' will be called. If your object Student would contains other object, you would have to delete them in the destructor.
Further notices
Since ID is an input that you are storing into a string, you will have to convert the ID to the same type of the student_id, which is a int. Then you can always write a loop for each student and check their ID to delete the proper one.
Concerning the container, a fixed array might not be the best to achieve this job. You might want to look the LinkedList concept.
It doesn't make much sense for enroll to be a member function, so
I'd wrap the roster into a class to get automatic clean up of my
pointers.
#include <cstddef>
struct Student {};
class Roster
{
private:
static const size_t size = 7;
// non-copyable
Roster(const Roster&);
Roster& operator=(const Roster&);
public:
Roster() {
for(unsigned i = 0; i < size; ++i) {
roster_[i] = NULL;
}
}
~Roster() {
for(unsigned i = 0; i < size; ++i) {
delete roster_[i];
}
}
// enroll by copy
bool enroll(const Student& s) {
for(unsigned i = 0; i < size; ++i) {
if(roster_[i] == NULL) {
roster_[i] = new Student(s);
return true;
}
}
// out of space
return false;
}
// enroll by taking ownership
bool enroll(Student* s) {
for(unsigned i = 0; i < size; ++i) {
if(roster_[i] == NULL) {
roster_[i] = s;
return true;
}
}
// out of space
return false;
}
private:
// data
Student* roster_[size];
};
int main()
{
Roster r;
Student s;
r.enroll(s);
Student* sp = new Student();
r.enroll(sp);
return 0;
}
What about this?
Student * roster[2];
roster[0] = new Student(5,"first","2A");
roster[1] = new Student(2,"Second","5B");
Ps:
Enrol and Size shouldn't be members of the student class.
Print should ideally be externalized and a ToString function should be added instead.
You should use the inline constructor initialization instead:
Student(int a,string b,string c):id(a),name(b),class(c){}
You've used the keyword class as a variable name of type string. You shouldn't do that. Does it even compile like that?
enroll should have two arguments: void enroll( Student enrollee, Student Roster[]). You should probably change the name of Roster to roster because it's not a class and typically class names are capitalized.
If your array will only ever have 7 students then you could use some sentinel value to mark that the current student as an invalid student. Perhaps the id will be -1 to mark this. It means basically that you need some way to keep track of which spots in the array you can still use. If you don't do this then declaring an array of Students will get you an array of students with garbage member variables. You wouldn't be able to tell which students are real ones and which are just place holders for when someone new enrolls in the class. I would create a default constructor of Student and initialize its member variables like this:
id=-1;
name="";
name_of_class="";
I changed the name of your string class to avoid confusion.
After all that, enroll would look something like this:
void Student::enroll( Student enrolee, Student roster[]){
//search through roster to check for the first student with an
//id of -1
//if there are no students with id of -1, produce an error message
//that the class is full
//overwrite the student with id of -1 with the id, name, and
//name_of_class of enrollee
}
Although I'm not sure what exactly string class is there for. Does it store what class the Student is in? Is it their year in school like Freshman, Sophomore?
If you're suppose to use dynamic allocation of roster, though, it's a different story, but you said it will only ever have seven students.
I have Class Email,
there is parameter "bcc" in her constructor.
Its actually list of emails for copies.
There is no fixed number of these emails and later i have to have possibility to extend this list.
//constructor prototype
Email::Email(vector<string> bcc)
So i want to use type vector or list for that and function push_back().
How can i make a new instance with bcc emails?
I need actually declaration with definition for my list.
I've found this definition with iterator for integer type:
int myints[] = {16,2,77,29};
Email myEmail(vector<int> (myints, myints + sizeof(myints) / sizeof(int) ));
, but its not very user friend and i need it with strings.
Is there something like this?
Email myEmail(vector<string> ("first","second","third"));
Apart from C++0x list-initialization, there is the Boost.Assign library which should do similar things.
If you have C++0x, you can do vector { "first", "second", "third" }. Else, you will have to create a new vector in scope somewhere and manually push on each that you want, then construct.
Also, you should really take that vector by reference, it's really quite large.
You should use a std::vector unless you know that you will need to insert items into the middle, not on the end.
If you're not using C++0x then you have no access to initialisers. Could you add a constructor that takes any old iterator, viz:
#include <vector>
#include <list>
#include <iostream>
struct Email
{
typedef std::vector<std::string> BccType;
BccType m_bcc;
template <typename T>
Email(const T& iter, const T& end)
:m_bcc(iter, end)
{
}
// Purely here for illustrative purposes...
void display()
{
std::cerr << m_bcc.size() << " addresses..." << std::endl;
for (BccType::iterator iter = m_bcc.begin(), iterEnd = m_bcc.end(); iter != iterEnd; ++iter)
{
std::cerr << *iter << std::endl;
}
}
};
int main(int, char*[])
{
// Plain old char* array...
const char* bcc[] = {"Jeff", "Joel", "Larry", "Brin"};
const size_t count = sizeof bcc / sizeof bcc[0];
Email email(&bcc[0], bcc + count);
email.display();
// STL container variation...
std::list<std::string> names;
names.push_back("Bill");
names.push_back("Steve");
Email reply(names.begin(), names.end());
reply.display();
return 0;
}
There is, of course, no reason why you can't have a ctor that takes const BccType& (typedefed for brevity and maintainability) additionally. Note that I suggest passing this by reference to save copying the std::vector twice.