Dynamicly setting which parameter to extract - c++

Having structure
struct Person{
Person(int a , int i):age(a),id(i){};
int age;
int id;
}
Is it possible to pass which argument to exctract as argument in function? Something like
int extract( Person * p , param ){
return p -> param;
}
which would return id , if used it like
extract( p , "id" )
and age if i used it like
exctract(p , "age")
Is something like this possible in c++?

You can use pointers to class members.
struct Person{
Person(int a , int i):age(a),id(i){};
int age;
int id;
};
int extract(Person* p, int Person::* param)
{
return p->*param;
}
and you'd use it like this:
extract(p, &Person::id);
demo

You can do it with preprocessor abuse, and more notably, without using string comparisons, which seems like what you want to do.
#include <iostream>
#define extract(p, i) (p->i)
struct Person{
int age;
int id;
};
int main() {
Person p;
p.age = 100;
p.id = 30;
std::cout << extract((&p), id) << '\n' << extract((&p), age) << '\n';
}
Not that I suggest doing this, though.

You can use Map<char*, int>which can do exactly that (but arguably is a bit of owerkill).
Or just plain char[][] and check equality with the parameter in a for loop.

Related

How can I get a cstring into a class?

I'm going to using cstring finish the program
now I required to get the name of PokemonWorld from user
and initialize it
But I can't get the cstring from main() into class PokemonWorld
I tried cin >> world.setName and cin >> name; world.setName(name)
both of which failed
class PokemonWorld {
private:
char name[10];
public:
void setName(char x[]) {
*name = x;
};
char* getName(){
return name;
};
};
void main() {
PokemonWorld world;
int number;
char name[10];
cout << "What is the World Name ?" ;
cin >> name;
world.setName(name);
Also I cannot using getName to return the name that assigned into PokemonWorld
Here is the error code:
Error C3867 'PokemonWorld::getName': non-standard syntax; use '&' to create a pointer to member
Should I create PokemonWorld as pointer?
With cstring, you have to use c string manipulator. You definitively can't assign an array to another one. For your issue, use strncpy_s:
void setName(char x[]) {
strncpy_s(name, 10, x, 10);
};
Also, to avoid issue, your getter should be:
const char * const getName() const {
return name;
};
Also, to compare, use strncmp, to concatenate use strncat, ...
Or, because you use c++, use std::string
You need to set the name first. In your main method,
PokemonWorld world;
int number;
char name[10];
cout << "What is the World Name ?" ;
cin >> name;
world.setName(name);
//Then get the name
world.getName(name);
You are not using a cstring (most people who hear "cstring" think of the CString class of the MFC library).
You are using a "C" string, or a raw char pointer. This is bad practice in general, as it involves performing memory management manually, and unless you solve the problems that come with using raw pointers, you will simply have those problems in your code.
setName should be:
void setName(char const * const x)
{
auto length = std::min(std::strlen(x), 9); // only copy up to 9 characters
std::copy_n(x, length, name);
name[length] = '\0';
}
... or (as already pointed out):
strncpy_s(name, 10, x, 10);
getName should be:
char const * const getName() const
{
return name;
}
This code is almost correct:
char name[10];
cout << "What is the World Name ?" ;
cin >> name;
world.setName(name);
... but you should initialize name where you declare it:
char name[10] = { 0 };
Also, what will you do if the user inputs more than 10 characters? In that case the stack of your program will become corrupted.
You could avoid the problem by using a std::string.

Extract elements from a vector of object

Given a vector of objects, is there an elegant way to extract its member? I am currently just using a for loop but it would be nice if there is a way to do it. Example:
#include <vector>
struct Object {
int x;
float y;
};
int main() {
std::vector<Object> obj;
// Fill up obj
std::vector<int> all_x = obj.x; // Won't work obviously
}
With range-v3, it would simply be
std::vector<int> xs = objs | ranges::view::transform(&Object::x);
or just use the view:
auto xs = objs | ranges::view::transform(&Object::x);
Demo
As std::vector (or c++ in general) does not support covariant aggregation, there is no syntactically pretty way to do what you want.
If you really want to initialize all_x with x members of obj elements, then you can define a new iterator class, like that:
class getx_iter : public vector<Object>::iterator
{
public:
getx_iter(const vector<Object>::iterator &iter) : vector<Object>::iterator(iter) {}
int operator*() { return (*this)->x; }
};
Working code example
If you're okay with initializing an empty vector and then filling it, std::transform with a labmda is a clearer option (as #andars suggested).
You could also avoid extra initialization by using vector::reserve() and back_inserter:
xs.reserve(foos.size());
std::transform(foos.begin(), foos.end(), back_inserter(xs), [](Foo f){return f.x;});
Also notice that while x is a private member of Object and has no getters, it will be quite hard to extract it.
I can't think of a really good way.
One alternative would be to use std::transform with a lambda.
#include <vector>
#include <algorithm>
class Foo {
public:
Foo(int x_): x(x_) {}
int x;
};
int main() {
std::vector<Foo> foos;
for (int i = 0; i<10; i++) {
foos.push_back(Foo(i));
}
std::vector<int> xs;
xs.resize(foos.size());
std::transform(foos.begin(), foos.end(), xs.begin(), [](Foo f){return f.x;});
}
Some template and macro magic, and it works:
#include <vector>
#include <algorithm>
using namespace std;
class Foo {
public:
Foo(int x_): x(x_) {}
int x;
};
#define GETFIELD(type, field) [](const type & obj){return obj.field;}
template<typename T,typename U, typename TMapper>
void MapVector(vector<T>& src, vector<U>& dst, TMapper mapper) {
for (const auto& it: src) {
dst.push_back(mapper(it));
}
}
#define MapVectorField(src, dst, field) MapVector(src, dst, GETFIELD(decltype(src)::value_type, field))
int main() {
vector<Foo> vFoo;
for (int i = 0; i < 10; i++) {
vFoo.push_back(Foo(i));
}
vector<int> vX;
MapVector(vFoo, vX, GETFIELD(Foo, x));
MapVectorField(vFoo, vX, x);
for (int i = 0; i < vX.size(); i++) {
printf("%d\n", vX[i]);
}
}
Of course, remember that it is not very good to name macro MapVectorField as it is function, or write using namespace std in production.
Here's another macro that's pretty easy to use. It does require the programmer to know that the internal loop variable name is "e", for element. If you keep that in mind, it will work for both fields or methods, and also for normal objects or pointers to objects.
The macro is simply:
#define map(vTarget, vSource, eField) \
for (auto e: vSource) { vTarget.push_back(eField); }
For example, suppose we have a class named Person which has a string name field and an int age field, and we want to extract just the names into a new vector. An example usage might then be:
map(names, people, e.name);
Note the "e" in the third parameter. This is required because the macro uses the variable "e" to iterator over the elements in the vector. So keep in mind which syntax you need to use. I believe these are the 4 cases:
e.field
e.method()
e->field
e->method()
Let's try them out. Here's a full example. Hey, if anyone has a more elegant join solution, I'm all ears.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
#define map(vTarget, vSource, eField) \
for (auto e: vSource) { vTarget.push_back(eField); }
class Person {
public:
string name;
int age;
public:
Person(string name, int age) {
this->name=name;
this->age=age;
}
string& getName() {return name;}
int getAge() {return age;}
};
string join(vector<string> vSource, const string separator) {
string buf;
bool first=true;
for (string e: vSource) {
if (first) {
first=false;
} else {
buf+=separator;
}
buf+=e;
}
return buf;
}
int main(int argc, char **argv) {
// using a normal Object
vector<Person> people;
vector<string> names;
people.push_back(Person("john", 27));
people.push_back(Person("jane", 26));
names.clear();
map(names, people, e.name);
cout << join(names, ",") << endl;
names.clear();
map(names, people, e.getName());
cout << join(names, ",") << endl;
// using a pointer to an object
vector<Person*> morePeople;
morePeople.push_back(new Person("bob", 27));
morePeople.push_back(new Person("amy", 26));
names.clear();
map(names, morePeople, e->name);
cout << join(names, ",") << endl;
names.clear();
map(names, morePeople, e->getName());
cout << join(names, ",") << endl;
}
Sample output:
john,jane
john,jane
bob,amy
bob,amy

c++ array to array assignment

I wrote the following employee class:
#include<iostream>
#include<string>
using namespace std;
class employee
{
private:
int id;
int salaries[12];
int annualS;
string name;
public:
employee(int id2, string name2, int array[12])
{
id = id2;
name=name2;
salaries = array; //here where the error occurred.
}
~employee()
{
cout<<"Object Destructed";
}
employee()
{
id = 0;
name="Mhammad";
}
int annulalSalary()
{
for( int i=0; i<12; i++)
{
annualS+=salaries[i];
}
return annualS;
}
int tax()
{
return (annualS*10/100);
}
};
void main()
{
int salaries[12];
for(int i=0; i<12; i++)
{
cin>>salaries[i];
}
employee Mohammad(10,"Mohammad",salaries);
cout<< Mohammad.annulalSalary();
cout<< Mohammad.tax();
}
...but when I compile it the, compiler returns the following error:
cannot convert from 'int []' to 'int [12]'
Could anyone help me solve this problem?
you can not copy an entire array just using = operator in c++. you have two options.
overload = operator
or
use a for loop like this to copy each element of one array to another
for(int i=0 ; i<12 ; i++)
salaries[i]=array[i];
on a different note don't use magic numbers like 12 in your code.
Instead of a C array, use the C++ std::array<>, like this:
class employee {
//...
std::array<int, 12> salaries;
//...
};
and of course you'd have to include <array> too. And declare the constructor like this:
employee(int id2, string name2, std::array<int, 12> const & array)
{
//...
}
(or drop the const & if you're not sure what they are or don't need them.)
You can not copy arrays by assignment. You need to copy each element individually. Use std::copy.
std::copy(array, array+12, salaries);
Or use std::vector<int> or std::array<int, 12> as suggested by Borgleader which does copy by assignment.
Use the vector class!
But to solve your problem :
int salaries[12] should be int* salaries
employee(int id2, string name2, int array[12]) should be employee(int id2, string name2, int* array)
but then you might a problems of referencing things outside the allocated memory and segfault.
USE VECTORS!

c++ char array output in class functions

I am a real c++ beginner and I have a problem with my char array output in a c++ excerise. I was asked to transform a certain UML class in to c++ and generate an working output with the parameters given in main. Here ist the code:
#include <iostream>
#include <stdlib.h>
/*My class defintion book*/
class Book
{ protected:
long int number;
char author[25];
int year;
bool lent;
void setLent(bool x);
bool getLent();
public:
Book(long int n, char a[25], int j, bool x);
long int getNr();
int getYear();
void print();
};
/*Method definition Book*/
Book::Book(long int n, char a[25], int j, bool x)
{number=n;
author=a;
year=j;
lent=x;}
long int Book::getNr()
{return number; }
int Book::getYear()
{return year;}
void Book::setLent(bool x)
{lent=x;}
bool Book::getLent()
{return lent;}
void Book::print()
{
std::cout << "Book Nr: " << number << std::endl;
std::cout << "Author: " << author << std::endl;
std::cout << "Year: " << year << std::endl;
if (lent==0)
std::cout << "Lent [yes/no]: no" << std::endl;
else
std::cout << "Lent [yes/no]: yes" << std::endl;
}
/*MAIN*/
int main()
{
Book b1(123456, "test", 2014, false);
b1.print();
system("pause");
return 0;
This is my output:
Book Nr: 123456
Author: b<Vv-[[vóYA
Year: 2014
Lent [yes/no]: no
Press any key to continue...
As you can see all outputs work except for the "Author". There I am getting crap. Note that I have to use char as type. since it is given in the UML class I had to transform into c++.
I really searched everywhere. But didn't find the correct solution. I have the feeling it will be a very simple one...
Thanks in advance for your help!
The reason this doesn't work is that you're assigning your pointer author to another pointer a, which then goes out of scope... so you're left with author pointing to some garbage. If you want to stick with character arrays, you'll have to copy all the data that a points to:
strcpy(author, a);
But since it's C++, you should just use strings, which are easier to deal with:
class Book {
...
std::string author;
....
};
Book::Book(long int n, const std::string& a, int j, bool x)
: author(a), ...
{ }
You are printing out uninitialized data.
Make author a string
#include <string>
class Book
{ protected:
long int number;
std::string author;
int year;
bool lent;
and make the argument to the constructor a string as well
Book::Book(long int n, const std::string& a, int j, bool x)
Arrays of characters are not as flexible as std::strings. they are just chunks of data. If you want to use strings then use std::string instead.
Also, use an initializer list in C++ constructors, not java style
Book::Book(long int n, const std::string &a, int j, bool x)
: number(n),
author(a),
year(j),
lent(x)
{ }
There are two bugs in your code:
Book::Book(long int n, const char a[25], int j, bool x)
{
number=n;
strncpy(author, a, 25); // author = a; doesn't work! shouldn't compile either...
year=j;
lent=x;
}
First: The variable author is a pointer to a zero terminated string. You can use strcpy() to copy this string. Therefore you need to #include <memory.h. But you need to be sure that the string -is- really zero-terminated and fits into your target variable! Else you'll overwrite other memory regions next to the target variable, which is also called a buffer overflow! Better use strncpy(target, source, maxlength); which avoids this problem.
Second: Your parameter a should be "const" as you want to be able to call it with a string constant like in Book b1(123456, "test", 2014, false); where "test" is a constant!
As others already suggested you should use std::string instead of a[25]. C-Strings are "C" and not "C++" and you should try to avoid them. C-Strings can introduce a lot of bugs into your code and enable buffer overflows (=security problems), too. Also they are more complicated to handle. You need to #include <string> to use them.

No suitable constructor to convert from int to emp

My program simply is to increase the salary int the emp class throw the the function increase
but I'm having this error int the call function line from the line :
No suitable constructor to convert from int to emp
here 's my code :
#include <iostream>
#include <string>
using namespace std;
class emp
{
public:
int salary;
};
void increase(emp x,emp y)
{
x.salary+=100;
y.salary+=250;
}
int main()
{
int value=0;
emp fst, scnd;
cin >> fst.salary >> scnd.salary;
increase(fst.salary,scnd.salary);
cout << fst.salary << endl << scnd.salary << endl;
cin >> value;
return 0;
}
increase expects two emps as parameters, yet you pass in two ints.
Change
increase(fst.salary,scnd.salary);
to
increase(fst,scnd);
Your next question is going to be why the values don't change, so to save you the trouble - it's because you're passing by value, effectively changing copies of your original objects. You'll need to pass by reference:
void increase(emp& x,emp& y)
increase(fst.salary,scnd.salary); should be increase(fst,scnd);, void increase(emp x,emp y) ... should be void increase(emp& x,emp& y) ...
You need to pass emp not int. Further, you are passing parameters by value. Use this instead:
void increase(emp &x,emp &y)
And pass the struct variables; i.e. fst and scnd instead of fst.salary and scnd.salary. Refer this question for better understanding.