I have a function that reads lines from a log file, converts these lines to a certain class and returns a STL list of instances of this class.
How should I declare this function so that the whole list is NOT copied when attributing it to the caller?
Without loss of generality, assume:
list<Request> requests = log_manipulator.getAsRequestList();
How should I declare getAsRequestList()? Should I return a reference to a list or just return a list?
This is a serious issue because in this particular assignment the lists will contain circa 1.5M elements, and thus a mistake like that can screw up memory usage.
Returning a reference is not advisable, and returning the list object would cause copying. Best would be to change the method's signature to accept and populate a list reference:
list<Request> requests;
log_manipulator.getRequestListByRef(requests);
with void getRequestListByRef(list<Request>&) as the method's signature.
You have two easy options:
A return parameter
void getAsRequestList(list<Request>& requests);
...
list<Request> requests;
log_manipulator.getAsRequestList(requests);
An output iterator
template <class OutputIterator>
void getAsRequestList(OutputIterator dest);
...
list<Request> requests;
log_manipulator.getAsRequestList(
insert_iterator< list<Request> >(requests, requests.begin()) );
See also: How do I return hundreds of values from a C++ function?
You might be able to get away with returning a simple list - search for "Return Value Optimization" for details. Simply, the compiler is allowed to generate code that bypasses the costly copy constructor.
Only after trying this and not being satisfied with the results would I recommend the "populate" versions suggested in the other answers. It's uglier.
Of course, the caveat is that RVO is not guaranteed to happen.
Return a auto_ptr to the list:
auto_ptr<list<Request> > getAsRequestList()
{
auto_ptr<list<Request> > list = new list<Request>();
// populate list
return list;
}
Return a local variable as a reference is a funny thing in C++,
list<Request>& requests = log_manipulator.getAsRequestList();
it is compiler dependent so works in one machine but not the other.
your best bet is to declare your getAsRequestList() this way
void getAsRequestList(list<Request>& requests)
{
// populate results in requests
}
or new your requests on heap with in getAsRequestList() and return a pointer
A pointer to a list or you can rewrite it as:
list<request> requests;
log_manipulator.populate_list(&requests);
Pass as in/out parameter by reference is definately my choice.
But just to give an alternative you could pass an iterator. Normally you would have to pre-size the container so that assignment by the iterator goes into a pre-allocated slot but the STL also has a back inserting iterator that you could use.
#include <iostream>
#include <iterator>
#include <list>
template<typename T>
void FillMyContainer(T inserter)
{
for(int loop=0;loop < 10;++loop)
{
(*inserter) = loop;
++inserter;
}
}
int main()
{
/*
* Example of back_inserter in use.
*/
std::list<int> data;
std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::back_inserter(data));
/*
* Using back_inserter in the context of the question
*/
std::list<int> fill;
FillMyContainer(std::back_inserter(fill));
}
So the question now becomes why does log_manipulator not have methods to return an iterator rather than a list. Then your underlying implementation does not need to rely on a specific container type?
Declare is as
void getAsRequestList(list<Request>* requests);
And call it as
list<Request> requests;
log_manipulator.getAsRequestList(&requests);
Since you have mentioned about memory usage, Have you considering treating log_manipulator as 'iterator' or treat log_manipulator as container and write an iteator (e.g. log_iter) which iterates over it? Such that iterator operator++ will result in reading and parsing the next line from the log file and operator * will return a current 'request object'. As long as the iterator corresponds to STL iterator semantics, you can use any STL algorithms on it.
For example, You can then easily convert it to a vector using
std::vector<Request> reqvector;
std::copy(log_manipulator.begin(), log_manipulator.end(), std::back_inserter(reqvector))
(assuming begin(),end() function return a 'log_iter')
Check Writing Your Own Iterators article from Dr. Dobb's journal
Related
There are many questions about output iterator and sets but none of them address this particular topic.
I writing a function to deserialize a sequence of objects to a container. I wish this function be as general as possible, so container can be either a set or a vector.
I'm thinking on receiving an OutputIterator as argument where to put each new object as they are deserialized. I'm not sure what to expect when writing to the iterator if the container is a set.
To be brief, let say the containers in this example hold int instead of pointers to objects, returned one by one by getIntFromInput().
void deserializeToContainer(OutputIterator oit){
while(moreInputAvailable()){
*oit = getIntFromInput();
oit++;
}
}
void deserializeToVector(vector<int> &vectorContainer){
deserializeToContainer(vectorContainer.back_inserter());
}
void deserializeToSet(set<int> &setContainer){
deserializeToContainer(setContainer.end());
}
Since set hasn't a back_inserter, and elements aren't stored in the order they are inserted, I wonder what to expect about *oit assignment and oit increment when the container is a set, like in deserializeToSet
Thank you very much.
You can use a std::insert_iterator (probably with std::inserter)
void deserializeToSet(set<int> &setContainer){
deserializeToContainer(std::inserter(setContainer, setContainer.end()));
}
I have a vector of pointers to structs, and want to check for existing items and sort by the value of a struct member. However, it appears that the check for existing items (I'm using QVector::indexOf()) is not working, and that std::sort is applying its sorting to some pointer value rather than the member value. I don't understand why the struct operator overloads aren't getting called (or called properly).
In header file:
struct PlotColumn {
quint32 octagonLength;
QVector<Qt::GlobalColor> columnVector;
bool operator< (const PlotColumn& pc) const
{
return (this->octagonLength < pc.octagonLength);
}
bool operator==(PlotColumn const& pc)
{
return this->octagonLength == pc.octagonLength;
}
bool operator> (const PlotColumn& pc) const
{
return (this->octagonLength > pc.octagonLength);
}
};
QVector<PlotColumn*> *plotMap;
In source file:
PlotColumn *pcNew = new PlotColumn();
pcNew->octagonLength = octagonLength;
// check if octagonLength has arrived before:
int plotXAxis = plotMap->indexOf(pcNew);
if (plotXAxis == -1) { // unknown, so insert:
... (some housekeeping)
plotMap->append(pcNew);
std::sort(plotMap->begin(), plotMap->end());
....
(some more housekeeping)
}
Using an external (not in the struct) is possible for sort, but not for indexOf (afaik).
Any thoughts?
PS: yes I know there are tons of already answered questions about sorting vectors of pointers to structs and I've tried to apply the pattern in those solutions but it still doesn't work.
You need to provide a comparater to std::sort() that works with pointers.
bool PlotColumnPointerComparator(const PlotColumn *a, const PlotColumn *b)
{
return (*a) < (*b); // this will call your PlotColumn:: operator<()
}
And change your std::sort() statement to
std::sort(plotMap->begin(), plotMap->end(), PlotColumnPointerComparator);
In C++11, you could do the above with a lambda function, but that's just a syntactic convenience.
Compilers are not omnipotent mindreaders. If you tell it to sort a set of pointers, it will do pointer comparison. If you want the comparison to dereference the pointers and compare the pointed-to objects, you need to tell it to do that .... as in the above.
std::sort is applying its sorting to some pointer value
Let's see:
QVector<PlotColumn*> *plotMap
std::sort(plotMap->begin(), plotMap->end());
Yep, that's exactly what you told std::sort to do (assuming QVector resembles vector): you have a container of pointers, told it to sort the elements of the container. Thus, you told it to sort pointers, not PlotColumn objects.
If you want to sort the pointers based on how the objects they point to compare, you have to apply one of those
answered questions about sorting vectors of pointers to structs
You've already identified your problem:
I've tried to apply the pattern in those solutions but it still doesn't work.
Don't give up on that line of inquiry: you've correctly identified something you need to work on, and should work to understand how those patterns work and how to apply them to your problem, and maybe ask a focused question regarding your attempts at that.
It will not work because You are using the same signature. Overload works for different signatures. Check your function signatures.
If I have this function:
void initPoints(sf::Vector2f points[]);
Why can I do this:
sf::Vector2f[] vecs = {sf::Vector2f(0,0), etc};
initPoints(vecs);
But can't do this?
initPoints({sf::Vector2f(0,0), etc});
VS gives me an error on the second one, but not on the first one.
By using std::vector or std::array you can resolve your problem easier :)
Furthermore, std::vector is RAII-conform, so you don't have to manage the memory by yourself. Generally, STL classes are better than C-type arrays.
#include <vector>
#include <initializer_list>
// ...
std::vector<sf::Vector2f> vecs = { Vector2f(0,0), etc };
Then:
initPoints(const std::vector<sf::Vector2f>& vec) {
// ...
}
initPoints(vecs);
C++ generally doesn't allow you to pass an actual array as a function parameter. It has a convenience feature that makes you think that's possible, in that you can actually pass a pointer.
In other words:
void initPoints(sf::Vector2f points[]);
Is the same thing as
void initPoints(sf::Vector2f* points);
Note that initPoints doesn't know the length of points, so generally you also pass a length parameter:
void initPoints(sf:Vector2f* points, size_t length);
What you're trying to do simply isn't valid pre-C++11. In C++11 you can overload initPoints() to take a std::initializer_list<sf::Vector2f>, and the syntax will work fine.
The kind of array you're using is often called a "C-style array." It exists in C, and has existed in C++ from the beginning. It has various limitations, such as what you've just run into. It looks like you really want a std::vector. There is some nuance to using std::vectors, and I don't know your level of C++ understanding, so I don't know if the phrases "usually you don't want to pass them by value" or "I would recommend you imitate STL functions and pass begin/end iterators instead" mean anything to you. You will eventually come across the parts of the language that make those statements useful. You don't need to worry about them right now.
I have a map of type std::map<std::string, std::vector<MyClass>>. The map is filled in this way that I create a vector and put it with a guid as a pair into the map. Then I want to call a function, give the just inserted vector to it and let it fill the vector. It looks like that:
{
std::string guid = "aGUID"
std::vector<MyClass> vec_myClass(0);
my_map[guid] = vec_myClass;
std::vector<MyClass>& vec_ref = my_map[guid];
FillVector(vec_ref);
}
FillVector(std::vector<MyClass>& vec) { vec.push_back(...); }
I think the [] operator returns a reference of the item in my_map, which I can give to a function to work with it, but my application crashes at this point. I am putting the vector first into the map (when it is empty) because I want to avoid copying effort as function FillVector puts lots of items into the vector. Where is my mistake? Might it be wrong to pass a reference by reference to a function? Or is there a clearly better solution to this? I prefer references over pointers here. Thx, and all the best.
All that code simplifies to:
{
std::string guid = "aGUID"
FillVector(my_map[guid]);
}
Btw. I think your problem does not appear to be here, but in code you don't show us...
std::map operator would create value for the key internally if it does not exist. see this link. passing the reference to function is ok, the problem seem to be somewhere else in your code.
In the code of my game, I want to remove some elements from a list,
which happens in a loop.The only problem I have is, when I use
list::erase I have to break after that function because I think
the list becomes "outdated". This causes a little flicker and I want to
try to remove it.
The current code is this:
for(list<Arrow*>::iterator it = arrows.begin(); it != arrows.end(); it++)
{
Arrow* a = (*it);
if(a->isActive() == true)
{
a->update();
}
else
{
arrows.erase(it);
break;
}
}
Thank you in advance!
Edit:
Sorry, I was confused with vector and list. Got the answer, thanks!
you should do:
it = arrows.erase(it);
//
list<Arrow*>::iterator it = arrows.begin();
while (it != arrows.end())
{
Arrow* a = (*it);
if(a->isActive())
{
a->update(); ++it;
}
else{ // delete (a); ???
it=arrows.erase(it);}
}
I am confused, you say vector and in your example you are using a list.
List is implemented with a double linked-list (actually it is likely to be implemented, because the standard fix just the complexity and not the details). The iterator after erasing are still valid. http://www.cplusplus.com/reference/stl/list/erase/
Erasing in the middle with vector is slow and also invalided all the iterators and references.
Like everyone said you example is confusing. If you want delete in the mid of your container and if you are using
1>vector:
Then every iterator and reference after the point of erase is invalidated.
Also vector deleting from mid of vector will causing element behind it to shift which might considered slow if you want performance.
2>list:
Then only the deleted iterator and reference is invalidated.
Either way using the erase-remove idiom is preferred when you want to delete some element in the middle of stl sequence container.
The proper way to filter out items in a standard library container is to use the Erase-Remove Idiom. Because you're using a member function as the test in the loop, you should adapt the example to use std::remove_if().
You can implement this in a short and sweet (provided you like functional programming) way:
#include <algorithm>
#include <functional>
arrows.erase(std::remove_if(
arrows.begin(), arrows.end(), std::mem_fun(&Arrow::isActive)
));
This will work for std::vector<>, std::deque<>, std::list<> etc. regardless of implementation and iterator invalidation semantics.
Edit: I see you're also using the Arrow::update() method inside the loop. You can either do a double pass on the list, use a function object to call both methods or write the loop manually.
In the last case, you can use the it = arrows.erase(it); trick, but this will only be efficient for std::list<>. The loop will have O(n^2) complexity for the
std::vector<> and std::deque<> containers.