0

I am using VS2008 compatible code with Boost 1.60 (no C++ 11). I have a third party library that returns a list-like interface. I want to take the elements of the list and put them in an std::vector. The track_list class has a next method which returns a pointer to a track pointer track**.

I was thinking I could use std::generate to fill a vector the same size as the track_list. I was able to come up with boost::bind(&track_list::next, tracks) which gets me the track** but I am not sure how to add the dereferencing part to get the track* to go into the vector<track*>.

Furthermore, there's actually a specific_track* that I know is safe to cast to from track*. So what I am really looking for is something along the lines of (specific_track*)(*boost::bind(&track_list::next, tracks)) but I am not sure how to construct that syntactically. Am I on the right track? I looked at boost lambdas but the generator function takes no arguments.

Edit: maybe a simpler example might help with clarifying exactly what I'm trying to do.

int** get_ptr_num() { int* i = new int(5); return new int*(i); }
int* get_num() { return new int(5); }

int main() {
  std::vector<int*> nums(10);
  std::generate(nums.begin(), nums.end(), get_num) // compiles      
  std::generate(nums.begin(), nums.end(), get_ptr_num) // cannot convert from int** to int*
}

Basically I just want to wrap get_ptr_num in some kind of bind or lambda to do the dereference without creating a separate function to do so.

The second part, the cast, would be something like:

int main() {
  std::vector<double*> nums(10);    
  std::generate(nums.begin(), nums.end(), get_ptr_num) // cannot convert from int** to double*
}

This seems like it would be trivial to do with C++ 11 lambdas, but I can't use C++ 11.

The last part about this being a member function would be something more like this:

class IntGenerator {
public:
  int** get_ptr_num() { int* i = new int(5); return new int*(i); }
}

int main() {
  IntGenerator int_gen;
  std::vector<int*> nums(10);
  std::generate(nums.begin(), nums.end(), boost::bind(&IntGenerator::get_ptr_num, int_gen)) // cannot convert from int** to int*

}

Paul
  • 189
  • 1
  • 6
  • 4
    Is your goal to create the vector, or is it to explore techniques that aggressively use the standard library? Copying the contents of a list into a vector is easy: just call `push_back` with each element. – Pete Becker Jun 09 '17 at 05:33
  • It's really not clear to me what you're trying to do. What code do you have? What does it do that you don't want. If you're looking for a code review, those are off topic on stack overflow. Also, asking a bunch of semi-related questions in the same question isn't good form. On top of that, It's not even clear to me how the question relates to the title. – xaxxon Jun 09 '17 at 05:38
  • Sorry, I thought the question was clear. How can I write the generator function for std::generate in place using Boost bind/lambdas to take a `track**` and turn it into `specific_track*`? – Paul Jun 09 '17 at 11:49
  • This is not a code review, and the second part is building on the first part. I am not sure why you don't see how the question relates to the title. The function `next` is a bound member function of `track_list` but the return value is not what I need; I need to dereference and cast it, and I am looking for some kind of lambda to do that. – Paul Jun 09 '17 at 11:58
  • Dereferencing and casting iterators: http://www.boost.org/doc/libs/1_64_0/libs/range/doc/html/range/reference/adaptors/reference/indirected.html and http://www.boost.org/doc/libs/1_64_0/libs/range/doc/html/range/reference/adaptors/reference/transformed.html (or the underlying adaptors from [Boost Iterator](http://www.boost.org/doc/libs/1_64_0/libs/iterator/doc/index.html#specialized-adaptors)) – sehe Jun 09 '17 at 12:17

1 Answers1

0

You could use a function input iterator:

Live On Coliru

#include <boost/iterator/function_input_iterator.hpp>
#include <vector>
#include <functional>
#include <iostream>
#include <array>

namespace TheAPI {

    struct track_list {
        std::array<char const*, 6> titles {{ "one", "two", "three", "four", "five", "six" }};
        size_t length() const { return titles.size(); }

        struct Iterator {
            char const* const* raw;
            char const* next() { return *raw++; }
        };

        Iterator get_iterator() const { return {titles.begin()}; };
    };

}

int main() {
    TheAPI::track_list tl;
    auto iter = tl.get_iterator();

    auto gen = std::bind(&TheAPI::track_list::Iterator::next, std::ref(iter));

    auto first = boost::make_function_input_iterator(gen, 0),
         last  = boost::make_function_input_iterator(gen, 6);

    std::vector<std::string> titles(first, last);

    std::copy(titles.begin(), titles.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}

Of course, if you were thinking of generate, it could be simpler with generate_n:

Live On Coliru

#include <functional>
#include <iterator>
#include <algorithm>
#include <iostream>
#include <array>

namespace TheAPI {

    struct track_list {
        std::array<char const*, 6> titles {{ "one", "two", "three", "four", "five", "six" }};
        size_t length() const { return titles.size(); }

        struct Iterator {
            char const* const* raw;
            char const* next() { return *raw++; }
        };

        Iterator get_iterator() const { return {titles.begin()}; };
    };

}

int main() {
    TheAPI::track_list tl;
    auto iter = tl.get_iterator();

    auto gen = std::bind(&TheAPI::track_list::Iterator::next, std::ref(iter));
    std::generate_n(std::ostream_iterator<std::string>{std::cout, "\n"}, tl.length(), gen);
}

Fill a vector using

std::vector<std::string> my_vector;
std::generate_n(back_inserter(my_vector), tl.length(), gen);

Both programs print

one
two
three
four
five
six
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks, this is helpful but I would really like to do it without building a separate functor. I have edited my original post with a much simpler example of what I'm trying to do. – Paul Jun 09 '17 at 15:16
  • In reply to the simplified question: http://coliru.stacked-crooked.com/a/ff45e245a77cbb11. Beware of the [memory leak operator](https://stackoverflow.com/questions/8839943/why-does-the-use-of-new-cause-memory-leaks). I'd say you need fewer pointers and more smart ownership: http://coliru.stacked-crooked.com/a/51b3e4edc6b80af8 (use `unique_ptr<>` to transfer ownership and `optional<>` to express optionality) – sehe Jun 09 '17 at 16:06
  • Unfortunately, I cannot use C++ 11, so I can't use a C++ 11 lambda; I was looking for a way to do this with Boost lambda but can't figure it out for a function that takes no arguments. – Paul Jun 09 '17 at 16:08
  • For the actual code, the pointer ownership is more clear; this is just a toy example. – Paul Jun 09 '17 at 16:09
  • With boost lambda: http://coliru.stacked-crooked.com/a/2999c14935ecc552 and again without leaking: http://coliru.stacked-crooked.com/a/ba8493c68b61e234 – sehe Jun 09 '17 at 16:19
  • I recommend Boost Phoenix which is strictly more modern and capable: http://coliru.stacked-crooked.com/a/ea6d891dd7efdd53 (again, the leaks are there). It takes a bit longer to compile though, but will take advantage of C++11 if you upgrade – sehe Jun 09 '17 at 16:23
  • Unfortunately, I am at Boost 1.60, so no Phoenix either. Thanks for the example. – Paul Jun 09 '17 at 16:29
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/146282/discussion-between-sehe-and-paul). – sehe Jun 09 '17 at 16:32