Reputation: 7913
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
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
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
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
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
Reputation: 171117
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