-1

An external API I'm using requires C-style array of objects:

// Some api function
void doStuff(const Foo* objects, size_t length);

Actually, the API uses int for length but that just makes it even worse. When creating the array of objects, I don't know how many will I have, because some turn out wrong:

void ObjManager::sendObjectsToApi(const std::list<const std::string>& names)
{
    // Create the most suitable type of connection
    std::????<Foo> objects;
    // Loop names, try to create object for every one of them
    for( auto i=names.begin(), l=names.end(); i<l; i++ ) {
        Foo obj = createFooWithName(*i);
        if( obj.is_valid() ) {
            objects.addToCollection( obj );
        }
    }
    // Convert collection to C style array
    size_t length = objects.size();
    Foo* c_objects = objects.toC_StyleArray();
    API::doStuff(c_objects, length);
}
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • Is there something wrong with `std::vector::data()`? – n. m. could be an AI Dec 01 '15 at 17:11
  • std::vector objects; – UmNyobe Dec 01 '15 at 17:11
  • Use a std::vector, that can also be accessed like an array – MrTux Dec 01 '15 at 17:11
  • 1
    so you want a contiguous collection of objects with a size to be determined at runtime. What's wrong with vector? – jaggedSpire Dec 01 '15 at 17:11
  • `std::vector` guarantees a contiguous buffer. – Cheers and hth. - Alf Dec 01 '15 at 17:11
  • I didn't exclude `vector`. But there's also `std::list` and others - I didn't know which one is better. – Tomáš Zato Dec 01 '15 at 17:12
  • 2
    @TomášZato list isn't contiguous – jaggedSpire Dec 01 '15 at 17:12
  • `std::list` has no advantage for anything I know of, except that pointers to nodes can remain valid even if you delete or insert. – Cheers and hth. - Alf Dec 01 '15 at 17:12
  • @jaggedSpire Maybe the thing here is you somewhat expect me to know that even though I obviously don't, which is why I asked a question in the first place. – Tomáš Zato Dec 01 '15 at 17:13
  • @Cheersandhth.-Alf - well, that is a distinct advntage is several scenarios :) – SergeyA Dec 01 '15 at 17:13
  • @TomášZato Ah, well. For future reference, there's a [wiki page](http://en.cppreference.com/w/cpp/container) which holds links to documentation for every one of the standard library structures. Each of these structures fulfills different requirements, and once you know which structures meet which requirements, it becomes easier to choose the appropriate one: `vector` and `array` are contiguous, but `array` has a size determined at compile time, encoded in the type system. For runtime size determination, you need `vector`. – jaggedSpire Dec 01 '15 at 17:20
  • @Cheersandhth.-Alf: and even then, `deque` has the same property for insertions and deletions at the ends, which covers quite a few cases where you want the property. `list` has 2-arg splice, which always looks like it might be useful someday, but I've never used it myself. – Steve Jessop Dec 01 '15 at 17:25
  • @SteveJessop, who told you this nonsense? _deque has the same property for insertions at the ends_ is plain wrong. – SergeyA Dec 01 '15 at 17:28
  • @SergeyA: the standard used to say, "An insert at either end of the deque invalidates all the iterators to the deque, but has no effect on the validity of references to elements of the deque". Have they changed it? – Steve Jessop Dec 01 '15 at 17:31
  • @SteveJessop, yeah, it is still valid. I was talking about iterators, but was off-base, since Alf mentioned pointers. I would retract my comment if I could. – SergeyA Dec 01 '15 at 17:38

1 Answers1

3

If doStuff requires an array then I would use a std::vector and then use data() to get the array from the vector.

std::vector<Foo> temp(names.begin(), names.end());
doStuff(temp.data(), temp.size());

std::vector guarantees that the data will be stored contiguously.

The above is if you want to copy from a std::list directly to a std::vector. I your case since you are looping through the contents of the list and creating new objects then you would have

void ObjManager::sendObjectsToApi(const std::list<const std::string>& names)
{
    // Create the most suitable type of connection
    std::vector<Foo> objects;
    objects.reserve(names.size()); // allocate space so we only allocate once
    // Loop names, try to create object for every one of them
    for( auto i=names.begin(), l=names.end(); i<l; i++ ) {
        Foo obj = createFooWithName(*i);
        if( obj.is_valid() ) {
            objects.push_back( obj );
        }
    }
    // Convert collection to C style array
    API::doStuff(names.empty()? nullptr : objects.data(), objects.size());
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • It might do OP good to `resize()` the vector before insertion. Since you didn't follow original example (where something is needed to be done with the string) you code can not illustrate this. – SergeyA Dec 01 '15 at 17:16
  • @SergeyA I didn't notice that. I have updated the answer. – NathanOliver Dec 01 '15 at 17:21
  • 1
    alternately, `reserve` only ensures that there's enough space, without constructing objects. – jaggedSpire Dec 01 '15 at 17:22
  • @jaggedSpire, that is EXACTLY what I meant to type. I am terrible sorry, resize() is simply incorrect here. – SergeyA Dec 01 '15 at 17:23
  • @SergeyA I already changed it. I get them confused from time to time as well. – NathanOliver Dec 01 '15 at 17:25
  • @jaggedSpire : reserve does not ensure anything, it just hints. An implementation of reserve which would do nothing is valid – galinette Dec 01 '15 at 17:28
  • @galinette Reserve guarantees that the capacity of the vector will grow if the supplied size is greater than the capacity. From [vector.capacity] *Effects: A directive that informs a vector of a planned change in size, so that it can manage the storage allocation accordingly. After reserve(), capacity() is greater or equal to the argument of reserve if reallocation happens; and equal to the previous value of capacity() otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument of reserve().[...]* – NathanOliver Dec 01 '15 at 17:32
  • @galinette and in addition to NathanOliver's quote, for additional clarity from the standard there's this under *Remarks*: "No reallocation shall take place during insertions that happen after a call to reserve() until the time when an insertion would make the size of the vector greater than the value of capacity()." (n4140) – jaggedSpire Dec 01 '15 at 17:39