class XYZCoordinate{ public: int x; int y; float z; XYZCoordinate(int X, int Y, float Z){ x = X; y = Y; z = Z; } };
Integrate a formatting utility, like clang-format or astyle, into your tool-chain. Your indentations and spacing are inconsistent.
Do you plan on working with x
and y
as float
s? Avoid transformations until you absolutely need them.
For constructors, prefer initialization over assignment.
class XYZCoordinate { public: XYZCoordinate(int x, int y, float z) : x{x}, y{y}, z{z} {} int x; int y; float z; };
Consider leveraging the type system to safely operate on the dimensions of your coordinate class. You can read up on strong types here and here
Loadfileoutput(int xmn, int xmx, int ymn, int ymx, vector<XYZCoordinate> val){
Avoid using namespace std;
.
Your coordinate vector argument is being copied here. Treat vector<XYZCoordinate>
either as a input-only argument or as a sink argument.
// In-Only - Pass by reference to const then copy Loadfileoutput(/* ... */, const std::vector<XYZCoordinate>& val) : /* ... */ , values{val} // copied! {} // Sink - Pass by value and move into place Loadfileoutput(/* ... */, std::vector<XYZCoordinate> val) : /* ... */ , values{std::move(val)} // moved! {}
std::string str;
Poorly-chosen names can mislead the reader and cause bugs. It is important to use descriptive names that match the semantics and role of the underlying entities, within reason. Your intent is to use the string as a buffer, so just name it buffer
.
bool isfirst = true; int xmin, xmax, ymin, ymax;
Any time you have a situation where you need a boolean flag to distinguish between a value being set or not, consider using an optional type (boost::optional
, std::optional
, etc).
vector<XYZCoordinate> file = vector<XYZCoordinate>();
Don't repeat yourself.
std::vector<XYZCoordinate> coordinates(); ^ ^ Type mentioned once Has an appropriate name
while (std::getline(in, str)) if (str[0] == '#') //.skipping the .txt file header
std::getline
does not skip leading whitespace. Use the IO manipulator std::ws
:
while (std::getline(in >> std::ws, str)) if (str[0] == '#') //.skipping the .txt file header
If you really want to use std::getline
and a string buffer, consider using string_views liberally to access the buffer. Trim and split are easy to implement for string views. Numeric conversions already exists (std::from_chars
).
vector<string> v = vector<string>();
std::vector
doesn't have a small buffer optimization to take advantage of, so every loop on a line builds up and tears down this buffer. Move it outside the loop. That will also mean that you need to appropriately size it (3 records + remaining) and you'll need to wrap either boost::split
or boost::is_any_of
with that limits to 3 splits.
std::string
does have a small string optimization, but it's implementation defined on how much space you have before you are forced to allocate.

The longest coordinate from your example above, including sign and decimal, is 14 characters in length. If it's possible for coordinates to be longer than the SSO supported capacity of your implementation, consider using a string view instead (boost::string_view
, std::string_view
) and use from_chars()
to convert to your numeric type.
boost::split(v, str, boost::is_any_of(" ,"));
Separators are an interesting detail you must take care with. Locales using the SI style may use spaces for its thousands separator. Almost half of the countries using the hindu-arabic numeral system use period for its decimal separator. A similar amount use the comma as a decimal separator. The rest use some combination of both and other characters. Unless you are sure everyone uses the same locale, you just can't split on comma. Similarly, CSV fields typically allow commas to be part of the value, as long as they are part of an escaped sequence,
212311231,3.14,"3,14" ^ ^ ^ | | Don't split on this Split on these commas
If you want to support CSVs, then support the full specification or document the extent by which you support comma separated values.
string xstr = v[0]; string ystr = v[1]; string zstr = v[2];
These are unused variables and should be removed. Your compiler didn't mention it because copying may have intended side effects for non-pod types. Keep in mind that you have pointers and references if you need to locally refer to something.
int xint, yint; float x,y,z; stringstream(v[0]) >> x; xint = (int)round(x); stringstream(v[1]) >> y; yint = (int)round(y); stringstream(v[2]) >> z;
You construct and destruct 3 instances of std::stringstream
, which is expensive.
Make sure you are using the C++ version of std::round
by including <cmath>
.
Calls to round
can fail. Check to see if std::round
flagged an error with std::fetestexcept
.
XYZCoordinate temp = XYZCoordinate(xint, yint, z); file.push_back(temp);
You've created a named temporary object and you copy it into your vector. Since you no longer need temp
, just move it into the vector.
XYZCoordinate temp(xint, yint, z); file.push_back(std::move(temp));
You can shorten this up by avoiding the named temporary.
file.push_back(XYZCoordinate(xint, yint, z));
You can avoid the temporary by directly emplacing the objects arguments.
file.emplace_back(xint, yint, z);
Consider another approach that doesn't rely on string conversions and the temporary shuffling. Others have mentioned implementing operator>>(istream&, XYZCoordinate&)
which does a formatted read and the rounded conversion.
friend std::istream& operator>>(istream& in, XYZCoordinate& c) { float f_x; float f_y; if (!(in >> f_x >> f_y >> c.z)) { return in; } c.x = static_cast<int>(std::round(f_x)); // check the error! c.y = static_cast<int>(std::round(f_y)); // check the error! return in; }
Back in the line reading loop, you simply move the sentry in the file, skipping whitespace and unnecessary portions (your data is 1 record per line, so skip everything after the third value).
while (in >> std::ws) { // skip any whitespace if (in.peek() == '#') { // skip comment lines consume_line(in); // by skipping to '\n' continue; } auto& coord = coordinates.emplace_back(); // create the object if (!(in >> coord)) { // write its values // Read failed: Log, throw, something } consume_line(in); // the record has been read, to the next record! // use coord.x and coord.y for minmax finding. }
As the comment says, consume_line
just skips all the remaining characters upto and including the end-of-line character.
std::istream& consume_line(std::istream& stream) { return stream.ignore(std::numeric_limits<std::streamsize>::max(), stream.widen('\n')); }
Loadfileoutput output = Loadfileoutput(xmin,xmax,ymin,ymax,file); return output;
Similarly on simplifying things, you can directly initialize output
.
Loadfileoutput output(xmin, xmax, ymin, ymax, file); return output;
You don't need a named return variable as you just returning it immediately and making no changes.
return Loadfileoutput(xmin, xmax, ymin, ymax, file);
Your compiler will still be able to use return value optimization and construct the returned value in-place.
file >> x >> y >> z
instead of reading a line, putting it into stringstream, thein reading them? It also looks like input file contains commas, whereas your example does not show that.\$\endgroup\$