sabanana sabanana - 8 days ago 4
C++ Question

fatal error on std::transform c++

I'm trying to transform a vector of foo instances to a string but I'm having fatal error on std::transform.

Say

data
has the following value:

[0]
[name] = John
[size] = 3423

[1]
[name] = Joseph
[size] = 3413


Code:

struct foo {
foo(std::string n, size_t s)
: name(std::move(n)),
size(s)
{
}
std::string name;
size_t size;
};

std::string server = "1";
std::vector<std::string> output;
output.reserve(static_cast<unsigned_long>(std::distance(std::begin(data), std::end(data))));

std::transform(std::begin(data),
std::end(data),
std::begin(output),
[&, this](foo const& item){

std::ostringstream result;
data << server << ","
<< item.name << ","
<< item.size << ";";
return result.str();
});


While debugging it stops at the line
*__result = __unary_op(*_first)
of
tranform
implementation in
stl_algo.h
then goes to FatalConditionHandler of catch test framework. I'm new to both catch testing and std::transform. Can someone explain what might cause the problem and how to solve it? Thanks a lot!

Answer

You've reserved space in output, but you've left its size at zero.

You then proceed to write through its begin iterator, just as if it had space to hold data.

Then everything goes "boom".

Instead of writing through std:begin(output), consider using std::back_inserter(output) as the destination iterator.

You also have one other problem: inside your lambda, you have:

        std::ostringstream result;
        data << server << ","
             << item.name << ","
             << item.size << ";";
        return result.str();

This looks like a fairly obvious mistake--you undoubtedly intended:

        std::ostringstream result;
        result << server << ","
             << item.name << ","
             << item.size << ";";
        return result.str();

Personally, I'd probably structure the code somewhat differently. I'd add something like:

struct foo {
    std::string name;
    size_t size;

    // new addition:
    friend std::ostream &operator<<(std::ostream &os, foo const &f) { 
        return os << f.name << ',' << f.size;
    }
};

...then the lambda in your transform becomes rather simpler:

std::ostringstream result;
result << server << "," item;
return result.str();

It might, however, be worth considering doing this without the stringstream itermediaries at all. In this case, you really just need string concatenation, and they impose quite a bit of overhead to do that.

struct foo { 
// ...
    std::string to_string() { 
        return name + "," + size;
    }
};

Then the lambda body becomes:

return server + "," + item.to_string();

Shorter, simpler, and almost certainly faster.