By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
425,647 Members | 1,178 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 425,647 IT Pros & Developers. It's quick & easy.

How do you return a list? Best practices

P: n/a
Hi,
This may sound a odd question, but I wanted to know how you return a list of
data from a function. These are some of the ways I know how, and I was
wondering which method you normally use. This is more of a best practices
question rather than a technical one.

1) Return a list instance ie
std::list myFunction() {
std::list list();
return list;
}

This however does a lot of copying and isn't very efficient, but seems
conceptually the easiest.

2) Return a list pointer (or reference) ie
std::list * my Function() {
std::list *list = malloc(sizeof(std::list));
return list;
}

This only needs to copy a pointer, however this list must now be deleted. If
we passed the address of say a member variable (or global variable) then we
don't need to delete, but make sure it doesn't change before the caller is
finished using it.

3) Return a iterator ie,
std::list::iterator myFunction() {
std::list list;
return list.begin();
}

This has little copying again, but we also need to know the list.end() maybe
from another method.

4) Pass the list in
void myFunction(std::list &list) {
// Fill the list with stuff
}

Again little copying, sounds a good idea.
Each has pros and cons but this may sound like a silly question, but which
method is most "advisable" from a API point of view? For example if I was
writing a API for other developers to use, which would be the best?

Thanks very much
Andrew

P.S Would the answer to this question be any different if it was a set,
vector, or map?
Dec 12 '05 #1
Share this Question
Share on Google+
15 Replies


P: n/a
* Andrew Brampton:
Hi,
This may sound a odd question, but I wanted to know how you return a list of
data from a function. These are some of the ways I know how, and I was
wondering which method you normally use. This is more of a best practices
question rather than a technical one.

1) Return a list instance ie
std::list myFunction() {
Assuming you mean e.g. std::list<int>.

std::list list();
That's a declaration of a function 'list' returning a 'std::list'.

return list;
}

With a compiler that does NRVO optimization this (with errors mentioned above
fixed) is the best. Many compilers now do NRVO.

This however does a lot of copying and isn't very efficient
With NRVO it's actually the most efficient.

, but seems conceptually the easiest.
That also.
2) Return a list pointer (or reference) ie
std::list * my Function() {
std::list *list = malloc(sizeof(std::list));
return list;
}
Don't use malloc in C++, use 'new'.

This only needs to copy a pointer, however this list must now be deleted. If
we passed the address of say a member variable (or global variable) then we
don't need to delete, but make sure it doesn't change before the caller is
finished using it.
Use a std::auto_ptr< std::list<T> >.

3) Return a iterator ie,
std::list::iterator myFunction() {
std::list list;
return list.begin();
}
That's VERY BAD: you're returning a pointer to a local variable, and have
Undefined Behavior.
This has little copying again, but we also need to know the list.end() maybe
from another method.

4) Pass the list in
void myFunction(std::list &list) {
// Fill the list with stuff
}

Again little copying, sounds a good idea.
That's essentially what an NRVO optimization does, except the optimization
avoid the original initialization.
Each has pros and cons but this may sound like a silly question, but which
method is most "advisable" from a API point of view? For example if I was
writing a API for other developers to use, which would be the best?
The first, or the first plus the last (with the first as a wrapper for the
last, both offered to the client code).
P.S Would the answer to this question be any different if it was a set,
vector, or map?


No.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Dec 12 '05 #2

P: n/a

Andrew Brampton wrote:
Hi,
This may sound a odd question, but I wanted to know how you return a list of
data from a function. These are some of the ways I know how, and I was
wondering which method you normally use. This is more of a best practices
question rather than a technical one.


One way to do this is to use intrusive lists. Sketch:

template<class T>
struct list_node
{
list_node* next;
T value;
};

template<class T>
void delete_list(list_node<T>* node)
{
if(node)
{
delete_list(node->next);
delete node;
}
}

list_node<whatever>* create_my_list()
{
list_node<whatever>* head = new list_node<whatever>();
// attach other nodes to head->next
return head;
}

Dec 12 '05 #3

P: n/a
When I want to return a collection, I usually do it like this:

template<typename InIterator>
void GetCollection(InIterator it)
{
// return 1, 2, 3
*it++ = 1;
*it++ = 2;
* it++ = 3;
}

And call it like this:
std::list<int> myList;
GetCollection(std::back_inserter(myList));

--
Dag Henriksson

Dec 12 '05 #4

P: n/a

da************@hotmail.com wrote:
When I want to return a collection, I usually do it like this:

template<typename InIterator>
void GetCollection(InIterator it)
{
// return 1, 2, 3
*it++ = 1;
*it++ = 2;
* it++ = 3;
}


What would you do if this function were going to be exported from a
shared library?

Dec 12 '05 #5

P: n/a

Maxim Yegorushkin wrote:
da************@hotmail.com wrote:
When I want to return a collection, I usually do it like this:

template<typename InIterator>
void GetCollection(InIterator it)
{
// return 1, 2, 3
*it++ = 1;
*it++ = 2;
* it++ = 3;
}


What would you do if this function were going to be exported from a
shared library?


With shared libraries you will always have a problem as none of the STL
collections are portable across libraries, nor is std::string.

(You can use boost::shared_array instead of vector if you can be
certain that everyone is using the same implementation of boost).

Dec 12 '05 #6

P: n/a

Earl Purple wrote:
Maxim Yegorushkin wrote:
da************@hotmail.com wrote:
When I want to return a collection, I usually do it like this:

template<typename InIterator>
void GetCollection(InIterator it)
{
// return 1, 2, 3
*it++ = 1;
*it++ = 2;
* it++ = 3;
}
What would you do if this function were going to be exported from a
shared library?


With shared libraries you will always have a problem as none of the STL
collections are portable across libraries, nor is std::string.


This is often caused by the static linking of the C runtime library.
If you dynamically link to your basic runtime libraries (in ALL of the
libs being used) then everyone will use the same allocators and such
and the problem goes away. Of course this is a platform issue but
there you go...
(You can use boost::shared_array instead of vector if you can be
certain that everyone is using the same implementation of boost).


Dec 12 '05 #7

P: n/a
"Andrew Brampton" <an****@bramp.freeserve.co.uk> wrote:
std::list myFunction() {
std::list list();
return list;
}
Yuck. Wastes time and memory.

std::list * my Function() {
std::list *list = malloc(sizeof(std::list));
return list;
}
EWWW! Leaks memory like a sieve, if you're not very,
very careful.

std::list::iterator myFunction() {
std::list list;
return list.begin();
}
You cant name a list "list"! Also, passing an iterator to
a local that goes out of scope won't work.

void myFunction(std::list &list) {
// Fill the list with stuff
}


Yes. This is the way I always do it. But you still can't
name a list "list"! I don't think. Even if you can get
away with it for some reason, it's a bad idea. I'd write
that:

int MyFunction(std::list<Wombat> & Aardvark)
{
do stuff
maybe change contents of Aardvark
maybe delete or insert elements in Aardvark
maybe sort or re-order Aardvark
do more stuff
if (stuff went well) return 42;
else return 666;
}
--
Robbie Hatley
Tustin, CA, USA
lone wolf intj at pac bell dot net
home dot pac bell dot net slant earnur slant
Dec 12 '05 #8

P: n/a
Robbie Hatley wrote:
"Andrew Brampton" <an****@bramp.freeserve.co.uk> wrote:

std::list::iterator myFunction() {
std::list list;
return list.begin();
}

You cant name a list "list"! Also, passing an iterator to
a local that goes out of scope won't work.

So long as his variable isn't in the std:: namespace, why not?
I'll agree, though, that it's a bad idea.
Dec 12 '05 #9

P: n/a
Well thankyou to all that replied.

Other than my obvious mistakes you have given me some insight into the
"best" ways to do this.

Thanks again
Andrew

"Andrew Brampton" <an****@bramp.freeserve.co.uk> wrote in message
news:43***********************@ptn-nntp-reader02.plus.net...
Hi,
This may sound a odd question, but I wanted to know how you return a list
of data from a function. These are some of the ways I know how, and I was
wondering which method you normally use. This is more of a best practices
question rather than a technical one.

...

Thanks very much
Andrew


Dec 12 '05 #10

P: n/a

ro**********@gmail.com wrote:

[]
This is often caused by the static linking of the C runtime library.
If you dynamically link to your basic runtime libraries (in ALL of the
libs being used) then everyone will use the same allocators and such
and the problem goes away. Of course this is a platform issue but
there you go...


On Windoze only. There is no such problem on Linux.

Dec 13 '05 #11

P: n/a
I think the best way to written a list is for example

const list& myfunction() {

....
return list_xyz;
}

Dec 13 '05 #12

P: n/a

swesoc wrote:
I think the best way to written a list is for example

const list& myfunction() {

...
return list_xyz;
}


Yes, this is a good way to return a dangling reference.

Dec 13 '05 #13

P: n/a

Robbie Hatley wrote:
"Andrew Brampton" <an****@bramp.freeserve.co.uk> wrote:
std::list myFunction() {
std::list list();
return list;
}

Read Alf P Steinbach's post in this thread about Named Return Value
Optimisation. Quick summary:
Yuck. Wastes time and memory.


Not with most modern compilers.

Gavin Deane

Dec 14 '05 #14

P: n/a
de*********@hotmail.com wrote:
Robbie Hatley wrote:
"Andrew Brampton" <an****@bramp.freeserve.co.uk> wrote:
std::list myFunction() {
std::list list();
return list;
}

Read Alf P Steinbach's post in this thread about Named Return Value
Optimisation. Quick summary:

Yuck. Wastes time and memory.

Not with most modern compilers.

Gavin Deane


Is there a reliable way to find out if my compiler does NRVO?

I can think of:

a) Look at the compiler output. (I'd rather not.)

b) Given std::list<MyClass>, add a copy constructor to MyClass that does
a 'std::cout << "I'm being copied." << std::endl;' and watch what
happens. Might this be optimised away, even though NRVO is not happening?

c) Benchmark.

I wouldn't want to do something that is efficient on one compiler and
lousy on another. Hmm, are there circumstances where I might want NRVO
to not happen?

Thanks.

PJDM
Dec 15 '05 #15

P: n/a
On 2005-12-14, Peter Mayne <Pe*********@hp.com> wrote:
de*********@hotmail.com wrote:
Robbie Hatley wrote:
"Andrew Brampton" <an****@bramp.freeserve.co.uk> wrote:

std::list myFunction() {
std::list list();
return list;
}

Read Alf P Steinbach's post in this thread about Named Return Value
Optimisation. Quick summary:

Yuck. Wastes time and memory.

Not with most modern compilers.

Gavin Deane


Is there a reliable way to find out if my compiler does NRVO?

I can think of:

a) Look at the compiler output. (I'd rather not.)

b) Given std::list<MyClass>, add a copy constructor to MyClass
that does a 'std::cout << "I'm being copied." << std::endl;'
and watch what happens. Might this be optimised away, even
though NRVO is not happening?


This is not a good test since a compiler might realize that the
contructors have side effects and reject an optimization it would
otherwise have made.
c) Benchmark.

I wouldn't want to do something that is efficient on one
compiler and lousy on another. Hmm, are there circumstances
where I might want NRVO to not happen?


Don't write functions like these except when it is necessary, and
hope for the best. Alternatives are sometimes merely yucky, but
often actually incorrect. For example, do not try to manually
implement the NRVO when writing operator+.

--
Neil Cerutti
Dec 15 '05 #16

This discussion thread is closed

Replies have been disabled for this discussion.