1

I have the following working for the simple case of a non-multi-container in a container:

template <typename MultiContainer, typename SingleContainer>
void check (const MultiContainer& container, SingleContainer& v) {
    if (std::is_same<MultiContainer, typename SingleContainer::value_type>::value)
        return;
    if (is_container<typename MultiContainer::value_type>::value) {
        std::vector<typename MultiContainer::value_type::value_type> a;  // Using vector for this first draft.
        for (const auto& x : container)
            std::copy (x.begin(), x.end(), std::back_inserter(a));
        if (std::is_same<typename MultiContainer::value_type::value_type, typename SingleContainer::value_type>::value)
            v = a;
//      else check<decltype(a), SingleContainer>(a, v);  // Won't compile
    }
}

template <typename MultiContainer, typename SingleContainer>
SingleContainer extractFromMultiContainer (const MultiContainer& container) {
    SingleContainer v;
    check<MultiContainer, SingleContainer>(container, v);
    return v;
}

int main() {
    using Multi = std::list<std::vector<int>>;
    const Multi multi = { {1,2,3}, {4,5}, {6,7,8,9} };
//  using Multi = std::deque<std::list<std::vector<int>>>;
//  const Multi multi = {  { {1,2,3}, {4,5}, {6,7,8,9} },  { {10,11}, {12,13}, {14,15,16} }  };
    const auto v = extractFromMultiContainer<Multi, std::vector<int>>(multi);
    for (int x : v) std::cout << x << ' ';  // 1 2 3 4 5 6 7 8 9
}

But I cannot get the required recursion line in the 'check' function above working to handle the container-in-container-in-container case (must work no matter how deeply nested actually). I'm using vector as SingleContainer for this first draft (I'll try to generalize that later). Incidentally, I obtained the code of is_container from a previous thread in stackoverflow Determine if a type is an STL container at compile time, so we can just take that part for granted:

template<typename T>
struct has_const_iterator {
private:
    typedef char yes;
    typedef struct { char array[2]; } no;

    template<typename C> static yes test(typename C::const_iterator*);
    template<typename C> static no  test(...);
public:
    static const bool value = sizeof(test<T>(0)) == sizeof(yes);
    typedef T type;
};

template <typename T>
struct has_begin_end {
    template<typename C> static char (&f(typename std::enable_if<
      std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::begin)),
      typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

    template<typename C> static char (&f(...))[2];

template<typename C> static char (&g(typename std::enable_if<
      std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::end)),
      typename C::const_iterator(C::*)() const>::value, void>::type*))[1];

    template<typename C> static char (&g(...))[2];

    static bool const beg_value = sizeof(f<T>(0)) == 1;
    static bool const end_value = sizeof(g<T>(0)) == 1;
};

template<typename T> 
struct is_container : std::integral_constant<bool, has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value> {};
Community
  • 1
  • 1
prestokeys
  • 4,817
  • 3
  • 20
  • 43

1 Answers1

1

As far as I understand, you want a generic flatten function. You can do that by first defining the basic flattener

template<typename T, bool cont = is_container<typename T::value_type>::value>
struct flattener {
    template<typename In, typename Out>
    static void flatten(In first, In last, Out res);
};

and then go on with partial specialization for both the container

template<typename T> struct flattener<T, true> {
    template<typename In, typename Out>
    static void flatten(In first, In last, Out res) {
        for (; first != last; ++first)
            flattener<typename T::value_type>::flatten(std::begin(*first), std::end(*first), res);
    }
};

and non container type

template<typename T> struct flattener<T, false> {
    template<typename In, typename Out>
    static void flatten(In first, In last, Out res) {
        std::copy(first, last, res);
    }
};

You can now use this like

std::vector<int> v1 = { 1, 2, 3 };
std::vector<int> res1;
flattener<decltype(v1)>::flatten(v1.begin(), v1.end(), std::back_inserter(res1));

std::vector<std::vector<int> > v2 = { {9, 8, 7}, {6, 5, 4}, {1, 2, 3} };
std::vector<int> res2;
flattener<decltype(v2)>::flatten(v2.begin(), v2.end(), std::back_inserter(res2));

std::vector<std::vector<std::vector<int> > > v3 = { { {9, 8}, {7, 6} },
                                                    { {6, 5}, {4, 3} },
                                                    { {1, 2}, {3, 4} } };
std::vector<int> res3;
flattener<decltype(v3)>::flatten(v3.begin(), v3.end(), std::back_inserter(res3));
Olaf Dietsche
  • 72,253
  • 8
  • 102
  • 198
  • Well done! The forward declaration of the `static void flatten` function was not necessary, but I can see you wanted it mentioned anyway. – prestokeys Nov 03 '14 at 23:03
  • You must mean that you are still learning the C++ language itself (among the other languages you know). Because the logic behind your solution, which is probably language-independent, is really high-order thinking that I could never have thought of. – prestokeys Nov 04 '14 at 15:51
  • Even though I use C++ for many years, I still learn new things. – Olaf Dietsche Nov 04 '14 at 20:46