snippetcppMinor
Function to convert from const char * to char**
Viewed 0 times
convertcharfunctionconstfrom
Problem
I made an adapter function to convert a
Basically the function takes the original C-string, a
In the first part of
Of course both the vector and the string passed to
This looks fair, but I'm not totally proud of it...
const char * to char ** splitting the words in the initial string at each white space (leading, consecutive and trailing white spaces should be ignored).#include
#include
#include
void str2arg(const char * str, std::vector & arg, std::string & memory) {
arg.clear();
memory.clear();
bool copied_first_char = false;
for ( size_t i = 0; str[i]!='\0'; ++i) {
if (isspace(str[i])) {
if (copied_first_char) {
if (!isspace(str[i+1])) {
memory += '\0';
}
}
} else {
copied_first_char = true;
memory += str[i];
}
}
if (memory.back()!='\0') memory += '\0';
//from now on, memory should't be touched!
for ( size_t i = 0; i av;
std::string m;
str2arg(" this is a test string ", av, m);
std::cout << "argc=" << av.size() << " argv=" << av.data() << std::endl;
for (auto i: av) std::cout << i << ".\n";
return 0;
}Basically the function takes the original C-string, a
std::vector that will hold the pointers to the words, and a std::string for automated memory management.In the first part of
str2arg() I copy the chars from the original string substituting white spaces with the termination char '\0'. Then I push the char* into the vector looking at the positions of '\0'.Of course both the vector and the string passed to
str2arg() must then be used as read-only variables.This looks fair, but I'm not totally proud of it...
Solution
I basically reject your code for admittance into the master branch.
This is not C++ code but primarily C and duplicates functionality that exists in the standard library.
Let's look at your interface first:
The parameter
The parameter
Next issues is you are using pointers (and dropping the constness). Pointers are horrible and should only be used at the lowest level of your code for creating containers. Normally you can use normal objects to represent stuff. Here use std::string.
Why are pointers horrible:
They do not provide any semantic definition of ownership. Ownership is a very important concept in C++ as it defines who is responsible for deleting an object. Normally we use containers and smart pointers to define ownership of dynamic objects and we refer to other objects via references when we don't own them.
Const correctness is a big deal in C++ this is something you should take to heart very quickly. You drop the constness on your pointers thus leading to a possibility where people incorrectly use the pointer you return (by modifying the content of the string). This is a real problem if the string is actually const and will cause the program to crash.
Algorithm:
Much improvement can be done here.
Let us examine the utilities provided by the standard.
Note 1:
The stream
Note 2:
The
Since we now have an iterator to scan through a stream of words we can also apply the standard algorithms.
Also note that
So now that we have gone through all that we can re-code your function like this:
Side note.
Since we mention command line args in other answers.
I like to convert the command line args into string before proceeding.
main
Main with Boost
This is not C++ code but primarily C and duplicates functionality that exists in the standard library.
Let's look at your interface first:
void str2arg(const char * str, std::vector & arg, std::string & memory) {The parameter
memory is only used internally to the function. It is not used externally and provides no utility. So get rid of it as a parameter. Make it an internal member of the function.The parameter
arg is used as an out parameter. You don't need to do this. Return the vector by value. This makes it very clear what you are doing. The optimizer will elide the copy and with move semantics in C++11 it will use move if it can not elide the copy. Summary: it is not an issue to return big values from functions the compiler will optimize away any issues (if for some reason it can't and you notice during benchmarking then you can have a look at using out parameters).std::vector str2arg(const char * str);Next issues is you are using pointers (and dropping the constness). Pointers are horrible and should only be used at the lowest level of your code for creating containers. Normally you can use normal objects to represent stuff. Here use std::string.
std::vector str2arg(std::string const& str);
^^^^^^ Allows you to pass a literal.
That can be converted to a string.Why are pointers horrible:
They do not provide any semantic definition of ownership. Ownership is a very important concept in C++ as it defines who is responsible for deleting an object. Normally we use containers and smart pointers to define ownership of dynamic objects and we refer to other objects via references when we don't own them.
Const correctness is a big deal in C++ this is something you should take to heart very quickly. You drop the constness on your pointers thus leading to a possibility where people incorrectly use the pointer you return (by modifying the content of the string). This is a real problem if the string is actually const and will cause the program to crash.
Algorithm:
Much improvement can be done here.
Let us examine the utilities provided by the standard.
Note 1:
The stream
operator>> when applied to a std::string will read one space separated word from the stream.std::string word;
stream >> word; // drops leading space and reads a single space delimited word.Note 2:
The
std::istream_iterator are iterators that treat streams as forward containers. They read X from the stream using operator>> and you can then access the object of type X with the standard iterator methods. Use this in combination with std::string and you can read words from a stream.std::istream_iterator loop(stream);
std::istream_iterator end;
for(;loop != end;++loop)
{ std::cout << *loop << ".\n";
}Since we now have an iterator to scan through a stream of words we can also apply the standard algorithms.
// Assuming the loop above had not drained the stream.
// The following alternative copies a stream into the vector one word at a time.
std::vector data;
std::copy(loop, end, std::back_inserter(data));Also note that
std::vector has a constructor that takes iterators so you can build it directly, rather than using an algorithm.std::vector data(loop, end);So now that we have gone through all that we can re-code your function like this:
#include
#include
#include
#include
std::vector str2arg(std::string const& str)
{
std::stringstream stream(str);
std::vector result((std::istream_iterator(stream)),
std::istream_iterator());
return result;
}Side note.
Since we mention command line args in other answers.
I like to convert the command line args into string before proceeding.
int main(int argc, char* argv[])
{
std::vector args(argv+1, argv + argc);
}main
int main()
{
std::vector data = str2arg(" this is a test string ");
std::vector ptr;
// Doing it like this because I don't feel inclined to go look
// up the appropriate iterator adapter to convert a string into a pointer.
std::for_each(args.begin(), args.end(),
[&ptr](std::string& a){ptr.push_back(&a[0]);});
SomeCFunctionThatNeedsPointers(&ptr[0]);
}Main with Boost
#include
int main()
{
typedef boost::transform_iterator,
std::vector::iterator
> ti;
std::vector data = str2arg(" this is a test string ");
std::vector ptr(ti(args.begin(), [](std::string& x){return &x[0];}), ti(args.end()));
SomeCFunctionThatNeedsPointers(&ptr[0]);
}Code Snippets
void str2arg(const char * str, std::vector<char*> & arg, std::string & memory) {std::vector<char*> str2arg(const char * str);std::vector<std::string> str2arg(std::string const& str);
^^^^^^ Allows you to pass a literal.
That can be converted to a string.std::string word;
stream >> word; // drops leading space and reads a single space delimited word.std::istream_iterator<std::string> loop(stream);
std::istream_iterator<std::string> end;
for(;loop != end;++loop)
{ std::cout << *loop << ".\n";
}Context
StackExchange Code Review Q#61455, answer score: 6
Revisions (0)
No revisions yet.