How to reduce code duplication of a class that is template specialized?
I am trying to create a class (MyArray) that acts like std::vector but receives raw-pointer as parameter in some functions.
Here is a simplified version of it :-
template<class T> class MyArray{
T database[10];
public: T& get(int index){return database[index];}
void set(int index, T t){
database[index]=t;
}
};
template<class T> class MyArray<std::unique_ptr<T>>{
T* database[10];
public: T*& get(int index){return database[index];}
void set(int index, std::unique_ptr<T> t){
T* tmp=t.release();
database[index]=tmp;
}
};
Here is a test:-
class B{};
int main() {
MyArray<B> test1;
MyArray<B*> test2;
MyArray<std::unique_ptr<B>> test3;
test3.set(2,std::make_unique<B>()));
return 0;
}
Question: Please demonstrate an elegant way to reduce the above code duplication in MyArray.
A solution that I wished for may look like :-
template<class T> class MyArray{
using U = if(T=std::uniquePtr<X>){X*}else{T};
U database[10];
public: U& get(int index){return database[index];}
void set(int index, T t){
U u = convert(t);//<-- some special function
database[index]=u;
}
};
There might be some memory leak / corruption. For simplicity, please overlook it.
I just want an idea/rough guide. (no need to provide a full run-able code, but I don't mind)
In real life, there are 20+ function in MyArray and I wish to do the same refactoring for many classes.
Edit: I have (minor) edited some code and tag. Thank AndyG and Jarod42.
Maybe can you delegate the implementation details to a struct you provide to your class, and you specialize this struct, not MyArray:
template <typename T>
struct Policy {
using type = T;
static type convert(T t) { ... }
};
template <typename T>
struct Policy<std::unique_ptr<T>> {
using type = T*;
static type convert(T t) { ... }
};
template <typename T, typename P = Policy<T>>
class MyArray
{
using type = typename P::type;
void set(int index, T t) { type result = P::convert(t); }
};
You might think about using a common base class for the common functionality:
template<class T>
class Base{
protected:
T database[10];
public:
T& get(int index){return database[index];}
};
template<class T>
class MyArray : public Base<T>{
public:
void set(int index, T t){
this->database[index]=t;
}
};
template<class T>
class MyArray<std::unique_ptr<T>> : public Base<T*>
{
public:
void set(int index, std::unique_ptr<T>&& t){
T* tmp=t.release();
this->database[index]=tmp; //a little different
}
};
Demo
Related
For example, I have a class template:
template <typename T>
class base {
public:
void set(T data) { data_=data; }
private:
T data_;
};
And for a certain type I would like to add a function, but also have functions from the template class.
template <>
class base<int>{
public:
void set(int data) { data_=data; }
int get(){ return data_;} //function specific to int
private:
int data_;
}
How to do that without copying all members from the template class?
With inheritance:
template <typename T> struct extra {};
template <> struct extra<int> {
public:
int get() const;
};
template <typename T>
class base : public extra<T> {
friend class extra<T>;
public:
void set(T data) { data_=data; }
private:
T data_ = 0;
};
int extra<int>::get() const{ return static_cast<const base<int>*>(this)->data_;}
Demo
You can do this by using enable_if from type_traits to enable the get function only when the template parameter is int. One example is shown below.
#include <type_traits>
template <typename T>
class base {
public:
template <typename X=T,
std::enable_if_t< std::is_same<X,typename T>::value
&& std::is_same<X,int>::value, bool> = false>
int get() { return data_; }
void set(T data) { data_=data; }
private:
T data_;
};
I am just working on my school homework, and I am interested if C++ could create specialized destructor for pointers. I know that this is not good practice, but because of performance, I want to do it this way. Also because I am curious about it. So lets say, that we have this class:
template<typename T,int(*comp)(const T& first, const T& second)>
class BinomialHeapLite
{
private:
class Node
{
Node* next;
T instance;
}
Node* top;
public:
~BinomialHeapLite();
}
Now I want destructor, that delete just node, if is T only type and delete also inner instance, if is T pointer..
template<typename T, int(*comp)(const T& first, const T& second)>
BinomialHeapLite<T,comp>::~BinomialHeapLite()
{
//some code
delete this->top;
}
template<typename T, int(*comp)(const T* &first, const T* &second)>
BinomialHeapLite<T*,comp>::~BinomialHeapLite()
{
//some code
delete this->top->instance;
delete this->top;
}
But this give me "invalid use of incomplete type". I also want to use pure C++11, because I want to be independent to another libraries (also standards libraries), moreover that libraries are not permitted in out system.
Is something like this possible in pure C++?
You're giving your BinomialHeapLite class a responsibility that it shouldn't have: cleaning up instance if it's a heap-allocated pointer.
That burden should be on the user of your class: what if he is already calling delete in its own code? What if the object is supposed to be reused after your BinomialHeapLite is destroyed?
All your class should do is provide a generic container that manages its own memory. For that task, your should also use smart pointers (std::unique_ptr in this case):
template<typename T,int(*comp)(const T& first, const T& second)>
class BinomialHeapLite
{
private:
class Node
{
std::unique_ptr<Node> next;
T instance;
}
std::unique_ptr<Node> top;
}
You won't need an hand-written destructor then. Refer to the "rule of three/five/zero" for more information.
If you really want to implement your flawed/unconventional design, you could use a partially specialized helper struct that calls delete if its type is a pointer:
template <typename T>
struct dtor_helper
{
static void do_it(T&&){}
};
template <typename T>
struct dtor_helper<T*>
{
static void do_it(T* x){ delete x; }
};
template<typename T, int(*comp)(const T& first, const T& second)>
BinomialHeapLite<T,comp>::~BinomialHeapLite()
{
//some code
dtor_helper<T>::do_it(this->top->instance);
delete this->top;
}
wandbox example
You can not partially specialize a member function. But you can partially specialize a class template:
template <class T>
struct Deallocator{
template <class Node>
static void Deallocate(const Node &node){}
};
template <class T>
struct Deallocator<T *>{
template <class Node>
static void Deallocate(const Node &node){}
};
template<typename T, int(*comp)(const T& first, const T& second)>
BinomialHeapLite<T,comp>::~BinomialHeapLite()
{
//some code
Deallocator<T>::Deallocate(top);
}
Or overload a function :
template<typename T,int(*comp)(const T& first, const T& second)>
class BinomialHeapLite
{
private:
struct Node // a little change here
{
Node* next;
T instance;
};
Node* top;
template <class Ty>
void deallocate(Ty *) {}
template <class Ty>
void deallocate(Ty &) {}
public:
~BinomialHeapLite();
};
template<typename T, int(*comp)(const T& first, const T& second)>
BinomialHeapLite<T,comp>::~BinomialHeapLite()
{
//some code
deallocate(top->instance);
}
I accept onathan Wakely's comment as answer. Thank you! The below is original post.
[original post]
Have an idea about making set and get methods in a class automatically as shown below
template<class T>
class Has
{
public:
template<class U>
const U& get() const;
template<>
const T& get<T>() const
{
return m_t;
}
template<class U>
void set(const U& t);
template<>
void set<T>(const T& t)
{
m_t = t;
}
private:
T m_t;
};
An example
class Door {};
class Window {};
class House
: public Has<Door>
, public Has<Window>
{};
House house;
// set
house.set(Door());
house.set(Window());
// get
const Door& door = house.get<Door>();
const Window& window = house.get<Window>();
If the components' types are different and their type names are readable, the code shown above is fine. But if the type name is not readable, such as the area of the house has a type double, I'd like to use
house.get<Area>(); // or
house.get<AREA>(); // where the template argument can be a const integer
other than
house.get<double>();
And if there are two components with double type, such as area and volume, how to deal with the complex? Thanks a lot!
There is a way to wrap the double as a new type like
template<class T>
class Wrap
{
public:
Wrap(const T& value = T())
: m_value
{}
operator T()
{
return m_value;
}
private:
T m_value;
};
class Area
: public Wrap<double>
{};
To doing this, is there any performance affection? Thanks.
Following jweyrich's suggestion, add the following code
template<class Name, class T>
class With
{
public:
template<class U>
const U& get() const;
template<>
const T& get<Name>() const
{
return m_t;
}
template<class U>
void set(const U& t);
template<>
void set<Name>(const T& t)
{
m_t = t;
}
private:
T m_t;
};
I think this work perfectly.
An example
class Door {};
class Window {};
class Area {}; // Empty, just a name holder
class Volume {}; // Another a name holder
class House
: public Has<Door>
, public Has<Window>
, public With<Area, double>
, public With<Volume, double>
{};
House house;
// set
house.set(Door());
house.set(Window());
house.set<Area>(3000);
house.set<Volume>(30000);
// get
const Door& door = house.get<Door>();
const Window& window = house.get<Window>();
double area = house.get<Area>();
double volume = house.get<Volume>();
Any one know for the template inheritance, is there any performance reduction? In my opinion, I think there is not. Thanks a lot!
I am writing a generalized container using a class template, with a restriction (policy) that the items stored in the container should derive from a specific base class.
Here is the definition of the class template
// GenericContainer.hpp
// --------------------------------------
class ContainerItem
{
protected:
virtual ContainerItem& getInvalid() = 0;
public:
virtual ~ContainerItem();
bool isValid() const;
};
template<typename D, typename B>
class IsDerivedFrom
{
static void Constraints(D* p)
{
B* pb = p; // this line only works if 'D' inherits 'B'
pb = p; // suppress warnings about unused variables
}
protected:
void IsDerivedFrom2() { void(*p)(D*) = Constraints; }
};
// Force it to fail in the case where B is void
template<typename D>
class IsDerivedFrom<D, void>
{
void IsDerivedFrom2() { char* p = (int*)0; /* error */ }
};
template <class T>
class GenericContainer : public IsDerivedFrom<T, ContainerItem>
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
// Class to be contained (PURPOSELY, does not derive from ContainerItem)
// Data.hpp
//----------------------------------------------------------------
class Data
{ /* implem details */};
// Container for Data items
// Dataset.h
// ----------------------------------------------------------------------------
#include "GenericContainer.hpp"
#include "Data.hpp"
class Dataset: public GenericContainer<Data>
{
public:
Data& getInvalid();
};
// C++ source
// -----------------------------------------------------------
#include "Dataset.hpp"
Dataset ds;
Can anyone explain why the code above compiles?.
[Edit]
The code above should NOT compile for two reasons:
The class 'Data' does NOT derive from ContainerItem, and yet it can be stored in GenericContainer (as illustrated by the class Dataset). Incidentally, this issue has now been resolved thanks to the answer given by Omifarious and jdv
The class 'Data' does NOT implement the pure virtual method declared in the ABC ContainerItem - using the fixes recommended in the answers below, the first issue (enforcement of policy) is resolved, however the compiler fails to notice that Data does not implement the getInvalid() method of the ContainerItem 'interface'. Why is the compiler missing this glaring mistake?
BTW, compiler and OS details are:
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Change IsDerivedFrom2 to IsDerivedFrom and it fails to compile in just the expected manner.
The problem is that a method from a template class is never instantiated if it isn't called. Changing the name makes it a constructor, so it then ends up being called by the constructors of classes derived from IsDerivedFrom. It will still compile to empty code. The compiler will optimize it away the dead assignment.
I would recommend you not write template code like this yourself if you can manage to use Boost, particularly is_base_of from the Boost type traits library.
In particular, your GenericContainer template can be more simply and easily implemented this way using Boost:
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_base_of.hpp>
template <class T>
class GenericContainer
{
private:
typedef std::vector<T> TypeVect;
void addElement(const T& elem);
TypeVect m_elems;
public:
unsigned int size() const;
T& elementAt(const unsigned int pos);
const T& elementAt(const unsigned int pos) const;
GenericContainer() {
BOOST_STATIC_ASSERT( (::boost::is_base_of<ContainerItem, T>::value) );
}
};
template <class T>
void GenericContainer<T>::addElement(const T& elem)
{
m_elems.push_back(elem);
}
template <class T>
unsigned int GenericContainer<T>::size() const
{
return m_elems.size();
}
template <class T>
T& GenericContainer<T>::elementAt(const unsigned int pos)
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
template <class T>
const T& GenericContainer<T>::elementAt(const unsigned int pos) const
{
unsigned int maxpos = m_elems.size();
if (pos < maxpos)
return m_elems[pos];
return T::getInvalid();
}
The Constraints function is not generated because IsDerivedFrom2 is never referenced. This is required behavior for C++. Maybe it helps to call it from the constructor. Otherwise, check the boost library for functionality like this.
I have this:
template <typename T>
class myList
{
...
class myIterator
{
...
T& operator*();
}
}
...
template<typename T>
T& myList<T>::myIterator::operator*()
{
...
}
That is giving me the following error: "expected initializer before '&' token". What exactly am I supposed to do? I already tried adding "template myList::myIterator" before it, but that didn't work.
How about some semicolons and publics:
template <typename T>
class myList
{
public:
class myIterator
{
public:
T& operator*();
};
};
Compiles Fine:
If you want to post code it should be as simple as passable, BUT it should still be compilable. If you cut stuff out will nilly then you will probably remove the real error that you want fixed and the people here are real good at finding problems if you show people the code.
In this situation we can only put it down to some code that you have removed.
template <typename T>
class myList
{
public:
class myIterator
{
public:
T& operator*();
};
};
template<typename T>
T& myList<T>::myIterator::operator*()
{
static T x;
return x;
}
int main()
{
myList<int> a;
myList<int>::myIterator b;
int& c= *b;
}