The Quantum Physicist
The Quantum Physicist

Reputation: 26346

template template class, call a function if it exists

I have this simple function with a template template parameter. It's meant to take an STL container, convert the smart ptr to a normal ptr (it's a C++03 project, but I'm also interested in the answer for C++11):

template <template <typename _T, typename = std::allocator<_T> > class Container>
static Container<T*> GetRawPtrContainer(const Container<SmartPtr<T> >& input_container)
{
    Container<T*> container;
    for(typename Container<SmartPtr<T> >::const_iterator it = input_container.begin();
        it != input_container.end();
        it++)
    {
        container.push_back(it->ptr);
    }
    return container;
}

This is a static member function of the class SmartPtr<T>.

You see here, all this does is push_back all the elements from input_container to another one and return.

You may have noticed that if the input is std::vector, then there's a performance issue with O(1) insertions, while this is fine for std::list and std::deque. So what I'd like to do is call this before the loop if it's possible (decided at compile-time):

container.reserve(input_container.size());

How can I do that?

Upvotes: 4

Views: 2508

Answers (4)

xroche
xroche

Reputation: 259

In addition to ACB's solution, note that you can also dispatch (C++17) using constexpr and traits, allowing to have complex constexpr conditions:

template <typename T, typename = int>
struct HasReserve : std::false_type {
};

template <typename T>
struct HasReserve<T, decltype(&T::reserve, 0)> : std::true_type {
};

template<typename T>
inline constexpr void reserve(T& container, size_t s)
{
    if constexpr (HasReserve<T>::value)
        container.reserve(s);
}

Upvotes: 4

ACB
ACB

Reputation: 1637

Check if class has a reserve function:

C++03:

template<typename T> struct HasReserve {
    struct Fallback { void reserve(size_t); };
    struct Derived : T, Fallback { };

    template<typename C, C> struct Check;

    template<typename C> static char(&f(Check<void (Fallback::*)(size_t), &C::reserve>*))[1];
    template<typename C> static char(&f(...))[2];

    static bool const value = sizeof(f<Derived>(0)) == 2;
};

C++11:

template <typename T, typename = int>
struct HasReserve : std::false_type { };

template <typename T>
struct HasReserve <T, decltype(&T::reserve, 0)> : std::true_type { };

Function that calls reserve if possible:

template<typename T>
typename std::enable_if<HasReserve<T>::value>::type
    Reserve(T& container, size_t s)
{
    container.reserve(s);
}

template<typename T>
typename std::enable_if<!HasReserve<T>::value>::type
Reserve(T&, size_t)
{
}

Just call the Reserve function before your loop and it should work like you want it to.

template <template <typename _T, typename = std::allocator<_T> > class Container>
static Container<T*> GetRawPtrContainer(const Container<SmartPtr<T> >& input_container)
{
    Container<T*> container;
    Reserve(container, input_container.size()); // just add this to your function
    for(typename Container<SmartPtr<T> >::const_iterator it = input_container.begin();
        it != input_container.end();
        it++)
    {
        container.push_back(it->ptr);
    }
    return container;
}

std::enable_if for C++03

template<bool B, class T = void>
struct enable_if {};

template<class T>
struct enable_if<true, T> { typedef T type; };

Upvotes: 5

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42929

Here's a C++11 implementation that's container agnostic and reserves buffer in case the input container has a .reserve member function.

template<typename Container, typename T>
auto 
insert(Container& container, T &&v, int) ->
decltype(container.push_back(std::forward<T>(v)), void()) {
  container.push_back(std::forward<T>(v));
}

template<typename Container, typename T>
void
insert(Container &container, T &&v, ...) {
  container.insert(std::forward<T>(v));
}

template<typename T, template<typename...> class Container>
auto
GetRawPtrContainer_helper(Container<T> const &container, int) -> 
decltype(container.reserve(42), Container<typename T::element_type*>()) {

  Container<typename T::element_type*> out;
  out.reserve(container.size());
  for(auto &&e : container) insert(out, e.get(), 0);

  return out;
}

template<typename T, template<typename...> class Container>
Container<typename T::element_type*>
GetRawPtrContainer_helper(Container<T> const &container, ...) {

  Container<typename T::element_type*> out;
  for(auto &&e : container) insert(out, e.get(), 0);

  return out;
}

template<typename T, template<typename...> class Container>
Container<typename T::element_type*>
GetRawPtrContainer(Container<T> const &container) {

  return GetRawPtrContainer_helper(container, 0);
}

Live Demo

Upvotes: 0

amchacon
amchacon

Reputation: 1961

You can use overloading

// Common code in general template and specialization
template <template <typename _T, typename = std::allocator<_T> > class Container>
static Container<T*> GetRawPtrContainer(const Container<SmartPtr<T> >& input_container,Container<T*> &container)
{
    for(typename Container<SmartPtr<T> >::const_iterator it = input_container.begin();
        it != input_container.end();
        it++)
    {
        container.push_back(it->ptr);
    }
    return container;
}

// General template
template <template <typename _T, typename = std::allocator<_T> > class Container>
static Container<T*> GetRawPtrContainer(const Container<SmartPtr<T> >& input_container)
{
    Container<T*> container;
    return GetRawPtrContainer(input_container,container);
}

//Vector specialization
template <template <typename _T>
static Container<T*> GetRawPtrContainer(const std::vector<SmartPtr<T> >& input_container)
{
    std::vector<T*> container;
    container.reserve(input_container.size());
    return GetRawPtrContainer(input_container,container);
}

Upvotes: 1

Related Questions