(Sorry if my sentances are full of mystakes, I'll do my best to write something readable) Hi, I'm working on a function that reads a file and store every line whose first char is ":" and removes every dash contained in the string. Every time this kind of line is found, push_back() is used to store this line in a vector. The problem is that, every time push_back() is used, all the elements in the vector takes the value of the last one. I don't understand why does it happen. Here's the code :
string listContent;
size_t dashPos;
vector<char*>cTagsList;
while(!SFHlist.eof())
{
getline(SFHlist,listContent);
if(listContent[0]==':')
{
listContent.erase(0,1);
dashPos = listContent.rfind("-",string::npos);
while(dashPos!=string::npos)
{
listContent.pop_back();
dashPos = listContent.rfind("-",string::npos);
}
char* c_listContent = (char*)listContent.c_str();
cTagsList.push_back(c_listContent);
}
}
I first thought it was a problem with the end of the file but aborting the searching process before reaching this point gives the same results.
the c_str()-method of std::string states:
The pointer returned may be invalidated by further calls to other member functions that modify the object.
If you're allowed to use a std::vector< std::string > instead of the vector of char*, you're fine since there would be always a copy of the std::string listContent pushed into the vector, ie.
std::string listContent;
size_t dashPos;
std::vector<std::string>cTagsList;
while(!SFHlist.eof())
{
getline(SFHlist,listContent);
if(listContent[0]==':')
{
listContent.erase(0,1);
dashPos = listContent.rfind("-",string::npos);
while(dashPos!=string::npos)
{
listContent.pop_back();
dashPos = listContent.rfind("-",string::npos);
}
cTagsList.push_back(listContent);
}
}
(I haven't tested it)
Related
I have a problem with a lambda function in C++: I'm trying to define an asynchronous loader that fills an array of object given a list of string as input.
The code looks like this (not exactly, but I hope you get the idea):
void loadData() {
while (we_have_data()) {
std::string str = getNext();
array.resize(array.size() + 1);
element &e = array.back();
tasks.push_back([&, str] () {
std::istringstream iss(str);
iss >> e;
}
}
for (auto task: tasks) {
task();
}
}
When at the end I scan the list of tasks and execute them, the application crashes on the first access to the variable e inside the lambda. If I run inside a debugger, I can find the right values inside the object e itself. I am doing something wrong, but I don't really understand what.
You are holding a dangling reference. When you do
tasks.push_back([&, str] () {
std::istringstream iss(str);
iss >> e;
}
You capture by reference the element returned by array.back() since a reference to e is actually a reference to whatever e refers to. Unfortunately resize is called in the while loop so when array is resized the references to back() are invalidated and you are now referring to an object that no longer exist.
The scope of element& e is the while-loop.
After every iteration of the while-loop, you have lambda functions with a captured reference to different e's, which have all gone out-of-scope.
You capture e (aka. array.back()) "by-reference" when creating the lambda, with subsequent resizes of the array (with possible reallocations), leaves a dangling reference and in turn causes an error when you attempt to access this dangling reference. Any attempt (not limited to the lambda) to access elements in the array by a previously assigned reference after the array has undergone a resize (and reallocation) will cause a "dangling reference" problem.
An alternative... instead of the two loops, why not just execute the task immediately in the while loop and forgo the dangling reference and attempting to get pointer or iterator based alternatives working.
Further alternative... if the elements in the array can be shared, a std::shared_ptr solution could work, the caveat would be to capture the shared_ptr elements by value, thus ensuring the lambda shared shares ownership of those elements with the array as it is resized.
A sampling of the idea...
void loadData() {
while (we_have_data()) {
std::string str = getNext();
array.resize(array.size() + 1);
std::shared_ptr<element> e = array.back();
tasks.push_back([e, str] () {
std::istringstream iss(str);
iss >> *e;
}
}
for (auto task: tasks) {
task();
}
}
You have two strikes against you here.
Firstly, you are capturing a reference to an iterator to a vector that is likely to be resized and thus relocated.
Secondly, you are capturing a reference to a local (stack) variable that goes out of scope. Within the loop, the compiler probably uses the same memory location for 'e' each time, so all of the references would point to the same stack location.
A simpler solution would be to store the element number:
while (we_have_data()) {
std::string str = getNext();
size_t e = array.size();
array.resize(e + 1);
tasks.push_back([&, e, str] () {
std::istringstream iss(str);
iss >> array[e];
}
}
If you have C++14 and your strings are long, you may want to consider:
tasks.push_back([&, e, str{std::move(str)}] () {
All of this assumes that array will not undergo further manipulations or go out of scope while the tasks are running.
I am trying to find the difference in my code when I use std::find.
For my test code. I made a Vector called Test
std::vector<const char*> Test;
To test the find function, I filled the Test vector with dummy data by using push_back function
Test.push_back("F_S");
Test.push_back("FC");
Test.push_back("ID");
Test.push_back("CD");
Test.push_back("CT");
Test.push_back("DS");
Test.push_back("CR");
Test.push_back("5K_2");
Test.push_back("10K_5");
Test.push_back("10K_1");
Test.push_back("10K_2");
Test.push_back("10K_3");
Test.push_back("10K_4");
Test.push_back("10K_5");
What I want to do with the find function is to go through the Test and see if there are any repeated data. The first time a encounter the data, I will save it to a vector called Unique_Data.
std::vector<const char*> Unique_Data;
So for the 14 data points above, only 13 will be saved because 10K_5 repeated.
The Code I am using looks like this
for(int i = 0; i < Test.size(); i++)
{
if( Unique_Data.empty())
{
Unique_Data.push_back(Test[i]);
}
else if (std::find(Unique_Data.begin(), Unique_Data.end(), Test[i]) != Unique_Data.end())
{
// Move on to next index
}
else
{
Unique_Data.push_back(Test[i]);
}
}
The problem I am having is when I am using the dummy data. I am getting a correct answer for Unique_Data.
However, if I save the actual data into the Test vector which are saved in linked list. I get that they are all unique.
The code looks like this
p_curr = List.p_root;
while(p_curr != NULL)
{
// id starts from 0
if(atoi(p_curr->id) == 14) break;
Test.push_back(p_curr->Descriptor);
p_curr = p_curr->p_next;
}
I tested with the same 14 data. They are all const char* types. However, when I used the linked list data. The find function thinks all the data is unique.
Can anyone tell me what is wrong with this?
Using C-style strings is a bit tricky, they are just a pointer, and pointers are compared by identity. Two C strings with the same sequence of characters, but different addresses will compare different.
const char first[] = "Hi";
const char second[] = "Hi";
assert(first == second); // will fail!
There are two solutions to this problem. The simple one is using std::string in your container, as std::string will provide value comparisons. The alternative is to pass a comparison functor to std::find as a last argument. But this will still leave the problem of managing the lifetime of the const char*-s stored in the vector.
This is a pointers problem. You're not storing strings in your array, you're storing the memory address of the data in the string.
This strange behaviour is probably because in your example case you have literal strings that cannot be changed, so the compiler is optimising the storage, and when two strings are the same then it stores the same address for all strings that have the same text.
In your real data example, you have a bunch of strings that hold the same data, but each of these strings lives at a different memory address, so the find function is saying that all strings have a different address.
In summary, your find function is looking at the memory address of the string, not the data (text) in the string. If you use std::strings then this problem will disappear.
I would highly recommend using strings, as performance is going to be more than good enough and they eliminate a vast number of problems.
As David Rodriguez mentions in his answer, you're only comparing pointers, and not the contents of the strings themselves. Your solution will work as is if you were storing std::strings instead of char const *. With the latter, you need to resort to std::find_if and a predicate that calls strcmp to determine whether the strings are identical.
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
int main()
{
std::vector<const char*> Test;
Test.push_back("F_S");
Test.push_back("FC");
Test.push_back("ID");
Test.push_back("CD");
Test.push_back("CT");
Test.push_back("DS");
Test.push_back("CR");
Test.push_back("5K_2");
Test.push_back("10K_5");
Test.push_back("10K_1");
Test.push_back("10K_2");
Test.push_back("10K_3");
Test.push_back("10K_4");
Test.push_back("10K_5");
std::vector<const char*> Unique_Data;
for(auto const& s1 : Test) {
if(std::find_i(Unique_Data.cbegin(), Unique_Data.cend(),
[&](const char *s2) { return std::strcmp(s1, s2) == 0; })
== Unique_Data.cend()) {
Unique_Data.push_back(s1);
}
}
for(auto const& s : Unique_Data) {
std::cout << s << '\n';
}
}
Here's a live example
How can input a word and reverse the output of it. I made a function to calculate the length of the word and from here I have to reverse the word depending on the length of it.
How can I do that?
#include<iostream>
using std::cout;
using std::endl;
using std::cin;
int LengthOfString( const char *); // declaring prototype for length of the string
int reverse(const char []);
int main()
{
char string1[100];
cout<<"Enter a string: ";
cin>>string1;
cout<<"Length of string is "<<LengthOfString(string1);
system("PAUSE");
return 0;
}
int LengthOfString( const char *x)
{
int index;
for(index = 0; *x!='\0';x++,index++);
return index;
}
int reverse(const char y[])
{
/* my attempted loop, its not right i know.
a[] = *index; // length of the word
for(int i=0; i<=index/2; i++)
for(j=0; j == length, j--) */
}
This wheel has already been invented, and exists in the standard library.
#include <iostream>
#include <string>
#include <algorithm>
int main()
{
std::string word;
std::cout << "Enter a word: ";
std::cin >> word;
std::reverse(word.begin(), word.end());
std::cout << "Reverse: " << word << std::endl;
return 0;
}
To understand exactly what's going on here, there are a few things that you must cover first:
data structures (classes)
containers
iterators
I hope you already know what a class is. In case you're still in introductory stuff, a class is basically a user defined collection of state and behavior. The author can choose to restrict access to the state or behavior of a class for a variety of reasons. In the case of std::string, the standard library string class, all of the state is hidden and only behavior is accessible.
The string class is a container that contains characters. There are numerous other container classes, each of which with different strengths and weaknesses. The string class contains a sequence of characters with a strict order. Other containers exist, such as std::set, std::vector, std::list, and others. std::string bears a passing resemblance to std::vector, and is a distant cousin of std::list. Each collection behaves differently and is suited for different things.
You might think you need to understand how the string class stores its data in order to reverse it, but you don't. This is where iterators come in. std::string owns a typedef, std::string::iterator, which is a special object which stores the location of a single element in a string. std::reverse is a library function which takes 2 iterators and repeatedly swaps their contents and moves them towards each other. This looks like this as it's happening:
v v <-- positions of iterators (start at the start, end at the end)
ABC <-- initial state
v v <-- the end iterator moved back
ABC
v v
CBA <-- the iterators swapped their values
vv <-- the begin iterator moved forward
CBA
V <-- the end iterator moved back; both iterators are in the same place
CBA <-- therefore, we're done, the string is reversed
One thing about iterators is they're kind of like pointers. In fact, you can pass pointers to some functions that expect iterators because they behave syntactically the same. Therefore, you should be able to write your own reverse function that uses pointers that basically does the same thing this did, except with char *s.
Here's some pseudocode that you should be able to write the function with (I won't write it out completely because it's homework):
namespace BaidNation
{
void reverse(char *begin, char *end)
{
loop forever
{
if (end equals begin):
done;
move end backwards;
if (end equals begin):
done;
swap end's and begin's characters;
move begin forwards;
}
}
}
Keep in mind that BaidNation::reverse (as well as std::reverse) expects for end the iterator that references the element AFTER the end of the collection, not the one that references the last element. How does it then make sense to use this?
Your LengthOfString function returns the number of non-null characters in a string. Since arrays are zero-indexed, we know that, like any other array, if we check string1 + LengthOfString(string1), we'll get a pointer to the character after the end which is, for once, exactly what we want.
Thus, we can use this to reverse the string:
BaidNation::reverse(string1, string1 + LengthOfString(string1));
If you have to use exactly the signature earlier, you can adapt this design into the other one:
int reverse(const char str[])
{
char *start = str, *end = str + LengthOfString(str);
BaidNation::reverse(start, end);
}
Based on the fact that the return type of your prototype function is int, it looks to me like you want to do an in-place reversal of a string. You first need to find out how long the string is (although you computed that before, you didn't pass the result to this function), then swap elements until you get to the middle. To make this work you need to pass, not a const char[], but just a char* (indicating that you will be changing the content):
int reverse(char* y)
{
int ii, n;
n = LengthOfString(y); // "no built in functions - otherwise, use strlen()
for(ii=0; ii<n/2;ii++) {
char temp;
temp = y[ii];
y[ii] = y[n - ii - 1];
y[n - ii] = temp;
}
}
Declare a new char* of the same length, and then loop as follows -
for(int i=0;i<stringLength;i++){
newString[i]=oldString[stringLength-i];
}
return newString;
Also you might want to consider using the String class instead of char*.
std::array<LINE,10> currentPaths=PossibleStrtPaths();
LINE s=shortestLine(currentPaths); //ERROR
LINE CShortestPathFinderView::shortestLine(std::array<LINE,10> *currentPaths)
{
std::array<LINE,10>::iterator iter;
LINE s=*(currentPaths+1); //ERROR
for(iter=currentPaths->begin()+1;iter<=currentPaths->end();iter++)
{
if(s.cost>iter->cost)
s=*iter;
}
std::remove(currentPaths->begin(),currentPaths->end(),s);
//now s contains the shortest partial path
return s;
}
At both those statements I'm getting the same error: no suitable conversion from std::array<LINE,10U>*currentPaths to LINE . Why is this so? Should I pass the array another way? I've also tried passing currentPaths as a reference, but it tells me that a reference of the type cannot be initialized.
You said you tried a reference and it failed. I don't know why, because that was the correct thing to do.
LINE CShortestPathFinderView::shortestLine(std::array<LINE,10> ¤tPaths);
From the sounds of it, you also used a reference for the temporary variable. That's wrong.
std::array<LINE,10>& currentPaths = PossibleStrtPaths(); // WRONG
std::array<LINE,10> currentPaths = PossibleStrtPaths(); // RIGHT
LINE s = shortestLine(currentPaths);
And finally, the first element is number zero. The subscripting operator [] is preferred when you are doing array access. So:
LINE s = currentPaths[0];
But you also can easily get the first item from the iterator.
Final code:
/* precondition: currentPaths is not empty */
LINE CShortestPathFinderView::shortestLine(std::array<LINE,10>& currentPaths)
{
std::array<LINE,10>::iterator iter = currentPaths.begin();
LINE s = *(iter++);
for(; iter != currentPaths->end(); ++iter) {
if(s.cost>iter->cost)
s=*iter;
}
std::remove(currentPaths.begin(), currentPaths.end(), s);
//now s contains the shortest partial path
return s;
}
You are dereferencing (currentPaths+1) which is of type std::array* (more precisely: you are incrementing the pointer and then accessing its pointed data) while you probably want to retrieve the first element of currentPaths, that is: currentPaths[0] (the first index in an array is 0).
Eclipse shows the following Error on the push_back line:
Invalid arguments '
Candidates are:
void push_back(const std::basic_string<char,std::char_traits<char>,std::allocator<char>> &)
My Question is, how can i make sure that the vector contains strings and not basic_strings?
I have figured out in another piece of code where i wanted to access this vector that i have troubles compiling it, it seems to know later that it is a basic_string and not a "std::string", in the current context i know that i only will use char but i would like to keep the superclass in there so that in the future i don't have to adjust anything if i swap to w-char ect.
I have already tried casting it to a string but that seems not to work
std::string test = (std::string)tokens[1];
The final goal would be to construct some object with that vector
if(tokens->size() == 3){
TestObject test = new TestObject(tokens[1],)tokens[2])
}
This is the Function i have found on Stackoverflow which was proposed by a stackoverflow user
Function Origin
Thanks for helping.
void TestClass::splitString(const std::string &str, std::vector<std::string>* tokens){
boost::char_separator<char> sep(",");
std::string separator;
boost::tokenizer< boost::char_separator<char> > tok(str,sep);
for(boost::tokenizer< boost::char_separator<char> >::iterator it=tok.begin(); it != tok.end(); ++it)
{
tokens->push_back(*it);
}
}
no matching function for call to ‘TestObject::defineX(std::basic_string*)’
accessing with defineX(vector[1].data());
no matching function for call to ‘TestObject::defineX(std::vector >&)’
accessing with defineX(vector[1]);
The definition of the function is declared as the following(maybe this is the issue?):
typedef std::string td_MAP_KEY;
td_MAP_VALUE * TestObject::defineX(td_MAP_KEY key);
I call the method like this, if your keen to compile this, it should work, unfortunately i' am not able to paste the complete code here but the code below should doit:
std::string x = "one,two,three,four"
std::vector<std::string> * splittedString = new std::vector<std::string>;
splitString(x,splittedString);
TestObject * object = new TestObject();
object->defineX(splittedString[1]);
The problem with this code:
std::vector<std::string> * splittedString = new std::vector<std::string>;
:
object->defineX(splittedString[1]);
is that you define splittedString as a vector pointer, and point it at a single vector (rather than an array of vectors), and then try to access the second element of the (non-existent) array of vectors. You probably want
object->defineX((*splittedString)[1]);
which dereferences the vector pointer and then grabs the second element of the vector.