Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the value type from an output iterator?

Let's say that I have a C container (e.g., MyContainer) with contained objects stored as void* pointers. The only way to iterate through the elements of this container is via two interface functions:

  1. getFirstElem(MyContainer const&, void*): Outputs the first element of the container.
  2. getNextElem(MyContainer const&, void*): Outputs the next element of the container.

I want to code a generic function that iterates through the elements of this C container via the interface functions mentioned above and copy their values into a C++ container (e.g. std::vector).

What I've done so far:

template<typename OutputIterator>
void
copy_container(MyContainer const &cont, OutputIterator first) {
  typename std::iterator_traits<OutputIterator>::value_type elem;
  if(getFirstElem(cont, &elem)) {
    do {
      *first = elem;
      ++first;
    } while(getNextElem(cont, &elem))    
  }
}

The above example works OK with normal iterators. However, it fails to compile with output iterators (e.g., copy_container(cont, std::back_inserter(myvector));).

The reason is that std::iterator_traits::value_type results in void in cases where the argument type is an output iterator.

Is there a way to make this generic function work for output iterators as well?

I know that in C++11 it could be done by using decltype (e.g., decltype(*first)), but I'm particularly interested in pre-C++11 solutions since I use an old C++ compiler (gcc v4.4.7).

like image 791
101010 Avatar asked Aug 04 '14 12:08

101010


2 Answers

You may use typetraits and specialization

template <typename IT>
struct it_value_type
{
    typedef typename std::iterator_traits<IT>::value_type elem;
};

template <typename Container>
struct it_value_type<std::back_insert_iterator<Container>>
{
    typedef typename Container::value_type elem;
};

template <typename Container>
struct it_value_type<std::front_insert_iterator<Container>>
{
    typedef typename Container::value_type elem;
};

And then you code becomes:

template<typename OutputIterator>
void
copy_container(MyContainer const &cont, OutputIterator first) {
    typename it_value_type<OutputIterator>::elem elem;
    if (getFirstElem(cont, &elem)) {
        do {
            *first = elem;
            ++first;
        } while (getNextElem(cont, &elem));
    }
}
like image 161
Jarod42 Avatar answered Oct 16 '22 01:10

Jarod42


As correctly observed, the value_type of an output iterator is void. So there not much to do apart from replacing this :

typename std::iterator_traits<OutputIterator>::value_type elem;

with this

decltype(*first) elem;

(even though the Standard doesn't guarantee it'll work - a proxy might be returned by dereferencing an output iterator).

As you said no C++11 solution so a redesign might be needed. Here are some options:

1. Pass the container

Instead of an iterator to the first element, you could pass a reference to the container. It seems like all you want is a push_back.

template<template<typename,typename> class stlContainer>
void copy_container(
    MyMontainer const &cont, OutputIterator first) 
{ 
    // insertion in stlContainer

then all you need is a layer of traits to dispatch to the right implementation of insertion per container

2. Pass an extra template parameter

The value type could be an extra template parameter.

template<typename value_type, typename OutputIterator>
void copy_container(MyMontainer const &cont, OutputIterator first) 
{
    value_type elem;
...
like image 9
Nikos Athanasiou Avatar answered Oct 16 '22 02:10

Nikos Athanasiou