tokenizing string with boost fails when casting tokens to char* const* - c++

I'm using boost::tokenizer to tokenize a string in C++, then I want to pass it to execv.
Consider the following code snippet (compilable):
#include <iostream>
#include <cstdlib>
#include <vector>
#include <boost/tokenizer.hpp>
// I will put every token into this vector
std::vector<const char*> argc;
// this is the command I want to parse
std::string command = "/bin/ls -la -R";
void test_tokenizer() {
// tokenizer is needed because arguments can be in quotes
boost::tokenizer<boost::escaped_list_separator<char> > scriptArguments(
command,
boost::escaped_list_separator<char>("\\", " ", "\""));
boost::tokenizer<boost::escaped_list_separator<char> >::iterator argument;
for(argument = scriptArguments.begin();
argument!=scriptArguments.end();
++argument) {
argc.push_back(argument->c_str());
std::cout << argument->c_str() << std::endl;
}
argc.push_back(NULL);
}
void test_raw() {
argc.push_back("/bin/ls");
argc.push_back("-l");
argc.push_back("-R");
argc.push_back(NULL);
}
int main() {
// this works OK
/*test_raw();
execv(argc[0], (char* const*)&argc[0]);
std::cerr << "execv failed";
_exit(1);
*/
// this is not working
test_tokenizer();
execv(argc[0], (char* const*)&argc[0]);
std::cerr << "execv failed";
_exit(2);
}
When I run this script it calls test_tokenizer(), it will print 'execv failed'. (Although it prints the arguments nicely).
However if I change test_tokenizer to test_raw it runs fine.
It must be some easy solution but I didn't find it.
PS.: I also drop this into an online compiler with boost support here.

boost::tokenizer saves the token by value (and by default as std::string) in the token iterator.
Therefore the character array that argument->c_str() points to may be modified or invalidated when the iterator is modified and its lifetime will end with that of argument at the latest.
Consequently your program has undefined behavior when you try to use argc.
If you want to keep using boost::tokenizer, I would suggest to keep the tokens in a std::vector<std::string> and transform them to a pointer array afterwards.

Related

Trying to get to grips with C++ and cant work out why this doesn't print out the names of the vector<string> entries

So this code doesn't print out the entries inside of the vector gameLibrary
Originally I just used the gameLibrary.pushback(" ") function to add them and that worked fine.
I'm more just trying to get to grips with why this doesn't work. when ( at least in my mind it's doing the same thing)
#include <iostream>
#include <vector>
#include <string>
using std::cout;
using std::vector;
using std::string;
void addGame(vector<string> gameLibrary, string gameName);
int main()
{
vector<string> gameLibrary;
vector<string>::iterator editIter;
addGame(gameLibrary, "game");
addGame(gameLibrary, "game 2");
cout << "Your library: " << std::endl;
for (editIter = gameLibrary.begin(); editIter != gameLibrary.end(); ++editIter)
{
cout << *editIter << std::endl;
}
return 0;
}
void addGame(vector<string>gameLibrary, string gameName)
{
gameLibrary.emplace_back(gameName);
}
addGame must not receive the vector it is filling by copy. It must be passed by reference or by pointer.
Exemple passing by reference :
void addGame(vector<string>& gameLibrary, string gameName)
{
gameLibrary.emplace_back(gameName);
}
Otherwise, the copy is modified, so the vector declared in main is unchanged.
Please read up on passing by value vs reference. In your function you are passing your array by value, which means that only the value of your array gets copied into the function.
Any changes to the array insider the function are not reflected back. If you need that, you need to pass your array by reference
see this answer : Are vectors passed to functions by value or by reference in C++

c++ unit testing check output is correct

If I want to write my own test.cpp that checks if another .cpp file is outputting the way I want it to output, is there anyway to do it without explicitly printing it?
In other words, is there anything such as
assert(output_of_file_being_tested, "this is the correct output");
where output_of_file_being_tested is something that's supposed to be "cout"ed.
The solution is not to hard-code the output stream. Pass a reference to std::ostream to your code somehow, and use std::stringstream to collect the output in test environment.
For example, this is the content of your "another .cpp" file:
void toBeTested(std::ostream& output) {
output << "this is the correct output";
}
So in your production/release code you may pass std::cout to the function:
void productionCode() {
toBeTested(std::cout);
}
while in the test environment you may collect the output to a sting stream and check it for correctness:
// test.cpp
#include <sstream>
#include <cassert>
void test() {
std::stringstream ss;
toBeTested(ss);
assert(ss.str() == "this is the correct output");
}
In addition to Sergey's great answer above, you may choose to have std::cout as a default parameter.
So, if you have a code:
// Only need console output
using std::cout;
...
void toBeTested()
{
cout << "This is the correct output.";
}
And it is used (or may be frequently used in the future) in many places:
int main()
{
...
toBeTested();
...
toBeTested();
...
// And so on...
return 0;
}
In order to avoid breaking a lot of code and maintaining a simple interface,
you can convert the above function to:
using std::cout;
...
void toBeTested(std::ostream& cout = std::cout)
{
cout << "This is the correct output.";
}
And your main does not need to be touched.
Note that cout of the function now overshadows cout of the global scope. Therefore, this cout can be any output stream, and does not interfere with the global cout.
And now you can test this as above!
#include <sstream>
#include <cassert>
...
void testMyFunctionDisplay()
{
// Arrange
std::ostringstream output_buffer;
// Act
toBeTested(output_buffer);
// Assert
assert(output_buffer.str() == "This is the correct output.");
}
However. it is not necessary to make every function in this way.
It is useful if we want to redirect that function's output to other output streams:
Stringstreams: If you want to use the output somewhere, perhaps for testing the module, or for other purposes in the program.
Files: If you want to maintain the output even after the program termination.

In libtidy, how to read string-valued options

I am using libtidy and need to retrieve the current value of a string-based option (for example, TidyOptErrFile, a.k.a. error-file).
Even after reading through the tidy source code, I cannot figure out which function to use to read the current value of such an option.
The TidyOptGetVal() function seemed promising, but no matter what I do, it always returns a null pointer. Here is my minimalist attempt (in C++ so I can use the auto keyword):
#include <iostream>
#include <tidy.h>
#include <tidybuffio.h>
#include <tidyenum.h>
using namespace std;
int main(int argc, const char * argv[]) {
auto tidyDoc = tidyCreate();
// The following should set the `error-file` property to `Foobar`
tidyOptParseValue(tidyDoc, "TidyErrFile", "Foobar");
// The type is `ctmbstr` which is just an alias for `const char*`
auto errorFile = tidyOptGetValue(tidyDoc, TidyErrFile);
if (errorFile==nullptr) {
cout << "Null pointer" << endl;
} else {
cout << errorFile << endl;
}
tidyRelease(tidyDoc);
return 0;
}
I found the reason for the problem, which I will post for future reference.
The function tidyOptParseValue() takes as its second argument the name of the option as given in the API.
So, for example, the option name in this case should be error-file (not the name of the enumeration found in the source code).
The corrected line should be
tidyOptParseValue(tidyDoc, "error-file", "Foobar");
Using that fix, the word Foobar is printed, as expected.

Sort or Mergesort based on user input

I am trying to call a function based on user input. I cannt figure out what I am doing wrong. I keep getting this error
error: no matching function for call to 'sort(std::vector<int, std::allocator<int> >&)'
Can someone tell me what I am doing wrong. Please explain any suggestions thoroughly as I am brand new to C++. Here is my code:
#include <iterator>
#include <algorithm>
#include <vector>
#include <fstream>
#include <iostream>
#include <string>
std::ifstream in("");
std::ofstream out("outputfile.txt");
std::vector<int> numbers;
std::string sortType = "";
std::string file = "";
int main()
{
std::cout << "Which type of sort would you like to perform(sort or mergesort)?\n";
std::cin >> sortType;
std::cout << "Which file would you like to sort?\n";
std::cin >> file;
//Check if file exists
if(!in)
{
std::cout << std::endl << "The File is corrupt or does not exist! ";
return 1;
}
// Read all the ints from in:
copy(std::istream_iterator<int>(in), std::istream_iterator<int>(),
std::back_inserter(numbers));
//check if the file has values
if(numbers.empty())
{
std::cout << std::endl << "The file provided is empty!";
return 1;
} else
{
if(file == "sort")
{
sort(numbers);
}else
{
mergeSort();
}
}
}
void sort(std::vector<int>)
{
// Sort the vector:
sort(numbers.begin(), numbers.end());
unique(numbers.begin(), numbers.end());
// Print the vector with tab separators:
copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, "\t"));
std::cout << std::endl;
// Write the vector to a text file
copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(out, "\t"));
std::cout << std::endl;
}
void mergeSort()
{
//mergesort code..
}
You need to declare the sort function before you call it. Move its definition above main, or put void sort(std::vector<int>); before main.
And the same goes for mergeSort.
You should also fully qualify the call sort(numbers.begin(), numbers.end()); as std::sort(numbers.begin(), numbers.end());, and the same for copy, unique. If you don't, then for technical reasons called "ADL", which you can look up if you like, then the call only compiles if the arguments that you're calling it on (the iterators) are classes in namespace std. It's up to the specific implementation whether or not they are, so the call won't work on some compilers.
I agree with #steve on declaring sort function before main().
I think the problem here is that you're calling the sort() function with parameter std::vector but in definition of the function you just wrote the type of received parameter, you should also write some name for the variable. eg.
void sort(std::vector<int> <variable_name>)
{
//definition
}
One more thing I want to point out is that as you have declared the vector number globally then there is no need to call the like sort(number), because function will automatically find the globally defined vector number. So basically if you want the vector number to be defined globally then the function sort() should be parameterless.
you have also used scope std:: everywhere, instead you can just add one line right after the #includes -
using namespace std;
I hope it works!

Strange behavior of stringstream passed by reference

For a project, I'd like to use stringstream to carry on data. To achieve this goal, I have to pass some stringstream as parameter to some function, but when I output the stringstreams, I see something like an address.
The code :
#include <iostream>
#include <sstream>
void doStuff(const std::iostream& msg)
{
std::cerr << msg << std::endl;
}
int main(void)
{
doStuff(std::stringstream("av"));
}
The output is :
0xbff4eb40
Can someone explains why I get an address when passing an rvalue ?
And why can't I pass a stringstream by value ?
You probably want to access the string on which the stringstream is storing its data:
void doStuff(const std::stringstream& msg)
{
std::cerr << msg.str() << std::endl;
}
What is happening in your code is that iostreams contain a void* operator which returns 0 if the stream contains any error or has reached EOF, and another value otherwise. This is usefull for error checking.
When you try to write you stream to std::cerr, the compiler realizes that the stream can be converted to a void* using that operator, and that a void* can be written to a ostream(the operator<< has been defined), and therefore uses it.
Note that i changed the method's signature so that it receives an std::stringstream as an argument, since std::iostream::str is not defined(this method is only available on string streams).
You get an address because it (like other streams) has a conversion to void * (which is primarily useful as a Boolean, to see whether reading/writing the stream has failed).
You can't pass it by value, because streams (again, in general, not just stringstreams) don't support copying and/or assigning.
To print the content of the stream, you could do something like:
void dostuff(std::iostream &msg) {
std::cerr << msg.rdbuf() << "\n";
}
Edit: Here's a complete demo program:
#include <iostream>
#include <sstream>
void show(std::ostream &os) {
std::cout << os.rdbuf() << "\n";
}
int main(){
std::stringstream test("whatever");
show(test);
return 0;
}
When I execute it, the output I get is the expected "whatever".