C++ String Split function Error in Eclipse but Compiles - c++

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.

Related

Why can't I access a std::vector<std::pair<std::string, std::string>> through vec[i].first()?

I am attempting to print data from a std::vector<std::pair<std::string,std::string>> via a for loop. MSVC says that I can't call make a call through this vector. I tried it with std::vector<std::pair<int, int>> as well and got the same error. I tried iterating with a for loop on a std::vector<int> and it worked fine. I haven't tried on another compiler.
Sample code
std::vector<std::pair<std::string, std::string>> header_data = get_png_header_data(file_contents);
for (unsigned int i = 0; i < header_data.size(); i++)
{
std::cout << header_data[i].first(); //throws an error on this line "call of an object of a class type without an appropriate operator() or conversion functions to pointer-to-function type
}
I would appreciate an alternative way to access my vector or another storage type that I could use.
Thanks :)
Your std::pair is basically (in a manner of speaking):
struct std::pair {
std::string first;
std::string second;
};
That's what std::pairs are. first and second are ordinary class members, not methods/functions. Now you can easily see what's happening: .first() attempts to call first's () operator overload. Obviously, std::strings have no such overloads. That's what your C++ compiler's error message is telling you. If you reread your compiler's error message it now becomes crystal clear.
You obviously meant to write std::cout << header_data[i].first;.

How can I add members to a rapidjson document using integers as the key/name?

I'm using a for loop and want to use the iterator, i, as the key/name when I add a member to the document. For example I want the document to look like this:
{"1":"123.321","2":"456.654"}
Here is what I have tried so far.
1. Converting i to a const char*
rapidjson::Value newDouble(6);
for(int i = 0;i<laserScan.size();i++){
newDouble.SetDouble(laserScan[i]);
const char* index = std::to_string(i).c_str();
d.AddMember(index,newDouble,d.GetAllocator());
}
This generates a compiler error telling me that AddMember can only take arguments of type rapidjson::GenericValue&:
error: no matching function for call to ‘rapidjson::GenericDocument<rapidjson::UTF8<> >::AddMember(const char*&, rapidjson::Value&, rapidjson::MemoryPoolAllocator<>&)’
d.AddMember(index,newDouble,d.GetAllocator());//add this name-value pair to the JSON string
2. Converting i to a string using rapidjson types
rapidjson::Value newDouble(6), newStringIndex(5);
for(int i = 0;i<laserScan.size();i++){
newDouble.SetDouble(laserScan[i]);
const char* index = std::to_string(i).c_str();
size = (rapidjson::SizeType)std::strlen(index);
newStringIndex.SetString(rapidjson::StringRef(index,size));
d.AddMember(newStringIndex,newDouble,d.GetAllocator());
}
This throws the following run-time error from Writer class:
Assertion `!hasRoot_' failed.
Why I'm Confused
Shouldn't solution #1 be the same thing as doing the following?
d.AddMember("1",newDouble,d.GetAllocator());
This works when I try it, but I can't figure out why converting an integer to a const char* won't.
Although you have already find an workaround, I would like to state why the original solution is not working.
The problem of solution #1 is that, the index pointer is invalid when exiting the scope.
As stated in tutorial, you can create a key string with allocator to make a copy of it:
std::string s = std::to_string(i)
Value index(s.c_str(), s.size(), d.GetAllocator()); // copy string
d.AddMember(index, newDouble, d.GetAllocator());
And for your workaround, you can simply:
dataArray.PushBack(laserScan[i], allocator);
I found a workaround. Instead of making all the keys integers, I just added the key "indices" with the corresponding value being an array of all the indices. Then I added another array called "data" which contained an array of all the data:
rapidjson::Document document;
rapidjson::Document::AllocatorType& allocator = document.GetAllocator();
rapidjson::Value dataArray(rapidjson::kArrayType), ;
for(int i = 0;i<laserScan.size();i++){
dataArray.PushBack(rapidjson::Value().SetDouble(laserScan[i]),allocator);
}
document.AddMember("data",dataArrary,allocator);

std::vector does strange thing

(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)

How should I iterate this map according to a given string and then append its value?

Edited
Why I'm asking...
Yesterday, I started a project to create a Morse code translator which creates a file or appends to an existing file, translated Morse code from a given string or from the file text given.
Mainly,I have no idea in hell how to get this map to work with the string in which I want to return and I feel as if I've tried everything I can Google or read in documentation.
additionally...
I've left my horrendous attempt at iterating through the data structures , this time using vectors, having exhausted tries with map methods. I'm sure I'm missing simple syntx with the map structure but I left the last attempt up because I believe it conveys my intention quite clearly due to its baroque nature.
So to be more specific, what's the best way to access this map and return it through this function.
initial design
getTranslation()
/* #brief: Program returns string which is a translation
* of the Rvalue string which it takes as a argument
* #param text: string of letters, numbers and some symbols to be translated
* #return translation: translated string appended with map values
*/
string getTranslation (const string&& text) noexcept(true)
{
//return value
auto translation = "";
map <string,string> morseKey;
morseKey ["A"] = ".-";
morseKey ["B"] = "-...";
morseKey ["C"] = "-.-.";
morseKey ["D"] = "-...";
//...
// I was going to attempt to
// unpack to vectors then compare as vectors of
// strings because of consistent issues with
// type safety errors
// i've tried iterating over it differently
// but this is last hope code here
// any help on how to accomplish this in
// a better way but still retain the
// use of a map because of its ability
//to hold all sorts of characters
//would be greatly appreciated
/*
vector <string> mSymbol;
for (auto itr : morseKey)
{
mSymbols.push_back(itr.first);
}
vector <string> vText;
for (auto itr : text)
{
vText.push_back(itr);
}
for (int i = 0; i < text.length(); i++)
{
if (vText[i] == mSymbol[i])
{
translation += morseKey.at(i);
}
}
*/
translation = "*SCAFFOLDING* FUNCTION NOT COMPLETE";
return translation;
}
Edit:
Wow, Iv'e received some really good input and I believe that my issues are rooted in the fact that using auto caused translation to be as a const char* which wouldn't allow me to make my map a map std::map<char,string> morseKey. Also my Rvalue cast via move was apparently unnecessarily (I had a feeling). So I'm going to implement the knowledge I've gained from and post that before I mark my answer.
Edit 2
I removed the auto and 'translation' is now declared as a string
getTranslation's signature takes a const string&
I initialize morseKey as
static map <char,string> const morseKey = {{'A', ".-"},...
but get the compiler error of
'invalid conversion from ‘const char*’ to ‘const char&’
I don't understand why this is, or what makes either a pointer or ref in this situation and therefore how to fix it.
Wow... you practiced a lot of concepts, and you are just learning to code!!!
I'm sure you will be a successful programmer (but you should know it needs a lot more practicing!)
But about your "getTranslation" function, I changed it a little to:
#include <algorithm>
#include <cctype>
/*...*/
string getTranslation (string text) noexcept(true)
{
map <char,string> morseKey;
morseKey ['A'] = ".-";
morseKey ['B'] = "-...";
morseKey ['C'] = "-.-.";
/*...*/
string translation {""};
std::transform(text.begin(), text.end(), text.begin(), toupper);
for (it: text){
translation += morseKey[it];
}
return translation;
}
As you may know, map is an associative array; it means you don't need to iterate over all of its element to find your interesting element. You should associate a key to its corresponding record. In your case you should associate a char (not a string) to a string; so you should define your "morseKey" as:
map <char, string> morseKey;
and when you want to associate a character such as 'A' to ".-" you should do something like:
morseKey ['A'] = ".-"; /*instead of morsKey["A"] = ".-" */;
also when you used "auto" in defining your "translation" variable, compiler will consider it as a "const char*". so you should explicitly define your "translation" variable as:
string translation {""};
In addition because our "morseKey" map contains just uppercase of alphabets, we should convert alphabet characters of "text" variable to uppercase. This can be done very easily by:
std::transform(text.begin(), text.end(), text.begin(), toupper);
but for using this command you should include two libraries:
#include <algorithm> /*for "transform" */
#include <cctype> /*for "touppper" */
Also you shouldn't consider "text" variable as rvalue any more (because we modify it) so I change your function delcarion to:
string getTranslation (string text) noexcept(true)
Finally you should just iterate over your "text" variable and find corresponding Morse value of each character and append it to your return value; This also can be done very easily by:
for (it: text){
translation += morseKey[it];
}
Have fun with programming!
My reply for your second edit:
I think your information is not enough; perhaps it's better to ask your question as a new question, and also provide it with more details, such as in which line you got this compile error or any other useful details that you may think it can be helpful.
The function could be defined the following way. ( Note: I would make the map as a global variable in some namespace and initialize it with an array of std::pair(s) ).
std::string getTranslation( const std::string&& text) noexcept(true)
{
std::string translation;
std::map <std::string, std::string> morseKey;
morseKey ["A"] = ".-";
//...
for ( char c : text )
{
c = std::toupper( c );
auto it = morseKey.find( c );
if ( it != morseKey.end() ) translation.push_back( it->second );
}
return translation;
}

Trouble using find function in C++

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