tags:

views:

122

answers:

4

I'd like to do the following:

const int someInt;
const std::vector<int> someIntList;
const std::vector<std::vector<int>> someNestedIntList;

Marshall(someInt); // trivial case
Marshall(someIntList); // difficult case
Marshall(someNestedIntList); // difficult case

I tried the following:

template<std::vector<class Element>> 
void Marshall(const std::vector<Element>& toBeMarshalled)
{
    for (int i=0; i<toBeMarshalled.size(); ++i)
        Marshall<Element>(toBeMarshalled[i]);
}

Regrettably, this doesn't compile, and I failed to find the right syntax for it.

Note that there has to be only a single template parameter, otherwise the marshalling of a nested list won't work.

Update: Thanks to FredOverflow's answer, I found what I was looking for. I forgot that all container classes in the standard library have a value_type typedef. This can be used as a workaround for my problem:

template <class Container> 
void Marshall(const Container& toBeMarshalled)
{
    for (UINT32 i=0; i<toBeMarshalled.size(); ++i)
        Marshall<Container::value_type>(toBeMarshalled);
}

It is a bit of a patch, but I think it is good enough.

+2  A: 

The code you've pasted contains >> at the end of your template declaration. C++ compilers will interpret that not as two closing angle brackets, but as a single right-shift operator.

Try template<std::vector<class Element> >, with a space between the brackets.

Chris
While this is certainly right, this still won't compile.
sbi
+4  A: 

The template declaration is wrong. Do:

template< class Element >
void marshall( std::vector< Element > const& v )

Cheers & hth.,

Alf P. Steinbach
However, this fails if there every comes along an instance of `std::vector` that uses a different allocator.
sbi
Dammit, I'll never every learn to type "ever". `<wry_smile>`
sbi
@sbi: right, thanks! I didn't even think about custom allocators. Otoh., I wouldn't recommend wasting time on adding that kind of support except in library code like Boost where you have to support all kinds of client code. :-) Cheers,
Alf P. Steinbach
@Alf: Well, in an in-house lib I have seen a lot of code fail when someone suddenly needed a custom allocator. It wasn't my code, but I still spent a lot of time with the guy who needed that support fixing all the problems that arise. And that something fails to compile is the easiest of the problems you can run into due to such oversights. Much worse is when something compiles, but silently calls the wrong overloaded function template. This can make for many hours of debugging fun.
sbi
+7  A: 

There are two things wrong with your template:

  1. The template declaration is wrong. You only list the template arguments here, not the function argument types. Also, >> is parsed as shift operator.
  2. std::vector has two template parameters. Although in your daily work you will rarely use the second, it's still there and should be listed, or your template will fail if anyone ever attempts to use it with a std::vector that doesn't use the default allocator.

This should work for all std::vector instances:

template< typename T > 
void Marshall(const T& toBeMarshalled)
{
  // ...
}

template< typename T, class A > 
void Marshall(const std::vector<T,A>& toBeMarshalled)
{
    for (typename std::vector<T,A>::size_type i=0; i<toBeMarshalled.size(); ++i)
        Marshall(toBeMarshalled[i]);
}
sbi
You'll need `typename` before the `size_type` (although it's better to use an iterator). The base `Marshall` function doesn't necessarily need to be templated (and if it's only going to work on particular types probably shouldn't be).
Adam Bowen
@Adam: Thanks, fixed now!
sbi
+3  A: 

May I propose SFINAE and some boost metaprogramming?

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>

template <class T>
struct has_begin_end
{
    template <class U,
              typename U::const_iterator (U::*)() const,
              typename U::const_iterator (U::*)() const>
    struct sfinae { };

    template <class U>
    static char test(sfinae<U, &U::begin, &U::end>*);

    template <class>
    static long test(...);

    enum { value = (1 == sizeof test<T>(0)) };
    typedef boost::integral_constant<bool, value> type;
};

template <class Value>
typename boost::disable_if<has_begin_end<Value>, void>::type
Marshall(const Value& value)
{
    std::cout << value << ' ';
}

template <class Container>
typename boost::enable_if<has_begin_end<Container>, void>::type
Marshall(const Container& c)
{
    std::for_each(c.begin(), c.end(), Marshall<typename Container::value_type>);
}

int main()
{
    const int someInt = 42;
    const std::vector<int> someIntList {2, 3, 5, 7};
    const std::vector<std::vector<int>> someNestedIntList {{11, 13}, {17, 19}};

    Marshall(someInt);
    Marshall(someIntList);
    Marshall(someNestedIntList);
}
FredOverflow
Wow. This would work with any range-like type that has `begin()` and `end()`. Great!
sbi