I did a filter_iterator for C++ STL containers which is able to filter container elements based on a predicate, wich I have showed to my friend Thiago Adams. Then he pointed me a CUJ (C/C++ Users Journal) article where Maciej Sobczak introduced the Container View Concept. The main idea behind it is that it is able to move elements without really copying them and being able to integrate multiple container elements in just one view.

The View contains a collection of pointers to other elements. So the basic interface of the view would look like:

const std::string names1[] = { "felipe", "thiago" };
const std::string names2[] = { "joao", "andre", "bruno" };

view<const std::string> names_view(&names1[0], names1 + 2);
names_view.assign(&names2[0], names2 + 3);

std::sort(names_view.begin(), names_view.end());
std::copy(names_view.begin(), names_view.end(), std::ostream_iterator<const std::string>(std::cout, "\n"));

So, the idea is that the view<> object will be sorted (his pointers will point to the elements in the arrays in a sorted way) while the arrays itself will remain untouched.

In Maciej’s post it’s view was returning the pointer to the elements directly, so when he called std::sort he had to use an adapter in the predicate function to dereference the pointer to the elements. Actually, what we really want in this case is to have a container to store references to objects, but everyone knows that’s impossible… right?

Not really, TR1 has arrived with a class called reference_wrapper<T> wich basically defines a copyable reference, so:


  int data[] = { 51,9,0,5,4 };
  std::vector<std::tr1::reference_wrapper<int> > view;

  for (int i = 0; i < 5; ++i)
    view.push_back(std::tr1::ref(data[i]));

  std::sort(view.begin(), view.end());
  std::copy(view.begin(), view.end(), std::ostream_iterator<int>(std::cout, "-"));

Ok, that’s a pretty nice line reduction compared to the article :-) , and now we don’t need the special predicate in sort for it to work. But problems might still arise:

1) It is annoying to have to use a loop to push_back elements in our view.

2) The reference_wrapper<T> doesn’t define an operator= for us to be able to assign a new value to the reference. This can create problems with for example the std::replace algorithm defined in <algorithm>.

With these requirements we can create a class that better adresses our needs:


template<class T>
class view_reference
{
  T* value_;
public:
  view_reference(T& value)
    : value_(&value)
  {
  }

  operator T&()
  {
    return *value_;
  }

  view_reference<T> operator=(const T& value)
  {
    *value_ = value;
    return *this;
  }
};

void main()
{
  int data[] = { 51,9,0,5,4 };
  std::vector<view_reference<int> > view;

  std::copy(&data[0], data + 5, std::back_inserter(view));
  std::sort(view.begin(), view.end());
  std::replace(view.begin(), view.end(), 51, 3000);

  std::copy(view.begin(), view.end(), std::ostream_iterator<int>(std::cout, "-"));
}

So now we got a nice view container, hope you enjoy it :-) .

Post a Comment

*
*