C++ vectors with structs not working? - c++

I was working with some files and trying to load them. I wanted to use a vector to store the final information, so I can keep it globally without needing to know how big it is. This is my code but the program doesn't finish launching:
std::string one = "v 100.32 12321.232 3232.6542";
struct Face {float x, y, z;};
std::vector<struct Face> obj;
char space[3];
sscanf(one.c_str(), "%s %f %f %f", space, &obj[1].x1, &obj[1].y1, &obj[1].z1);
std::cout << obj[1].x1 << std::endl;

Default constructed vectors start empty, and even though the compiler lets you use operator [], it's undefined behavior to do so.
You can allocate some space when you create the vector though:
std::vector<struct Face> obj(2); // Allow enough space to access obj[1]

If you want to write to element 1 in the vector, the vector must have size() >= 2. In your example, size() is always 0.
Consider creating a temporary Face and then push_back-ing it into the vector<Face>.

Maybe you are using sscanf for a good reason, but at least I think is good to point that you can use streams to load the info into the struct.
In this case, I recommend you to use istringstream class, wich lets you read values as values from a string, casting as needed. So, your code, I think I can change it to this:
std::string one = "v 100.32 12321.232 3232.6542";
struct Face {float x,y,z;};
std::vector<struct Face>obj;
char space[3];
// As mentioned previously, create a temporal Face variable to load the info
struct Face tmp; // The "struct" maybe can be omited, I prefer to place it.
// Create istringstream, giving it the "one" variable as buffer for read.
istringstream iss ( one );
// Replace this line...
//sscanf(one.c_str(), "%s %f %f %f",space,&obj[1].x1,&obj[1].y1,&obj[1].z1);
// With this:
iss >> space >> tmp.x >> tmp.y >> tmp.z;
// Add the temporal Face into the vector
obj.push_back ( tmp );
// As mentioned above, the first element in a vector is zero, not one
std::cout << obj[0].x1 << std::endl;
The istringstream class (you need to include "sstream") is useful in this cases, when you have values to load from a string.
I hope my answer can help you in any way.

Related

Best way to concatenate and condense a std::vector<std::string>

Disclaimer: This problem is more of a theoretical, rather than a practical interest. I want to find out various different ways of doing this, with speed as icing on the new year cake.
The Problem
I want to be able to store a list of strings, and be able to quickly combine them into 1 if needed.
In short, I want to condense a structure (currently a std::vector<std::string>) that looks like
["Hello, ", "good ", "day ", " to", " you!"]
to
["Hello, good day to you!"]
Is there any idiomatic way to achieve this, ala python's [ ''.join(list_of_strings) ]?
What is the best way to achieve this in C++, in terms of time?
Possible Approaches
The first idea I had is to
loop over the vector,
append each element to the first,
simultaneously delete the element.
We will be concatenating with += and reserve(). I assume that max_size() will not be reached.
Approach 1 (The Greedy Approach)
So called because it ignores conventions and operates in-place.
#if APPROACH == 'G'
// Greedy Approach
void condense(std::vector< std::string >& my_strings, int total_characters_in_list)
{
// Reserve the size for all characters, less than max_size()
my_strings[0].reserve(total_characters_in_list);
// There are strings left, ...
for(auto itr = my_strings.begin()+1; itr != my_strings.end();)
{
// append, and...
my_strings[0] += *itr;
// delete, until...
itr = my_strings.erase(itr);
}
}
#endif
Now I know, you would say that this is risky and bad. So:
loop over the vector,
append each element to another std::string,
clear the vector and make the string first element of the vector.
Approach 2 (The "Safe" Haven)
So called because it does not modify the container while iterating over it.
#if APPROACH == 'H'
// Safe Haven Approach
void condense(std::vector< std::string >& my_strings, int total_characters_in_list)
{
// Store the whole vector here
std::string condensed_string;
condensed_string.reserve(total_characters_in_list);
// There are strings left...
for(auto itr = my_strings.begin(); itr != my_strings.end(); ++itr)
{
// append, until...
condensed_string += *itr;
}
// remove all elements except the first
my_strings.resize(1);
// and set it to condensed_string
my_strings[0] = condensed_string;
}
#endif
Now for the standard algorithms...
Using std::accumulate from <algorithm>
Approach 3 (The Idiom?)
So called simply because it is a one-liner.
#if APPROACH == 'A'
// Accumulate Approach
void condense(std::vector< std::string >& my_strings, int total_characters_in_list)
{
// Reserve the size for all characters, less than max_size()
my_strings[0].reserve(total_characters_in_list);
// Accumulate all the strings
my_strings[0] = std::accumulate(my_strings.begin(), my_strings.end(), std::string(""));
// And resize
my_strings.resize(1);
}
#endif
Why not try to store it all in a stream?
Using std::stringstream from <sstream>.
Approach 4 (Stream of Strings)
So called due to the analogy of C++'s streams with flow of water.
#if APPROACH == 'S'
// Stringstream Approach
void condense(std::vector< std::string >& my_strings, int) // you can remove the int
{
// Create out stream
std::stringstream buffer(my_strings[0]);
// There are strings left, ...
for(auto itr = my_strings.begin(); itr != my_strings.end(); ++itr)
{
// add until...
buffer << *itr;
}
// resize and assign
my_strings.resize(1);
my_strings[0] = buffer.str();
}
#endif
However, maybe we can use another container rather than std::vector?
In that case, what else?
(Possible) Approach 5 (The Great Indian "Rope" Trick)
I have heard about the rope data structure, but have no idea if (and how) it can be used here.
Benchmark and Verdict:
Ordered by their time efficiency (currently and surprisingly) is1:
Approaches Vector Size: 40 Vector Size: 1600 Vector Size: 64000
SAFE_HAVEN: 0.1307962699997006 0.12057728999934625 0.14202970000042114
STREAM_OF_STRINGS: 0.12656566000077873 0.12249500000034459 0.14765803999907803
ACCUMULATE_WEALTH: 0.11375975999981165 0.12984520999889354 3.748660090001067
GREEDY_APPROACH: 0.12164988000004087 0.13558526000124402 22.6994204800023
timed with2:
NUM_OF_ITERATIONS = 100
test_cases = [ 'greedy_approach', 'safe_haven' ]
for approach in test_cases:
time_taken = timeit.timeit(
f'system("{approach + ".exe"}")',
'from os import system',
number = NUM_OF_ITERATIONS
)
print(approach + ": ", time_taken / NUM_OF_ITERATIONS)
Can we do better?
Update: I tested it with 4 approaches (so far), as I could manage in my little time. More incoming soon. It would have been better to fold the code, so that more approaches could be added to this post, but it was declined.
1 Note that these readings are only for a rough estimate. There are a lot of things that influence the execution time, and note that there are some inconsistencies here as well.
2 This is the old code, used to test only the first two approaches. The current code is a good deal longer, and more integrated, so I am not sure I should add it here.
Conclusions:
Deleting elements is very costly.
You should just copy the strings somewhere, and resize the vector.
Infact, better reserve enough space too, if copying to another string.
You could also try std::accumulate:
auto s = std::accumulate(my_strings.begin(), my_strings.end(), std::string());
Won't be any faster, but at least it's more compact.
With range-v3 (and soon with C++20 ranges), you might do:
std::vector<std::string> v{"Hello, ", "good ", "day ", " to", " you!"};
std::string s = v | ranges::view::join;
Demo
By default, I would use std::stringstream. Simply construct the steam, stream in all the strings from the vector, and then return the output string. It isn't very efficient but it is clear what it does.
In most cases, one doesn't need fast method when dealing with strings and printing - so the "easy to understand and safe" methods are better. Plus, compilers nowadays are good at optimizing inefficiencies in simple cases.
The most efficient way... it is a hard question. Some applications require efficiency on multiple fronts. In these cases you might need to utilize multithreading.
Personally, I'd construct a second vector to hold a single "condensed" string, construct the condensed string, and then swap vectors when done.
void Condense(std::vector<std::string> &strings)
{
std::vector<std::string> condensed(1); // one default constructed std::string
std::string &constr = &condensed.begin(); // reference to first element of condensed
for (const auto &str : strings)
constr.append(str);
std::swap(strings, condensed); // swap newly constructed vector into original
}
If an exception is thrown for some reason, then the original vector is left unchanged, and cleanup occurs - i.e. this function gives a strong exception guarantee.
Optionally, to reduce resizing of the "condensed" string, after initialising constr in the above, one could do
// optional: compute the length of the condensed string and reserve
std::size_t total_characters_in_list = 0;
for (const auto &str : strings)
total_characters_in_list += str.size();
constr.reserve(total_characters_in_list);
// end optional reservation
As to how efficient this is compared with alternatives, that depends. I'm also not sure it's relevant - if strings keep on being appended to the vector, and needing to be appended, there is a fair chance that the code that obtains the strings from somewhere (and appends them to the vector) will have a greater impact on program performance than the act of condensing them.

C++: Member functions that read and write from txt file

I apologize in advance for how poorly this question is asked, I'm really struggling here.
I am writing a class named Point in C++ with private members x and y, and member functions getX, getY, setX, setY, read and write. I have been able to do everything except read and write, as I am awful with input/output files. I have the following declaration for read and write:
void read(istream& ins);
void write(ostream& outs);
The RME is as follows for read:
* Requires: ins is in good state.
* Modifies: ins, x, y.
* Effects: Reads point in form (x,y)
and for write:
* Requires: outs is in good state.
* Modifies: outs.
* Effects: Writes point in form (x,y).
'read' takes ordered points like (1, 5), (2, 7), etc. from a given file "data1.txt" and extracts the x and y components (at least, I believe this is what it should do). I was provided with a test suite for read:
void test_point() {
Point pt1;
pt1.setX(15);
cout << "pt1 is: " << pt1 << endl;
ifstream input_file;
input_file.open("data1.txt");
pt1.read(input_file);
cout << "pt1 is: " << pt1 << endl;
return;}
I really have no idea how to write the read function. I have tried defining characters a, b, c, and integers u, v, and executing:
ins >> a >> u >> b >> v >> c;
but that didn't work. Could someone please help me see how to implement this?
Quite a few things missing from your question, that you will need such that the use of this class to be viable. For one, reading a file of ordered points should not be implemented as a member function. If anything, you could use a loop as such:
while(input_file) {
// set a point to have members x, y that were read from file
// store this point in a vector of points
}
Otherwise there is no reasonable way to store a bunch of points that you have read from a file.
Your idea for this solution within the read function should definitely work (assuming chars a, b, c, and ints u, v):
input_stream >> a >> u >> b >> v >> c;
In fact, with the format that you have given us (taking an ifstream as an argument), there really is no need for be much else in the read function, as it mutates the object and doesn't need to return anything. Your implementation is the simplest way to parse such "records" in a structured file.
After you read all these, set the x coordinate of the caller object to u, and the y to v. This should be a void function, does not need to return anything but should alter the Point object that it is being called for. This member function should be called on a temporary point object in the loop that I mentioned, then add that object to a vector of points as such:
vector<Point> points;
while(...) {
// declare Point
// initialize with values from read
points.push_back(//the point you just created);
}
If in fact you need to be able to read multiple points.
In summary, your read function needs:
To check that the ifstream is actually good first of all, which I didn't mention yet (but this is one of your requirements):
if (!input_file) { //however you want to handle this error }
The temporary char and int variables (serving as buffers) to read into (by the way, you can even read straight into x and y instead of reading into u and v then copying, just saying).
If you choose to not read straight into x and y, you must assign the u and v values to x and y. Now your object is complete.
As for the write function, instead of std::cout you will use the name of the ofstream that was passed as an argument to the write function, and write the records with the format that you have shown. This is (in essence) no different from printing to a console, except the output is on a text file.
Note: Make sure you understand the difference between iostream objects (istream, ostream) and fstream (ifstream, ofstream, fstream) objects, which are preferable in this case.

std::vector<float> to std::vector<glm::vecX> without copying

I am writing a small toy game engine using Tinyobjloader for loading .obj files. I store the vertex data and everything using glm::vecX to make things easier.
Tinyobjloader gives me an std::vector<float>, when I want an std::vector<glm::vecX>. How would I do this without copying?
To be clear, a glm::vecX is a simple struct containing, for example, the float members x, y, z.
I was thinking that since structs can behave a bit like arrays, that std::move would work, but no luck.
Thanks!
Edit:
I know I wasn't clear about this, sorry. I would like to either move the std::vector<float> into an std::vector<glm::vecX> or pass it as a std::vector<glm::vecX>&.
Copying the data using std::memcpy works fine, but it copies the data, which I would like to avoid.
It may be possible to directly interpret the contents of the vector as instances of the struct, without having to copy the data. If you can guarantee the representation is compatible, that is. The contents of a vector<float> are laid out in memory as a sequence of float values directly following each other (an array) with no extra padding, while the contents of a vector<glm::vecX> are laid out as a sequence of vecX. Thus, you need to ensure the following conditions hold:
That glm::vecX is exactly the size of X floats, with no padding. Depending on the declaration of the struct, this may be platform-dependant.
That the contents of the vector<float> are in the correct sequence, i.e. as [x1,y1,z1, x2,y2,z2, ...] for a vec3 instead of [x1,x2,...,xN,y1,y2...].
In that case, you can safely reinterpret the data pointer of the float vector as pointer to an array of vecX as in this example:
std::vector<float> myObjData = ...;
auto nVecs = myObjData.size() / 3; // You should check that there are no remainders!
glm::vec3* vecs = reinterpret_cast<glm::vec3*>(myObjData.data());
std::cout << vecs[0]; // Use vecs[0..nVecs-1]
You cannot, however, safely reinterpret the vector itself as a vector of glm::vecX, not even as a const vector, because the number of elements stored in the vector might not be consistent after the reinterpretation. It depends on whether the vector<T> code stores the number of elements directly, or the number of allocated bytes (and then size() divides that by sizeof(T)):
// Don't do this, the result of .size() and .end() may be wrong!
const std::vector<glm::vec3>& bad = *reinterpret_cast<std::vector<glm::vec3>*>(&myObjData);
bad[bad.size()-1].z = 0; // Potential BOOM!
Most of the time, however, you don't need to pass an actual vector, since most functions in the standard library accept a container range, which is easy to give for arrays like the one in the first example. So, if you wanted to sort your vec3 array based on z position, and then print it out you would do:
// nVecs and vecs from the first example
std::sort(vecs, vecs+nVecs, // Sort by Z position
[](const glm::vec3& a, const glm::vec3& b) { return a.z < b.z; });
std::copy(vecs, vecs+nVecs, std::ostream_iterator<glm::vec3>(std::cout, "\n"));
In short: It is - to the best of my knowledge - not possible without copying.
And in my opinion, std::memcpy has no business being used with std::vector.

Prepend std::string

What is the most efficient way to prepend std::string? Is it worth writing out an entire function to do so, or would it take only 1 - 2 lines? I'm not seeing anything related to an std::string::push_front.
There actually is a similar function to the non-existing std::string::push_front, see the below example.
Documentation of std::string::insert
#include <iostream>
#include <string>
int
main (int argc, char *argv[])
{
std::string s1 (" world");
std::string s2 ("ello");
s1.insert (0, s2); // insert the contents of s2 at offset 0 in s1
s1.insert (0, 1, 'h'); // insert one (1) 'h' at offset 0 in s1
std::cout << s1 << std::endl;
}
output:
hello world
Since prepending a string with data might require both reallocation and copy/move of existing data you can get some performance benefits by getting rid of the reallocation part by using std::string::reserve (to allocate more memory before hand).
The copy/move of data is sadly quite inevitable, unless you define your own custom made class that acts like std::string that allocates a large buffer and places the first content in the center of this memory buffer.
Then you can both prepend and append data without reallocation and moving data, if the buffer is large enough that is. Copying from source to destination is still, obviously, required though.
If you have a buffer in which you know you will prepend data more often than you append a good alternative is to store the string backwards, and reversing it when needed (if that is more rare).
myString.insert(0, otherString);
Let the Standard Template Library writers worry about efficiency; make use of all their hours of work rather than re-programming the wheel.
This way does both of those.
As long as the STL implementation you are using was thought through you'll have efficient code. If you're using a badly written STL, you have bigger problems anyway :)
If you're using std::string::append, you should realize the following is equivalent:
std::string lhs1 = "hello ";
std::string lhs2 = "hello ";
std::string rhs = "world!";
lhs1.append(rhs);
lhs2 += rhs; // equivalent to above
// Also the same:
// lhs2 = lhs2 + rhs;
Similarly, a "prepend" would be equivalent to the following:
std::string result = "world";
result = "hello " + result;
// If prepend existed, this would be equivalent to
// result.prepend("hello");
You should note that it's rather inefficient to do the above though.
There is an overloaded string operator+ (char lhs, const string& rhs);, so you can just do your_string 'a' + your_string to mimic push_front.
This is not in-place but creates a new string, so don't expect it to be efficient, though. For a (probably) more efficient solution, use resize to gather space, std::copy_backward to shift the entire string back by one and insert the new character at the beginning.
The problem is efficiency: inserting to the beginning of the string is more expensive as it requires both reallocation and shifting of existing characters.
If you are only prepending to the string, the most efficient way is appending, and then either reverse the string, or even better, go through the string in reverse order.
string s;
for (auto c: "foobar") {
s.push_back(c);
}
for (auto it=s.rbegin(); it!=s.rend(); it++) {
// do something
}
If you need a mix of prepending and appending, I'd suggest using a deque, and then construct a string from it.
The double-ended queue supports O(1) insertion and deletion at the beginning and end.
deque<char> dq;
dq.push_front('f');
dq.push_back('o');
dq.push_front('o');
string s {dq.begin(), dq.end()};

C++ Tokenizing using iterators in an eof() cycle

I'm trying to adapt this answer
How do I tokenize a string in C++?
to my current string problem which involves reading from a file till eof.
from this source file:
Fix grammatical or spelling errors
Clarify meaning without changing it
Correct minor mistakes
I want to create a vector with all the tokenized words. Example: vector<string> allTheText[0] should be "Fix"
I don't understad the purpose of istream_iterator<std::string> end; but I included cause it was on the original poster's answer.
So far, I've got this non-working code:
vector<string> allTheText;
stringstream strstr;
istream_iterator<std::string> end;
istream_iterator<std::string> it(strstr);
while (!streamOfText.eof()){
getline (streamOfText, readTextLine);
cout<<readTextLine<<endl;
stringstream strstr(readTextLine);
// how should I initialize the iterators it and end here?
}
Edit:
I changed the code to
vector<string> allTheText;
stringstream strstr;
istream_iterator<std::string> end;
istream_iterator<std::string> it(strstr);
while (getline(streamOfText, readTextLine)) {
cout << readTextLine << endl;
vector<string> vec((istream_iterator<string>(streamOfText)), istream_iterator<string>()); // generates RuntimeError
}
And got a RuntimeError, why?
Using a while (!….eof()) loop in C++ is broken because the loop will never be exited when the stream goes into an error state!
Rather, you should test the stream's state directly. Adapted to your code, this could look like this:
while (getline(streamOfText, readTextLine)) {
cout << readTextLine << endl;
}
However, you already have a stream. Why put it into a string stream as well? Or do you need to do this line by line for any reason?
You can directly initialize your vector with the input iterators. No need to build a string stream, and no need to use the copy algorithm either because there's an appropriate constructor overload.
vector<string> vec((istream_iterator<string>(cin)), istream_iterator<string>());
Notice the extra parentheses around the first argument which are necessary to disambiguate this from a function declaration.
EDIT A small explanation what this code does:
C++ offers a unified way of specifying ranges. A range is just a collection of typed values, without going into details about how these values are stored. In C++, these ranges are denoted as half-open intervals [a, b[. That means that a range is delimited by two iterators (which are kind of like pointers but more general; pointers are a special kind of iterator). The first iterator, a, points to the first element of the range. The second, b, points behind the last element. Why behind? Because this allows to iterate over the elements very easily:
for (Iterator i = a; i != b; ++i)
cout << *i;
Like pointers, iterators are dereferenced by applying * to them. This returns their value.
Container classes in C++ (e.g. vector, list) have a special constructor which allows easy copying of values from another range into the new container. Consequently, this constructor expects two iterators. For example, the following copies the C-style array into the vector:
int values[3] = { 1, 2, 3 };
vector<int> v(values, values + 3);
Here, values is synonymous with &values[0] which means that it points to the array's first element. values + 3, thanks to pointer arithmetic, is nearly equivalent to &values[3] (but this is invalid C++!) and points to the virtual element behind the array.
Now, my code above does the exact same as in this last example. The only difference is the type of iterator I use. Instead of using a plain pointer, I use a special iterator class that C++ provides. This iterator class wraps an input stream in such a way that ++ advances the input stream and * reads the next element from the stream. The kind of element is specified by the type argument (hence string in this case).
To make this work as a range, we need to specify a beginning and an end. Alas, we don't know the end of the input (this is logical, since the end of the stream may actually move over time as the user enters more input into a console!). Therefore, to create a virtual end iterator, we pass no argument to the constructor of istream_iterator. Conversely, to create a begin iterator, we pass an input stream. This then creates an iterator that points to the current position in the stream (here, cin).
My above code is functionally equivalent to the following:
istream_iterator<string> front(cin);
istream_iterator<string> back;
vector<string> vec;
for (istream_iterator<string> i = front; i != back; ++i)
vec.push_back(*i);
and this, in turn, is equivalent to using the following loop:
string word;
while (cin >> word)
vec.push_back(word);