Skip navigation

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&#91;i&#93;));

  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 :-).

Advertisements

One Comment

  1. You also need operator < in your example or it won't compile.

    bool operator < (view_reference const& other) const
    {
    return *value_ < *other.value_;
    }


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: