views:

265

answers:

3

hello

Is there, perhaps in boost, consistent element access semantics which works across containers? something along the lines of:

element_of(std_pair).get<1>();
element_of(boost_tuple).get<0>();
element_of(pod_array).get<2>();

in principle i can write myself, but I would rather not reinvent the wheel.thanks

+1  A: 

I'm not aware of any generic accessors that would work across all known definitions of containers in C++. However, Boost.Range can be used as such to some extent.

For better flexibility, you will likely need to implement it yourself. Perhaps scratching something along this:

struct container_accessor { ... }
template <typename Container>
container_accessor make_accessor(Container& c) { ... }

template <typename Container>
container_const_accessor make_accessor(Container const& c) { ... }

where and then specialise container_accessor for all containers you need.

mloskot
i think tuple,pair, etc. might be problematic using this approach, since they may contain different types. but i suppose they can be specialized in wrapper.
aaa
@unknown yes, it may be tricky, you are right. The problem is how to specify common accessors for containers of different nature, usable with both, homogeneous and heterogeneous containers.
mloskot
+1  A: 

Containers have different ways of accessing them because they are inherently different. The closest that you get in the STL are iterators. All of the standard containers have iterators, so you can iterate over them and use the same algorithms on them using those iterators. However, what each iterator contains differs depending on the container (must just have the element, but maps have pairs). And if you're looking at pair as a container, it's not going to fit in with the rest because it doesn't have iterators.

In most cases, using iterators solves the problem. However, it obviously doesn't completely solve the problem, and the STL does not have a solution for it. Boost may, but I'm unaware of one.

The main point, however, is that containers are inherently different and to a great extent are not meant to be interchangeable. By using standard iterators, most containers can be swapped for one another fairly easily. But it doesn't generally make sense to swap one container for another without changing some of the code around it because they act so differently. I believe that Scott Meyers makes a point of this in his book "Effective STL."

If you're really trying to make the various containers interchangeable, I'd suggest rethinking that and looking more closely at what you're doing. Odds are that that's not the best idea. Now, it may very well be a good idea for your particular application - I certainly can't say without knowing anything about it, and you'd be the best judge of that - but in the general case, making containers truly interchangeable is a bad idea. Iterators make it possible to reuse many algorithms on them, but even there, the type of algorithms that you can use on a particular container varies depending on the type of iterators that that container uses (random access, bi-directional, etc.).

So, no, I'm not aware of a pre-existing solution for accessing container elements other than iterators, but generally speaking, I'd advise against attempting it. Containers are not truly interchangeable and are not meant to be.

Jonathan M Davis
"In most cases, using iterators solves the problem" - Boost.Range is an abstraction which provides another level of genericity in accessing containers using iterators.
mloskot
well, I am prototyping library for matrix manipulations and sometimes been able to use different containers for indices, range, etc. makes things a bit easier from user side
aaa
But pairs and tuples are not containers. Allowing the user to specify different containers is fine, and iterators (or Boost.Range) allow you to abstract that away.
jalf
+1  A: 

I'm not aware of such a thing.

You could most probably just implement a free get function for the types you're interested in. Boost.Tuple already has it. std::pair has it in C++0x. And the rest shouldn't be too complicated.

E.g

#include <iostream>
#include <utility>
#include <vector>
#include <boost/tuple/tuple.hpp>

namespace getter
{
    template <size_t Index, class Container>
    typename Container::reference get(Container& c)
    {
        return c[Index];
    }

    template <size_t Index, class Container>
    typename Container::const_reference get(const Container& c)
    {
        return c[Index];
    }

    template <size_t Index, class T>
    T& get(T *arr)
    {
        return arr[Index];
    }

    namespace detail {
        template <size_t Index, class T, class U>
        struct PairTypeByIndex;

        template <class T, class U>
        struct PairTypeByIndex<0u, T, U>
        {
            typedef T type;
            type& operator()(std::pair<T, U>& p) const { return p.first; }
            const type& operator()(const std::pair<T, U>& p) const { return p.first; }
        };

        template <class T, class U>
        struct PairTypeByIndex<1u, T, U>
        {
            typedef U type;
            type& operator()(std::pair<T, U>& p) const { return p.second; }
            const type& operator()(const std::pair<T, U>& p) const { return p.second; }
        };
    }

    template <size_t Index, class T, class U>
    typename detail::PairTypeByIndex<Index, T, U>::type& get(std::pair<T, U>& p)
    {
        return detail::PairTypeByIndex<Index, T, U>()(p);
    }

    template <size_t Index, class T, class U>
    const typename detail::PairTypeByIndex<Index, T, U>::type& get(const std::pair<T, U>& p)
    {
        return detail::PairTypeByIndex<Index, T, U>()(p);
    }

    using boost::get;
}

int main()
{
    boost::tuple<int, int> tuple(2, 3);
    std::cout << getter::get<0>(tuple) << '\n';
    std::vector<int> vec(10, 1); vec[2] = 100;
    std::cout << getter::get<2>(vec) << '\n';
    const int arr[] = {1, 2, 3, 4, 5};
    std::cout << getter::get<4>(arr) << '\n';
    std::pair<int, float> pair(41, 3.14);
    ++getter::get<0>(pair);
    const std::pair<int, float> pair_ref = pair;
    std::cout << getter::get<0>(pair_ref) << ' ' << getter::get<1>(pair_ref) << '\n';
}
UncleBens