I have a function with the following signature:
std::string f(const char *first, const char *last) {
std::string result;
std::for_each(first, last, some_lambda_which_appends_to_result);
return result;
}
and an overload for std::string which calls it:
std::string f(const std::string s) {
return f(&*s.begin(), &*s.end());
// The one below would assume that the string is not empty
// f(& s.front(), & s.front() + s.size());
}
However, this may be unsafe (dereferencing s.end() might be a red card offense in itself).
Is there a safe way to get a pointer to the beginning of characters and a one-past-the-end pointer (two null pointers would be fine in case of an empty string),
or do I have to write
std::string(const std::string& s) {
return s.empty() ? std::string() : f(& s.front(), & s.front() + s.size());
}
It's not safe to dereference end(). However, you can use either c_str() or data() to achieve what you need:
std::string(const std::string& s) {
return f(s.data(), s.data() + s.size());
}
Related
I want to replace my buffer string from a value of "eof" to "\0" using std::replace.
std::string buffer = "eof";
std::replace(buffer.begin(), buffer.end(), std::string("eof"), std::string("\0"));
Compiler error :
no match for ‘operator==’ (operand types are ‘char’ and ‘const std::__cxx11::basic_string<char>’)
The problem is that internally std::replace checks == on *first and old_value where first is the first argument passed(iterator here) and old_value is the third argument passed shown in the below possible implementation:
template<class ForwardIt, class T>
void replace(ForwardIt first, ForwardIt last,
const T& old_value, const T& new_value)
{
for (; first != last; ++first) {
//----------vvvvvv vvvvvvvvv--------->compares *first with old_value
if (*first == old_value) {
*first = new_value;
}
}
}
Now *first is char and old_value(and new_value) is std::string in our example and since there is no operator== for checking if a char and a std::string are equal and also as there is no implicit conversion between the two, we get the mentioned error.
One way to solve this is to use std::string::replace. More alternatives can be found at : Replace substring with another substring C++
You can use std::string::replace, if you intention is to replace all occurences of eof substring with \0 then I suggest using standard method as in:
https://en.cppreference.com/w/cpp/string/basic_string/replace
std::size_t replace_all(std::string& inout, std::string_view what, std::string_view with)
{
std::size_t count{};
for (std::string::size_type pos{};
inout.npos != (pos = inout.find(what.data(), pos, what.length()));
pos += with.length(), ++count) {
inout.replace(pos, what.length(), with.data(), with.length());
}
return count;
}
Your code should look as follows:
std::string buffer = "eof";
replace_all(buffer, "eof", "\0");
https://coliru.stacked-crooked.com/a/4110765b0f265aae
I'm converting this function to use std::tuple which does not have first and second memebers like std:pair.
std::type_index argumentType(const std::string& name) const
{
return std::find_if(args_.begin(), args_.end(),
[&name](std::pair<std::string, std::type_index> arg)->bool
{
return arg.first == name;
}
)->second;
}
I'm confused by the syntax ->second, what is this doing? and is thre equivalent std::get<1>(arg)
std::type_index argType(const std::string& name) const
{
return std::find_if(args_.begin(), args_.end(),
[&name](std::tuple<std::string, std::type_index, Attribute> arg)->bool
{
return std::get<0>(arg) == name;
}
)std::get<1>(arg);
Example 2:
std::pair
bool hasArg(const std::string& name) const
{
return std::find_if(args_.begin(), args_.end(),
[&name](std::pair<std::string, std::type_index> arg)->bool
{
return arg.first == name;
}
) != args_.end();
}
std::tuple
bool hasArg(const std::string& name) const
{
return std::get<0>(*std::find_if(args_.begin(), args_.end(),
[&name](std::tuple<std::string, std::type_index, Attribute> arg)->bool
{
return std::get<0>(arg) == name;
}
)) != args_.end();
}
std::find_if returns an iterator to a pair, that's why you need to use the arrow operator ->, which the iterator has overloaded to return a reference to the actual value for purposes of member access.
Yes, std::get<1> for a tuple will do pretty much the same, but the syntax will be different:
auto it = std::find_if(...);
return std::get<1>(*it);
You need to use the dereference operator * instead of the arrow operator, because std::get is a free function and not a tuple's member.
Checkout documentation of std::find_if
So std::find_if will return iterator. In code #1 it was iterator to pair, so ->second will husk second element of std::pair. You use -> overloaded operator for iterator.
Example:
auto it = std::find_if(args_.begin(), args_.end(),...);
if(it != args_.end()) // This is important to NOT dereference iterator if it is pointing to end
{
// also you can write (*it).second; (first use dereference operator)
return it->second;
}
// ...
So in you case you should refactor it to:
std::type_index argType(const std::string& name) const
{
// Please don't do so much in 1 line. Compiler will optimize this for you :)
// Please check for std::end, this is potential dangerous.
return std::get<1>(*std::find_if(args_.begin(), args_.end(),
[&name](std::tuple<std::string, std::type_index, Attribute> arg)->bool
{
return std::get<0>(arg) == name;
}
));
}
Same example:
auto it = std::find_if(args_.begin(), args_.end(),...);
if(it != args_.end()) // This is important to NOT dereference iterator if it is pointing to end
{
return std::get<1>(*it);
}
// ...
Important
Please also consider case when you don't find your element. std::find_if will return std::end(args_) if element you are looking for doesn't exist. So calling ->second or std::get<1>(*it) is undefinied behaviour and can crush.
Fix for hasArg function. Now you don't need dereference your iterator.
bool hasArg(const std::string& name) const
{
return std::find_if(std::begin(args_), std::end(args_),
[&name](const std::tuple<std::string, std::type_index, Attribute>& arg)->bool
{
return std::get<0>(arg) == name;
}
) != std::end(args_);
}
In this solution I used std::begin and std::end. This is more uniwersal way instead calling arg_.begin() :)
Please check how I ficed syntax of your second example.
One last thing, when you use std::find_if and lambda to find what you are looking for, argument should be const& in most cases :), because we don't want to make a copy here.
I am trying to implement the functions below, but the output for foo() is a bunch of nonsense. I tried to run the debugger and didn't see any problems inside the append function. But the total variable in foo() isn't properly assigned the value "abcdef". Any ideas why?
int main()
{
cout<<"foo is"<<endl;
foo();
return 0;
}
const char* append(const char* s1, const char* s2) {
string s(s1);
s += s2;
return s.c_str();
}
void foo() {
const char* total = append("abc", "def");
cout<<total;
}
Because in append(), you returned s.c_str(); then, s is destructed, which means that the pointer returned is invalidated immediately.
Let append() return std::string to solve this issue.
std::string append(const char* s1, const char* s2) {
return std::string(s1).append(s2);
}
void foo() {
std::string total = append("abc", "def");
cout << total;
}
Undefined Behaviour. c_str is only valid for the lifetime of s (and only then if s is not modified in any way). Once append has returned, s is out of scope. Boom!
One fix is to have append return a std::string.
const char* append(const char* s1, const char* s2) {
string s(s1);
s += s2;
return s.c_str();
}
The variable s is local to the function. It is destroyed when the function returns. That means that the value that you return, s.c_str(), points to memory that has been deallocated. De-referencing that memory results in undefined behaviour.
The rule with c_str() is, loosely put, that the value it returns is guaranteed to be valid until either the string object is modified or destroyed.
Simply put, you should stop using C strings unless you need to use them for interop. Your function should be:
string append(const string& s1, const string& s2)
{
return s1 + s2;
}
You are returning a pointer from append() that is not valid when the function returns.
string s(s1);
defines a object in append(). It is destroyed when you return from the function. Hence, the returned value of s.c_str() is not valid in foo.
You can change your code to be:
string append(const char* s1, const char* s2) {
string s(s1);
s += s2;
return s;
}
void foo() {
string total = append("abc", "def");
cout<<total;
}
That should work.
return s.c_str(); : you return pointer obtained from temporary variable s.
You may fix your problem in two ways -
Return std::string by value from append.
Pass to append pointer to fill it with data -
void append(const char* s1, const char* s2, char* out) {
string s(s1);
s += s2;
strncpy(out, s.c_str(), s.size()+1);
}
void foo() {
char total[7] = {0}; //should be enough to carry appended string + '\0'
append("abc", "def", total);
cout<<total;
}
Accelerated C++, exercise 14.5 involves reimplementing a split function (which turns text input into a vector of strings). One must use store input in a std::string - like class (Str) & use the split function to return a Vec<Str>, Vec being a std::vector - like container. The Str class manages a custom pointer (Ptr) to the underlying Vec<char> data.
The Str class provides a constructor Str(i,j), in Str.h below, which constructs a Ptr to the underlying Vec
The problem arises when I try to create substrings by calling str(i,j)
I've detailed in the code where the the issues arise.
Here is a whittled-down version of the Str class (can post more code if needed):
Str.h
#include "Ptr.h"
#include "Vec.h"
class Str {
friend std::istream& operator>>(std::istream&, Str&);
public:
// define iterators
typedef char* iterator;
typedef char* const_iterator;
iterator begin() { return data->begin(); }
const_iterator begin() const { return data->begin(); }
iterator end() { return data->end(); }
const_iterator end() const { return data->end(); }
//** This is where we define a constructor for `Ptr`s to substrings **
template<class In> Str(In i, In j): data(new Vec<char>) {
std::copy(i, j, std::back_inserter(*data));
}
private:
// store a Ptr to a Vec
Ptr< Vec<char> > data;
};
Split.h
Vec<Str> split(const Str& str) {
typedef Str::const_iterator iter;
Vec<Str> ret;
iter i = str.begin();
while (i != str.end()) {
// ignore leading blanks
i = find_if(i, str.end(), not_space);
// find end of next word
iter j = find_if(i, str.end(), space);
// copy the characters in `[i,' `j)'
if (i != str.end())
ret.push_back(**substring**); // Need to create substrings here
// call to str(i,j) gives error, detailed below
i = j;
}
return ret;
}
My first thought was to use this constructor to create (pointers to) the required substrings. Calling str(i,j) here gives the error message
type 'const Str' does not provide a call operator
It appears as if one cannot simply call str(i,j) here. Why not?
Could a solution be to write a Str member function which is similar to substr?
I have a problem that requires me to count the number of instances within this array that uses either std::count() or std::find(). I'm aware of how to do this using a standard data (see bottom code) type but not with the NameContainer that I'm using.
//Type
struct NameContainer{
char name [32];
}
//An array of containers
NameContainer *_storedNames = new NameContainer[_numberOfNames];
//An example of what I'm trying to do with a string rather than the NameContainer
std::vector<string> v(_storedNames, _storedNames + _numberOfNames);
//returns an numeric value
return std::count(v.begin(), v.end(), nameToSearch))
You can use a functor
struct names_equal {
string comp_to;
names_equal(string a) : comp_to(a) {}
bool operator()(NameContainer& p) {
return p.name == comp_to;
}
};
And count like
cout << std::count_if(v.begin(), v.end(), names_equal(nameToSearch));
This way nameToSearch doesn't have to be hard coded.
EDIT
If you can not use count_if, and has to be count then modify NameContainer and overload == for it.
struct NameContainer{
string name;
bool operator==(string str) {
return name == str;
}
};
Then count like this
cout << std::count(v.begin(), v.end(), nameToSearch);
you can use count_if and you provide a predicate (Unary function that accepts an element in the range as argument, and returns a value convertible to bool)
for example
bool myPred(NameContainer n){
return (strcmp(n.name, "name") == 0); }
std::vector<NameContainer> v(_storedNames, _storedNames + _numberOfNames);
int i=std::count_if(v.begin(), v.end(), myPred))
you can use strcmp() to compare character arrays.
if using only std::count or std::find:
both count and find takes the same type argument to compare as the type of conatainer, in your case NameContainer. std::count will execute following to compare searched values:
if (*first == val)
what means you have to overload operator== taking your class as arguments.
inline bool operator == (const NameContainer &first,const NameContainer &second){
return (strcmp(first.name,second.name)==0);
}
and then call std::count(v.begin(), v.end(), myObjectPredicate))
with myObjectPredicate being your NameContainer class object with name to be searched in vector.
so here is working solution. you might improve it in details:
struct NameContainer{
char name [32];
};
inline bool operator== (const NameContainer &first,const NameContainer &second){
return (strcmp(first.name,second.name)==0);
}
int main(int argc, char** argv) {
NameContainer* _storedNames = new NameContainer[1];
std::vector<NameContainer> vn(_storedNames, _storedNames + 1);
const char* cc="piotr";
NameContainer nc;
memcpy(nc.name,cc,strlen(cc)+1);
vn.push_back(nc);
NameContainer myObjectPredicate;
memcpy(myObjectPredicate.name,cc,strlen(cc)+1);
int count=std::count(vn.begin(), vn.end(), myObjectPredicate);
std::cout<<count;
return 2400;
}
output:
1
Read the docs on std::count, you'll see that it uses operator== for it's comparisons. Therefore, if you want to use std::count, the thing you want to compare MUST have an operator== defined for it. In your case, you could add one to your NameContainer pretty easily.