James Bond
James Bond

Reputation: 7913

c++ template programming: how to call type's function?

I am new to c++ template programming.

What i want to do is to writing a generic container with fixed capacity for a certain property of its items.

template <typename T >
    class SinkContainer{

    std::list<T> _buffer;

    int sum(); // i don't know how to implement this :-(
    public:
    void push_back(T a);
}

Type T could be

Class Client{
    public:
    int num_of_accounts;
}

or

class Supplier{
public:
     int num_of_warehouse
}

the container can only contain a MAX_NUM of accounts or warehouse, which means everytime we insert into the buffer, we need to sum up current total accounts OR num of wharehouse.

Can anyone suggest a way to write this generic container? should I over-ride + for Client and Supplier?

Upvotes: 1

Views: 771

Answers (5)

Useless
Useless

Reputation: 67723

Well, you can use a pointer-to-member, like this:

template <typename T, int T::*N>
class SinkContainer {
    bool do_push_back(T const &);
public:
    int sum();
    bool push_back(T const &t) {
        if (sum() + t.*N < MAX_NUM)
            return do_push_back(t);
        else
            return false;
    }
};

and use it as

SinkContainer<Client, &Client::num_of_accounts> clients;
SinkContainer<Supplier, &Supplier::num_of_warehouse> suppliers;

but it's very inflexible. The counter has to be an integer data member (it can't be unsigned, or long, or calculated on the fly), and MAX_NUM is hard-coded.

The traits approach gives you more flexibility, since it can do something different for each type: Agnew has already shown this, so I won't repeat it.

Upvotes: 0

okaerin
okaerin

Reputation: 799

Write in the classes Client and Supplier a "Getter" method to get the their number property.

this way you could always call the methods and just make the sum

int sum(void){
int iSum = 0;
for(std::list<T>::iterator it = _buffer.begin();it!=_buffer.end();++it){
    iSum+=it->GetNum();
}
return iSum;    
}

Upvotes: 2

Marcin Łoś
Marcin Łoś

Reputation: 3246

"Generic container with fixed capacity" is a description of std::array (or boost::array, depending on which you can use, if any). As for the generic summing, I'm not sure if this operation should be a part of the container. I believe an approach using generic function working on arbitrary STL-style sequence specified by pair of iterators, dispatched by iterators' value_type is a cleaner solution. Or, even better, use std::for_each. Something along the lines of

int extract_number(const Client& c)
{
    return c.num_of_accounts;
}

int extract_number(const Supplier& s)
{
    return s.num_of_wareouse;
}

template <typename Iter>
int sumStuff(Iter begin, Iter end)
{
    typedef typename Iter::value_type type;
    int sum;
    std::for_each(begin, end, [&sum](const type& item)
    {
        sum += extract_number(item);
    }
    return sum;
}

This way, operation and container are decoupled, and we get a neat, parametrically-polymorphic sum function.

Upvotes: 2

Victor  Laskin
Victor Laskin

Reputation: 2647

If you cant modify Client and Supplier - you can achieve what you want using template specialization:

template <typename T >
class SinkContainer{

     int getCount(T t);

}

Inside .cpp:

template<>
int SinkContainer<Client>::getCount(T t)
{
     return t.num_of_accounts;
}


template<>
int SinkContainer<Supplier>::getCount(T t)
{
     return t.num_of_warehouse;
}

Upvotes: 0

This is normally solved using a so-called traits class. Basically, you define an adaptor which provides a unified interface for your template to use.

// Traits class definition
template <class T>
struct SinkSumTraits;

template <>
struct SinkSumTraits<Client>
{
    static int summand(const Client &c) { return c.num_of_accounts; }
};

template <>
struct SinkSumTraits<Supplier>
{
    static int summand(const Supplier &s) { return s.num_of_warehouse; }
};


// Traits class usage

template <typename T >
class SinkContainer {
    std::list<T> _buffer;

    int sum()
    {
        return std::accumulate(
            _buffer.begin()
            , _buffer.end()
            , 0
            , [](int s, const T &a) { return s + SinkSumTraits<T>::summand(a); }
        );
    }
public:
    void push_back(T a);
}

Note: If you don't have C++11 (for the lambda used in sum()), simply replace it with some other use of SinkSumTraits<T>::summand().

Upvotes: 3

Related Questions