I am a beginner to C++, I have used C before but never used C++. This is one of my first programs, and it's supposed to do something really simple, however I'm unable to even pass strings between methods... When I call the method setMode with a string array, the method instance recieves an empty array, and not the one I've sent. Why is this happening?
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
#define LED_PATH "sys/class/leds/beaglebone:green:usr"
class LED{
private:
string path;
int number;
public:
LED(int number);
virtual void setMode(string mode[]);
virtual ~LED();
};
LED::LED(int number){
ostringstream fs;
this->number = number;
fs << LED_PATH << number;
this->path = string(fs.str());
cout << this->path << endl;
}
LED::~LED(){
}
void LED::setMode(string mode[]){
//Will use all fields of mode[] in the future
cout << "setMode: mode: " << mode[0].c_str() << endl;
}
int main(int argc, char* argv[]){
LED LEDs[4] = {LED(0), LED(1), LED(2), LED(3)};
string mode[argc-1];
//TODO Perform argc value safety check
for(int i=1; i<argc; i++){
mode[i] = string(argv[i]);
cout << mode[i].c_str()<<endl;
}
for(int i=0; i<4; i++){
LEDs[i].setMode(mode);
}
return 0;
}
Output:
debian#beaglebone:~/Desktop/LED_Cpp$ ./led on 1
sys/class/leds/beaglebone:green:usr0
sys/class/leds/beaglebone:green:usr1
sys/class/leds/beaglebone:green:usr2
sys/class/leds/beaglebone:green:usr3
on
1
setMode: mode:
setMode: mode:
setMode: mode:
setMode: mode:
string mode[argc-1];
This uses a proprietary GCC extension. In standard C++, a raw array's size must be known at compile time.
You need something like this instead:
if (argc < 4) {
std::cerr << "error\n";
return EXIT_FAILURE;
}
string mode[4];
Or, what would be very much preferable, use std::vector:
std::vector<std::string> mode(argc-1);
for(int i=1; i<argc; i++){
mode[i] = string(argv[i]);
cout << mode[i].c_str()<<endl;
}
This will leave mode[0] empty.
for(int i=0; i<4; i++){
LEDs[i].setMode(mode);
}
The array (or std::vector) passed to setMode will thus always be such that the first element is an empty string.
void LED::setMode(string mode[]){
This is an attempt to pass an array to a function. What happens really is that a pointer to the first element is passed and the size information is lost.
The correct way of passing a raw array including its size information would be to use a reference:
void LED::setMode(string (&mode)[4])
But as I mentioned previously, just use a std::vector and you'll be fine. When you need to modify the vector, pass it via &, else via const&:
void LED::setMode(std::vector<std::string> const& mode)
In either case, inside of the method, you currenly just access the first element:
cout << "setMode: mode: " << mode[0].c_str() << endl;
As we've established before, mode[0] is always empty. That's why nothing is printed.
You are off by one. You are writing to
mode[1]
in
for(int i=1; i<argc; i++){
mode[i] = string(argv[i]);
and you use
mode[0]
for output.
Related
I am entirely new to programming so I'm sorry if I don't explain this well. For my C++ assignment I had to write an object-oriented program that reads the names from a text file (the text file is just a list of first names) and prints them to the console in alphabetical order using an array. Originally, the description of the assignment said that the file had 20 names, so I based my code around that. The program works, but now it turns out the assignment description was inaccurate and we shouldn't assume that the text file has a specific number of names. How do I convert my code from specifically reading 20 names to instead reading an undefined number of names, while still using an array?
I don't fully understand the concepts that I'm implementing so it's difficult for me to know how to change my code while still following the requirements of the assignment. Here is my code:
#include <iostream>
#include <algorithm>
#include <fstream>
#include <string>
using namespace std;
class Names
{
private:
ifstream inStudents;
string studentNames[20];
string name;
int j;
public:
Names();
~Names();
void openFile(string);
void testFile();
void readFile();
void sortNames();
void closeFile();
void display();
};
Names::Names()
{
}
Names::~Names()
{
}
void Names::openFile(string d)
{
inStudents.open(d);
}
void Names::testFile()
{
if (!inStudents)
{
cout << "File did not open" << endl;
exit(10);
}
}
void Names::readFile()
{
cout << "Reading the input file..." << endl;
int j = 0;
while (inStudents >> name && j < 20)
{
studentNames[j++] = name;
}
}
void Names::sortNames()
{
sort(studentNames, studentNames + 20);
}
void Names::closeFile()
{
inStudents.close();
}
void Names::display()
{
cout << endl << "The alphabetical list: " << endl << endl;
for (int i = 0; i<20; i++)
cout << studentNames[i] << " " << endl;
cout << endl << endl;
}
int main()
{
Names r;
r.openFile("students.txt");
r.readFile();
r.testFile();
r.sortNames();
r.display();
r.closeFile();
return 0;
}
You can use std::vector object instead of a regular array. It will look like that:
vector<string> studentNames;
Now, instead of using the following line to insert a name to a known place in the array:
studentNames[j++] = name;
use:
studentNames.push_back(name);
//or
studentNames.emplace_back(name);
The the while loop inside your readFile function, will look like this:
while (inStudents >> name)
{
studentNames.push_back(name);
}
To display it now, all you have to change in your display function is the range. The vector object include a function named size which returns you the current vector size, or in other words- the elements' count that the vector includes. It will seem like the following line:
for (int i = 0; i < studentNames.size(); i++)
So this is a fragment of my code:
void reverse(string query, string reverseQuery) {
unsigned int i;
for(i=0; i<query.length(); i++) {
reverseQuery[i] = query[query.length()-1-i];
}
cout << reverseQuery << endl;
return;
}
The headers for iostream, string and using namespace std were also included in the code. The problem I am facing is that when I try to output the string reverseQuery nothing comes out. Anyone knows why? Thanks!
Your fragment should look like:
std::string reverse(const string& query) {
std::string reverseQuery(query.length(),0); // <<<<< Ensure that the size is the same
for(unsigned i=0; i<query.length(); i++) {
reverseQuery[i] = query[query.length()-1-i];
}
// cout << reverseQuery << endl;
return reverseQuery;
}
The way shorter and idiomatic code to achieve that with a standard c++ string is
std::string reverseQuery(query);
std::reverse(std::begin(reverseQuery),std::end(reverseQuery));
So, I'm trying bring over some code to a Qt project I'm working on. The Motion class imports some control points from .txt file into the public member variable ctrlPos using fstream. When I use readCtrlPositions and then try to access ctrlPos with writePositions, for example, I get the error "vector subscript out of range".
There is a lot more code, but hopefully this should be sufficient to answer my question. I'm also a bit of a novice, so with any luck it's not something too stupid.
Motion class header:
#ifndef MOTION_H
#define MOTION_H
#include <vector>
#include "DualQuaternion.h"
class Motion
{
public:
virtual ~Motion();
virtual void readCtrlPositions(char*, char*);
virtual void writePositions(char*);
virtual void drawCtrlPositions();
virtual void set(int, vector<DualQuaternion>);
virtual pair<int, vector<DualQuaternion>> get();
public:
vector<DualQuaternion> ctrlPos, c;
int numberOfPositions;
};
#endif
Motion class:
#include <stdlib.h>
#include <GL\glut.h>
#include "motion.h"
#include "Quaternion.h"
#include "hMatrix.h"
#include "hPoint.h"
using namespace std;
void Motion::readCtrlPositions(char *fileNameArg, char *t)
{
ifstream inFile(fileNameArg, ios::in);
if (!inFile)
{
cerr<<"File" << fileNameArg << "could not be opened" << endl;
exit(1);
}
int i;
inFile >> numberOfPositions;
Quaternion *RotationQuaternion = new Quaternion[numberOfPositions];
for (i = 0; i<numberOfPositions; i++)
inFile >> RotationQuaternion[i];
if (t == "v")
{
Vector *TranslationVector = new Vector[numberOfPositions];
for (i = 0; i<numberOfPositions; i++)
inFile >> TranslationVector[i];
ctrlPos.clear();
for (i = 0; i<numberOfPositions; i++)
{
DualQuaternion dQ(RotationQuaternion[i], TranslationVector[i]);
ctrlPos.push_back(dQ);
cout << "first position from input: " << ctrlPos[i] << endl;
}
delete[] TranslationVector;
}
else if (t == "q")
{
Quaternion *TranslationQuaternion = new Quaternion[numberOfPositions];
for (i = 0; i<numberOfPositions; i++)
inFile >> TranslationQuaternion[i];
ctrlPos.clear();
for (i = 0; i<numberOfPositions; i++)
{
DualQuaternion dQ(RotationQuaternion[i], TranslationQuaternion[i]);
ctrlPos.push_back(dQ);
cout << "first position from input: " << ctrlPos[i] << endl;
}
delete[] TranslationQuaternion;
}
delete[] RotationQuaternion;
}
void Motion::writePositions(char *fileNameArg)
{
ofstream outFile(fileNameArg, ios::out);
if (!outFile)
{
cerr<<"File" << fileNameArg << "could not be opened for writing" << endl;
exit(1);
}
int i;
outFile << numberOfPositions << endl << endl;
for (i = 0; i<numberOfPositions; i++)
outFile << ctrlPos[i].GetReal();
outFile << endl;
for (i = 0; i<numberOfPositions; i++)
outFile << ctrlPos[i].GetDual();
}
void Motion::set(int n, vector<DualQuaternion> p)
{
int i;
numberOfPositions = n;
ctrlPos.clear();
for (i = 0; i<numberOfPositions; i++)
ctrlPos.push_back(p[i]);
}
pair<int, vector<DualQuaternion>> Motion::get()
{
return make_pair(numberOfPositions, ctrlPos);
}
void Motion::drawCtrlPositions()
{
vector <hMatrix> homogeneousMatricesForCtrlPositions;
for (int i=0; i<numberOfPositions; i++)
{
homogeneousMatricesForCtrlPositions.push_back(ctrlPos[i].dualQuaternionToHomogeneousMatrix().transpose());
double MatrixforOpenGLStack[16];
for (int i1=0; i1<4; i1++)
for (int i2=0; i2<4; i2++)
MatrixforOpenGLStack[4*i1+i2] = homogeneousMatricesForCtrlPositions.at(i).m[i1][i2];
::glPushMatrix();
::glMultMatrixd(MatrixforOpenGLStack);
glutSolidTeapot(0.15);
::glPopMatrix();
}
}
Motion::~Motion()
{
}
Sample code where error occurs in Qt program:
static Curve m;
m.readCtrlPositions("input.txt", "v");
m.writePositions("output.txt"); //<--vector subscript out of range
m.readCtrlPositions("output.txt", "q");
ctrlPos = m.get().second;
numberOfPositions = m.get().first;
In readCtrlPositions, t is a char*, so nor t=="v", nor t=="q" will be evaluated to true (it would return true if the two pointers were having the same address). So your function will set numberOfPositions to a non zero value but will never fill ctrlPos vector with any value.
Later, you'll try to access ctrlPos elements from 0 to numberOfPositions (not zero), while ctrlPos vector is empty. That's why you are reported to access the vector out of its range!
Replace char* by std::string is an easy way to fix the problem. If you need to keep the parameter as a char*, then use strcmp to compare string values rather than pointers.
I would also strongly recommend that you remove your numberOfPositions attribute and simply use ctrlPos.size() instead. It would have prevented a crash in this case by guaranteeing your class attributes integrity.
I am learning about vector. I try to implement a code that print the struct element of a vector as displayed below. Many resources in the internet only teach me a simple vector. I get stcuk in expression when to print it. However, any suggestion for improving the quality and elegance of the code is open, although the change is fundamental (in struct or looping).
Thank you very much.
#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;
typedef struct _student {
string name;
int age;
vector <string> subject;
}student;
int _tmain(int argc, _TCHAR* argv[])
{
vector <student> x; //assmue at this point we do not know the number of students
student y;
//and I want to insert new information
y.name ="John";
y.age =9;
y.subject.push_back("biology");
y.subject.push_back("math");
y.subject.push_back("art");
x.push_back(y);
//get new information again
//and I want to insert new information
y.name ="Bon";
y.age =12;
y.subject.push_back("history");
y.subject.push_back("physics");
x.push_back(y);
// then I want display all data
cout << "myvector contains:";
for (int i=0; i<x.size(); i++)
{
cout << "Student # " << i+1 <<endl;
cout << " name : " << x.at(i).name <<endl; //Reference in the internet only display a simple vector --
cout << " age : " << x.at(i).age <<endl; //I get stuck to express this and next part
cout <<" Subject : ";
for (int j =0; j < x.at(i).subject.size(); j++)
{
cout << x.at(i).subject.at(j);
}
cout << endl;
cin.get();
return 0;
}
Here, added some comments and stuff. Not sure if this is what you were looking for, but here it is.
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <string> // string would be welcome here!
struct _student // the typedef thing is not necessary in C++
{
std::string name; // i find this "using namespace ..." thing a bad habit, it can make code harder to read
int age;
std::vector<std::string> subject;
};
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<student> x;
student y;
size_t size; // calling vector.size() every iterations is a bad idea, performance-wise
size_t size_subj; // same
y.name = "John";
y.age = 9;
y.subject.push_back("biology");
y.subject.push_back("math");
y.subject.push_back("art");
x.push_back(y);
y.name = "Bon";
y.age = 12;
y.subject.clear(); // clear subjects of the other student
y.subject.push_back("history");
y.subject.push_back("physics");
x.push_back(y);
std::cout << "my vector contains:";
for (int i = 0, size = x.size(); i < size; ++i)
{
size_subj = x[i].subject.size();
// I prefer using operator[] when I'm sure nothing can go wrong
std::cout << "Student # " << i + 1 <<endl;
std::cout << "\tname: " << x[i].name <<endl;
std::cout << "\tage: " << x[i].age <<endl;
std::cout << "\tSubjects: ";
for (int j = 0; j < size_subj; ++j)
std::cout << x[i].subject[j];
std::cout << endl;
}
return 0;
}
Finally, using a std::vector< std::string* > or std::vector< std::string& > could be a better idea performance-wise, depending on what you are planning to do with it later.
There is no real question here, so I'm assuming you are asking for "code review"
The "neat" way is of course to create an operator<< that takes your inner structure.
Aside from that, you may want to look at using iterators to walk your way through your vector - that way, you should be able to change your vector for any other container type without having to change the loop(s) that print things.
Use longer variable names than x and y for your vector and temporary student.
Use setw to print fields at the same width every time.
I'm sure there are plenty of other suggestions too.
As the comments point to, it turns out that you're not including the string header file.
Quick disclaimer, this is a contrived example meant to simulate an issue I am seeing in my homework problem. To that end, using strings is out of the question; I can only use char arrays as per the instructor :(
What I am trying to do is continuously read input from the keyboard and store it in a vector . The problem is, whatever data I add to the vector is lost as soon as the addData function ends (when I try to view it, I see \320\366\277_\377). I believe this is due to the fact I am using a vector<char*>, so the vector can only use the data for as long as the pointer exists. However, my code cannot compile if I change it to a vector<char>, as I get errors saying cannot convert char* to char.
So, how can I save a char array (not a single char) to a vector element? Or, is there perhaps a better approach to this that would avoid the problem altogether?
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
const int MAX_BUFFER_SIZE = 80;
// class declaration
class Example {
public:
void getData ();
void addData ( char * newData );
void displayData ();
private:
vector<char*> vec;
};
// main function
int main () {
bool quitProg;
int quit;
quitProg = false;
Example shoopDaWhoop; // buffers cannot overflow if you shoop da whoop
while (!quitProg) {
shoopDaWhoop.getData();
shoopDaWhoop.displayData();
cout << "Type 1 if you want to exit... ";
cin >> quit;
if (quit == 1) {
quitProg = true;
}
}
return 0;
}
void Example::getData () {
char userInput [MAX_BUFFER_SIZE];
cout << "Enter text: ";
cin.get(userInput, MAX_BUFFER_SIZE - 1, '\n');
if ( cin.fail() ) { // data is invalid
// clear and reset input stream
cin.clear(ios::goodbit);
cin.ignore(INT_MAX,'\n');
// alert user they entered bad data
cout << "That was bad data!" << endl;
}
else {
// data is good, pass it to addData
addData( userInput );
}
}
void Example::addData ( char * newData ) {
vec.push_back(newData);
cout << "You entered: " << vec.back() << endl;
}
void Example::displayData () {
for (int i=0; i<vec.size(); i++) {
cout << "Item " << i << ": " << vec[i] << endl;
}
}
Go with the vector<char>, but instead of
vec.push_back(newData);
Use:
size_t len = strlen(newData);
vec.insert(vec.end(), newData, newData + len);
Or does it actually need to be an vector of char arrays?
Use a std::vector<std::string>, that should "just work" with your existing code.
Since you cant do this with std::string (which would have been the proper way to use the language), the you a nested vector, like this:
std::vector<std::vector<char> > vex; // notice the space between the '>' chars, older compilers may need it this way
Then in your addData function:
std::vector<char> tmp;
while(*newData)
tmp.push_back(*newData++);
vec.push_back(tmp);
vector<char*> will only hold pointers - you want it to hold characters. To display the text, you'll have to iterate through the vector and print each character.
vector<char> vec;
void Example::addData ( char * newData ) {
cout << "You entered: ";
while (*newData) {
vec.push_back(*newData);
cout << (*newData);
++newData;
}
cout << endl";
}
If you want multiple strings you can use another vector.