tags:

views:

218

answers:

6

What's the most elegant way to return a std::list object from a shared lib function (implemented by C++ code) to a C consumer? I know for std::vector, we can return the address of the 1st element of the vector and have the consumer treat it as an array, but std::list is implemented as a linked lis.

+2  A: 

If you want the client code to manipulate a list, you will have to define a C type like:

struct Node
{
  int foo;
  float bar;
  // more data

  struct Node* next;
};

and return a pointer to the head of the list:

struct Node* getData();

Which basically means that you will have to copy the content of your std::list to a C like list data structure if you want the client code to manipulate a list.

Otherwise, you can copy the content of your std::list into a contiguous memory block, return this block to the caller but in this case it's the caller responsibility to release the memory. Returning an array also means the memory clean up has to be done with a function compatible with the function you used to allocate the block: your implementation will likely use malloc and not new to allocate this block, so that the caller can later use free on the block.

Gregory Pakosz
+3  A: 

you always can make a copy:

list<int> lst;
int *arr = new int[lst.size()];
copy(lst.begin(),lst.end(),arr);
catwalk
beware, if the shared library C++ implementation returns this block allocated with `new`, then you have risks the caller disposes of it using `free` which leads to undefined behavior
Gregory Pakosz
+7  A: 

Copy the std::list to a std::vector and return the address of the first element, as you already mentioned.

(Of course, this may mean that you don't want to be using a std::list in the first place.)

(This solution assumes that the object being accessed is owned by the C++ library -- If this isn't the case, you may need to consider allocating the memory from your C code and passing a pointer into the C++ library to copy the data.)

A: 

You can get an iterator or the list and fill a vector with it:

std::list<Node> nodes;
// .. fill nodes
std::vector<node> nodeVector;
std::copy(nodes.begin(); nodes.end(); std::back_inserter(nodeVector));

And then you can work with it just like with the vector that you are familiar with.

Fabian Wickborn
That is a very inefficient way to copy the list to the vector. Try this:`std::vector<node> nodeVector(nodes.begin(), nodes.end());`
Yes, you are right, your solution is more efficient. I just found the std::copy to be more readable if one is not familiar with how easy items can be shared between STL containers.
Fabian Wickborn
Ok, but I don't think that passing a range to a container constructor presents any readability issues...
+1  A: 

Try something like this, no need for a std::copy if you don't want to use it:

std::list<int> aList;
alist.push_back(1);
alist.push_back(2);
alist.push_back(3);

std::vector<int> aVector(alist.begin(), alist.end());

cFunction(&aVector[0], aVector.size());
Dmitry
+2  A: 

The only way to do it is to return a void* to the obejct. Then provide a set of functions that accept the void* and manipulate the list in the C++ code.

Edit:

For those that go Ehhh.

std::list<int>  plop;
void* getPlopList()
{
    retutn &plop;
}

void appendToCPPList(void* list,int val)
{
    static_cast<std::list<int>*>(list)->push_back(val);
} 
// Dont forget to declare the functions extern "C"
Martin York
Um......... huh?
+1, although I'd prefer an opaque pointer over void*
quinmars
This is in no way what was asked for. You are assuming that the C code is providing the list values to the C++ code?
@STingRaySC: Martin isn't assuming that the C code provides the values; he suggests writing a C interface for `std::list`
Christoph
@STingRaySC, The OP didn't tell us what he wants to do with the list in the C code. If he wants to only read it or manipulate it. That's only one possible example. You can wrap the whole list<int> API, including iterators, if you need to.
quinmars
I see. I guess I made the following assumption from reading the OP's post: The `std::list` is an implementation detail internal to the C++ library, hence wrapping the entire list API is neither elegant nor a wise design choice.
@StringRay: As you say the std::list is an implementation detail of C++. You can build list interfaces in C that do exactly the same thing. So your choice: a) Write the entire list API in C and implement it yourself. b) Write the entire list API in C that just wraps the C++ -API. No it is not elegant (its an OK design proven again and again that it works). The best design choice would be to move the code to all C++.
Martin York