compound eye
compound eye

Reputation: 2130

template function to handle class and class*

The code below allows me to template a function taking a parameter which is a vector of one of three different pointer types to Box objects:

const std::vector<std::shared_ptr<Box>>&
const std::vector<std::weak_ptr<Box>>&
const std::vector<Box*>&

Is there a way to extend this to support:

const vector<Box>& 
const vector<std::reference_wrapper<Box>>

perhaps something in boost?

#include <vector>
#include <iostream>

class Box{
public:

    Box (unsigned int id, unsigned int side): id(id), side(side){}

    int volume(){
        return side * side * side;
    }
    unsigned int id;
    unsigned int side;

};

template <typename T>
struct is_box_containter {
    enum { value = false };
};

template <>
struct is_box_containter <std::vector<std::shared_ptr<Box>>> {
    enum { value = true };
};

template <>
struct is_box_containter <std::vector<std::weak_ptr<Box>>> {
    enum { value = true };
};

template <>
struct is_box_containter <std::vector<Box*>> {
    enum { value = true };
};

template <typename T>
typename std::enable_if<is_box_containter<T>::value>::type
measure(T const& boxes )
{
    for (auto& box : boxes) {
        std::cout << box->id << " has volume " << box->volume() << std::endl;
    }
}

int main (){

    std::vector<std::shared_ptr<Box>>  some_boxes;
    some_boxes.push_back(std::shared_ptr<Box>(new Box(1,4)));
    some_boxes.emplace_back(new Box(2, 12));
    Box * box_3 = new Box(3, 8);
    Box * box_4 = new Box(4, 9);

    std::vector<Box*>  more_boxes;
    more_boxes.emplace_back(box_3);
    more_boxes.emplace_back(box_4);

    measure(some_boxes);
    measure(more_boxes);

    return 0;
}

Why I am asking this question: I have an application with two functions which implement near identical logic. One takes a list of SomeClass, the other takes a vector of pointers to SomeClass. I am currently planning on refactoring the code to replace the list of SomeClass with a list of shared pointers to SomeClass. But the only reason I am doing this is to move the logic to a common implementation. I don't want to do that if there is a perfectly reasonable way to avoid it.

Upvotes: 4

Views: 111

Answers (1)

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42929

If I understood your question correctly, you could use a dereferencing mechanism like below:

template<typename T> 
T& dereference(T &v) {
  return v;
}

template<typename T> 
const T& dereference(const T& v) {
  return v;
}

template<typename T> 
typename std::enable_if<!std::is_function<T>::value, T&>::type dereference(T* v) {
  return dereference(*v);
}

template<typename T> 
const T& dereference(const std::shared_ptr<T>& v) {
  return dereference(*v);
}

template<typename T> 
const T& dereference(const std::weak_ptr<T>& v) {
  return dereference(*v);
}

template<typename T> 
const T& dereference(const std::reference_wrapper<T>& v) {
  return v;
}

and then call your data like:

template <typename T>
typename std::enable_if<is_box_containter<T>::value>::type
measure(T const& boxes )
{
    for (auto& box : boxes) {
        std::cout << dereference(box).id 
                  << " has volume " << dereference(box).volume() << std::endl;
    }
}

LIVE DEMO

P.S You'll also have to define:

template <>
struct is_box_containter <std::vector<Box>> {
    enum { value = true };
};

template <>
struct is_box_containter <std::vector<std::reference_wrapper<Box>>> {
    enum { value = true };
};

Upvotes: 4

Related Questions