I want to implement an abstract Matrix (template) class and implement a lazy implementation first. Later on I want to implement a more performance oriented version of this class and want to use it in my whole project without changing everything.
The current problem is, that I am running in problems while implementing the +-operator.
The code below is one iteration, but i tried many different possibilities. But either I get an C2259 "Could not create instance of abstract class" like in the example below or I get runtime problems (Access violations while returning a reference or pointer).
I am sure that I am missing an easy and stupid point (again).
AbstMatrix.cpp:
#pragma once
#include "stdafx.h"
#include "Matrix.hpp"
template<typename T>
class AbstMatrix // : public AddMultEnabled<AbstMatrix<T>>
{
public:
inline virtual size_t getNRows() const = 0;
inline virtual size_t getNCols() const = 0;
inline size_t getNEle() const { return this->getNCols() * this->getNRows(); }
inline virtual T get(size_t iRow, size_t iCol) const = 0;
inline virtual void set(size_t iRow, size_t iCol, T val) = 0;
// Element wise addition
virtual AbstMatrix<T>& operator+=(const AbstMatrix<T>& obj) {
cout << "AM: op+=" << endl;
if (this->getNRows() != obj->getNRows()
|| this->getNCols() != obj->getNCols()) {
throw "Matricies unequal";
}
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, this->get(i, j) + obj->get(i, j));
}
}
return *this;
}
// Elementwise addition
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}
};
Matrix.cpp:
#pragma once
#include "stdafx.h"
#include <algorithm>
#include "AbstMatrix.hpp"
template<typename T>
class Matrix : public AbstMatrix<T>
{
protected:
size_t nRows;
size_t nCols;
size_t nEle;
T* dat;
public:
Matrix(size_t nRows, size_t nCols, T defVal = 0) {
this->nRows = nRows;
this->nCols = nCols;
this->nEle = nCols*nRows;
this->dat = new T[this->getNEle()];
std::fill_n(this->dat, this->getNEle(), defVal);
}
Matrix(const AbstMatrix& obj) {
cout << "M: abst cpy:" << &obj << endl;
this->nRows = obj.getNRows();
this->nCols = obj.getNCols();
this->nEle = obj.getNEle();
this->dat = new T[this->getNEle()];
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, obj.get(i, j));
}
}
}
Matrix & operator= (const AbstMatrix & obj) {
this->nRows = obj.getNRows();
this->nCols = obj.getNCols();
this->nEle = obj.getNEle();
this->dat = new T[this->getNEle()];
for (size_t i = 0; i < this->getNRows(); i++) {
for (size_t j = 0; j < this->getNCols(); j++) {
this->set(i, j, obj.get(i, j));
}
}
}
~Matrix() { if (this->dat) delete[] this->dat; }
inline size_t getNRows() const { return this->nRows; }
inline size_t getNCols() const { return this->nCols; }
inline size_t getNEle() const { return this->nEle; }
inline T get(size_t iRow, size_t iCol) const {
cout << "M: get " << iRow << ", " << iCol << endl;
return this->dat[this->getIdx(iRow, iCol)];
}
inline void set(size_t iRow, size_t iCol, T val) {
cout << "M: set " << iRow << ", " << iCol << endl;
this->dat[this->getIdx(iRow, iCol)] = val;
}
inline AbstMatrix* clone() const {
cout << "M: clone " << endl;
return new Matrix(*this);
}
protected:
size_t getIdx(size_t iCol, size_t iRow) const {
cout << "M: getIdx " << iRow << ", " << iCol << ", "
<< (size_t) (this->getNCols() * iRow + iCol) << endl;
return this->getNCols() * iRow + iCol;
}
};
main.cpp:
#include "stdafx.h"
#include "Matrix.hpp"
int main()
{
Matrix<float> a(5, 5);
Matrix<float> b(5, 5);
a + b;
return 0;
}
Thank you a lot for your help!
[EDIT:] I fixed the (copy-paste) errors mentioned below. Matrix has now a copy and a move constructor. I added the following code at the bottom of AbstMatrix:
namespace detail {
template <typename T>
T AbstMatrix_ElemType(const AbstMatrix<T>&) { return T(); }
}
template <typename M1, typename M2>
auto operator+(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
return obj1 += obj2;
}
template <typename M1, typename M2>
auto operator*(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
return obj1 *= obj2;
}
// Mat multiplication
template <typename M1, typename M2>
auto mult(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemType(obj2))>::value,
M1> {
cout << "AM: mult" << endl;
if (obj1.getNCols() != obj2.getNRows()) {
throw("Matricies incompatible");
}
typedef decltype(detail::AbstMatrix_ElemType(obj1)) matValueType;
M1 retM(obj1.getNRows(), obj2.getNCols());
for (size_t i = 0; i < obj1.getNRows(); i++) {
for (size_t j = 0; j < obj2.getNCols(); j++) {
matValueType tmp = 0;
for (size_t x = 0; x < obj1.getNCols(); x++) {
tmp += obj1.get(i, x) * obj2.get(x, j);
}
retM.set(i, j, tmp);
}
}
return retM;
}
This works perfectly for me. I unfortunately still don't understand why this code works. I tried to read the doc at cppreference, but it just confused me. Do you have a easier source where I can understand the code?
Thanks a lot #aschepler!
// Elementwise addition
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}
You cannot use an abstract class such as AbstMatrix<T> as a return type, since that involves creating an object of exactly that type. Also, your operator+ implementation relies on a specific subclass Matrix<T>. Generally a base class should not know anything about its derived classes (unless you're using CRTP).
Instead, you can define an operator+ template outside the class that acts on any two objects that inherit the same specialization of AbstMatrix, and returns the LHS type:
#include <type_traits>
namespace detail {
template <typename T>
T AbstMatrix_ElemType(const AbstMatrix<T>&);
}
template <typename M1, typename M2>
auto operator+(M1 obj1, const M2& obj2)
-> std::enable_if_t<
std::is_same<decltype(detail::AbstMatrix_ElemType(obj1)),
decltype(detail::AbstMatrix_ElemTYpe(obj2))>::value,
M1>
{ return obj1 += obj2; }
virtual AbstMatrix<T> operator+(const AbstMatrix<T>& obj) const {
cout << "AM: op+" << endl;
Matrix<T> retM(*this);
return retM += obj;
}
You cannot write such an operator that returns an abstract class object. Simply put, your operator says my method returns an instance of type AbstMatrix, but such an instance cannot exist for an abstract class. You can only have actual instances of derived, concrete (non-abstract) classes, and hold on them a reference (AbstMatrix<T>&) or a pointer (AbstMatrix<T>*).
You are actually creating an instance of the derived type Matrix<T> but to match the function's prototype, the return statement is forced to convert it to an AbstMatrix<T> instance (a mechanism called object slicing; it has to create an instance of AbstMatrix<T> and invoke the copy constructor). Since it cannot create such an instance, a compile error occurs.
At best you can make your operator+ to return a Matrix<T> object. But then the whole idea of having an abstract base class, that depends so strictly on one and only one of its derived classes, is not a good design idea. May be you should re-think the design and drop the idea of an abstract matrix class, bu only make it a class template.
Abstract interfaces used like this are not suitable for value-semantic computing.
You cannot return an instance of an abstract class. Actual instances -- values -- are of fixed storage size and type in C++. Derives instances of abstract interfaces won't "fit".
There are a few ways around this. One way is to implement your own polymorphism.
Start with template<class T>class AbstMatrix like you have. Get rid of operators -- only expose pure virtual methods. Virtual operators "work", but are awkward to use, so do not bother. Include virtual std::unique_ptr<AbstMatrix>clone()const=0 Consider eliminating other virtual methods; only include the ones you need. See below. You'll want at(x,y), clone(), increment_by(AbstMatrix), mult_by(AbstMatrix), etc.
Next write Matrix<T>. This does not inherit from AbstMatrix<T>, but instead owns a std::unique_ptr<AbstMatrix<T>> pImpl;. It can be construxted from a unique_ptr<AbstMatrix<T>> as well. It can be "empty"; its methods ahould not assume pImpl is non-null.
This Matrix<T> type implements operator+=, operator+, operator=, Matrix(Matris const&), Matrix(Matris&&)=default, etc. Its job is to be a value semantics type that is polymorphic because its behaviour is determined by the abstract class it owns in a unique ptr.
This lets you have a polymorphic value type.
Now your implementations inherit from AbstMatrix<T> and can be stored in a Matrix<T>.
Related
Currently, I'm creating my own vector class, and I like to create instances of any class via shared pointers. So, is there a way to overload operator[] in order to achieve the following code?
class myVector {
public:
myVector(/*some params*/);
~myVector();
// Some kind of overloading of operator []
// Some other code
private:
// private container array
double* vec;
// Some other code
};
int main ()
{
std::shared_ptr<myVector> sp = std::shared_ptr<myVector>(new myVector(/*some params*/));
double val = sp[1];
return 0;
}
You could declare the [] operator for your myVector class as follows (returning a reference to the element so you have read and write access):
double& operator [] (int i)
{
return vec[i];
}
You can then use this operator on the object pointed to by the shared_ptr by first dereferencing that. Here's a runnable example, where I've added some 'dummy' code to the constructor just to make it do something:
#include <iostream>
#include <memory>
class myVector {
public:
myVector(/*some params*/) {
vec = new double[10];
for (int i = 0; i < 10; ++i) vec[i] = 3 * i;
}
~myVector() {
delete[] vec;
}
double& operator [] (int i) {
return vec[i];
}
private:
double* vec;
};
int main()
{
std::shared_ptr<myVector> sp = std::shared_ptr<myVector>(new myVector(/*some params*/));
double val = (*sp)[6];
std::cout << val << std::endl;
(*sp)[4] = 312;
std::cout << (*sp)[4] << std::endl;
return 0;
}
You can overload the operator for the class the following way
double & operator []( size_t i )
{
return vec[i];
}
and
const double & operator []( size_t i ) const
{
return vec[i];
}
and call it like
std::shared_ptr<myVector> sp = std::shared_ptr<myVector>(new myVector(/*some params*/));
//...
std::cout << ( *sp )[i] << '\n';
I would not do this in real application. But just for demonstration, one possibility to achieve what you want (at least technically) is to derive from std::shared_ptr and there declare the index operator. For ex:
#include <memory>
#include <vector>
using namespace std;
template <class T>
class myshared_ptr;
template <class T >
class myshared_ptr<vector<T>> : public shared_ptr<vector<T>>
{
public:
using elem_type = vector<T>;
using shared_ptr<elem_type>::shared_ptr;
using shared_ptr<elem_type>::operator*;
using shared_ptr<elem_type>::get;
typename elem_type::value_type& operator[](size_t idx)
{
return (*get())[idx];
}
};
int main(int argc, const char * argv[]) {
// insert code here...
std::cout << "Hello, World!\n";
auto p = myshared_ptr<vector<int>>(new vector<int>{1, 2, 3, 4});
cout << p[2] << endl;
return 0;
}
Live Demo
I am trying to create template classes for matrices and vectors of different sizes.
For my Vector class I overloaded the += and + operator to be able to add two vectors of same length. If lengths do not match I want the compiler to throw an error.
I want to store multiple of those mvc::Vector objects (with different lenghts) inside a std::vector.
To do that I created a base class mvc::VectorBase which I inherit all mvc::Vector objects from.
Now I can write
std::vector<std::unique_ptr<mvc::VectorBase>> vectorBuffer;
To be able to call member functions of MyVector from vectorBuffer I added pure virtual functions for those members so that I can use
vectorBuffer[0]->GetLength().
My problem: I can't write code like because VectorBase does not know the operator overload.
mvc::Vector<2> result = (*vectorBuffer[0]) + (*vectorBuffer[1]);
Trying to add operator overloading as pure virtual to mvc::VectorBase did not work because one argument would have to be a template argument from mvc::Vector template class, which I can´t use outside the template.
#include <vector>
#include <memory>
#define T float
namespace mvc
{
class VectorBase
{
public:
// virtual Vector operator+= (Vector<Tlength>& other) = 0;
virtual int GetLength() const = 0;
};
template<int Tlength>
class Vector : public VectorBase
{
private:
T vec[Tlength];
public:
Vector operator+ (Vector<Tlength>& other)
{
for (int i = 0; i < Tlength; i++)
{
vec[i] += other.vec[i];
}
return *this;
}
int GetLength() const
{
return Tlength
}
}
}
int main()
{
mvc::Vector<3> vec3_1;
mvc::Vector<3> vec3_2;
mvc::Vector<4> vec4_1;
mvc::Vector<3> result = vec3_1 + vec3_2; // this line works properly
mvc::Vector<3> result = vec3_1 + vec4_1; //this line won´t compile (as expected)
std::vector<std::unique_ptr<mvc::VectorBase>> vectorBuffer;
vectorBuffer.push_back(std::make_unique<mvc::Vector<2>>());
vectorBuffer.push_back(std::make_unique<mvc::Vector<2>>());
mvc::Vector<2> result = (*vectorBuffer[0]) + (*vectorBuffer[1]); // <-- this is what i want to be able to do
}
How do I implement the desired behavior?
vectorBuffer[0] + vectorBuffer[1] works ONLY if MyVector objects are generated with the same template (Tlength is equal)
This already works with two separately stored instances of MyVector.
It fails when I use polymorphism to store multiple mvc::Vector objects in the same std::vector.
EDIT:
By overloading + operator with with base class as return type I got the requested behavior:
#include <vector>
#include <memory>
#include <iostream>
#define T float
namespace mvc
{
class VectorBase
{
public:
virtual VectorBase* operator+ (VectorBase& other) = 0;
virtual int GetLength() const = 0;
virtual T GetElem(int i) const = 0;
virtual void Print() const = 0;
};
template<int Tlength>
class Vector : public VectorBase
{
private:
T vec[Tlength];
public:
Vector(T initValue)
{
for (int i = 0; i < Tlength; i++)
{
vec[i] = initValue;
}
}
VectorBase* operator+ (VectorBase& other) override
{
if (other.GetLength() != Tlength)
{
std::cout << "[Error]: Argument dimensions mismatch. Program will terminate." << std::endl;
std::cin.get();
exit(-1);
}
//Vector<Tlength> tmpOther = dynamic_cast<Vector<Tlength>&>(other);
for (int i = 0; i < Tlength; i++)
{
//vec[i] += tmpOther.vec[i];
vec[i] += other.GetElem(i);
}
return this;
}
Vector<Tlength> operator+ (Vector<Tlength>& other)
{
for (int i = 0; i < Tlength; i++)
{
vec[i] += other.GetElem(i);
}
return *this;
}
int GetLength() const override
{
return Tlength;
}
T GetElem(int i) const override
{
return vec[i];
}
void Print() const override
{
for (int i = 0; i < Tlength; i++)
{
std::cout << " " << vec[i] << "\n";
}
std::cout << std::endl;
}
};
}
int main()
{
/* without polymorphism */
// vector1
mvc::Vector<2> vec3_1 = mvc::Vector<2>(1.2f);
vec3_1.Print();
// vector2
mvc::Vector<2> vec3_2 = mvc::Vector<2>(3.4f);
vec3_2.Print();
// vector2 = vector1 + vector2
vec3_2 = vec3_1 + vec3_2;
vec3_2.Print();
/* with polymorphism */
// vector buffer storing base class objects
std::vector<mvc::VectorBase*> vectorBuffer;
//vector1
vectorBuffer.push_back(new mvc::Vector<3>(3.5f));
vectorBuffer[0]->Print();
//vector2
vectorBuffer.push_back(new mvc::Vector<3>(2.8f));
vectorBuffer[1]->Print();
//vector2 = vector1 + vector2
vectorBuffer[1] = *vectorBuffer[0] + *vectorBuffer[1];
vectorBuffer[1]->Print();
std::cin.get();
for (unsigned int i = 0; i < vectorBuffer.size(); i++)
{
delete vectorBuffer[i];
}
}
plus operator is overloaded twice to also support "non polymorphic" usage. (see example inside main)
Inside the operator+ override is a commend using #Vikas Awadhiya ´s dynamic_cast solution. This also works. Currently I do not know about performance compared to my current solution with virtual getter function GetElem.
For now I was only able to get it working with raw pointers. Still working on a unique_ptr solution.
Thanks for all replys!
You can do this by making your virtual operator+ return and accept the base class:
class VectorBase
{
public:
virtual int GetLength() const = 0;
// We have to return a heap allocated object because the actual type and,
// hence, its size is unknown
virtual std::unique_ptr<VectorBase> operator+(VectorBase& other) = 0;
};
template<int Tlength>
class Vector: public VectorBase
{
private:
// ...
std::unique_ptr<VectorBase> operator+(VectorBase& other) override
{
if (other.GetLength() != Tlength)
return nullptr; // or throw an exception if you want
Vector result = *this + static_cast<Vector<Tlength>&>(other);
// or "new Vector<Tlength>(result)" if your compiler doesn't support C++14
return std::make_unique<Vector<Tlength>>(result);
}
// ...
};
I have made few changes in code see this,
#include <iostream>
#include <vector>
#include <memory>
namespace mvc
{
class VectorBase
{
public:
virtual ~VectorBase(){}
virtual VectorBase& operator+ ( VectorBase& other) = 0;
virtual int GetLength() const = 0;
};
template<int length>
class Vector: public VectorBase
{
private:
double vec[length];
public:
Vector(): VectorBase(),
vec{}
{
}
VectorBase& operator+ ( VectorBase& other) override
{
Vector< length>& subOther = dynamic_cast< Vector< length>&>( other);
for ( int i = 0; i < length; i++)
{
vec[i] += subOther.vec[ i];
}
return *this;
}
int GetLength() const
{
return length;
}
};
}
int main()
{
std::vector<std::unique_ptr<mvc::VectorBase>> vectorBuffer;
vectorBuffer.push_back( std::make_unique<mvc::Vector< 2>>());
vectorBuffer.push_back( std::make_unique<mvc::Vector< 2>>());
mvc::Vector< 2> result = dynamic_cast< mvc::Vector< 2>&>( *vectorBuffer[ 0] + *vectorBuffer[ 1]);
std::cout<< "result.length = "<< result.GetLength()<< std::endl;
}
output: result.length = 2
In C, one can assign a data pointer to a void pointer and then cast it back to the original type, that data pointer will be recovered. The language standard guarantees that such transformation does not lose information. This often means (not necessarily, but true for most platforms) that the size of void pointer is the same with that of data pointers. Thus one can count on these facts to use void pointers as general pointers to heterogeneous types while void pointers themselves are of uniform size and representation. For example, one has an array of void pointers, with its elements pointing to dynamically allocated objects of different types. Constructing such an array makes certain things convenient.
My question is: How does one implement something similar, a general pointer type in C++, which comply with the following: (assume g_pointer is the class name )
Constructed from any pointer types, one can write code like
g_pointer g_ptr = g_pointer(new T())
Recover the original pointer
T* ptr = g_ptr.recover(), or
auto* ptr = g_tr.recover()
Update: According to some comments, the above couldn't be done in C++, then something like
recover<Type>(g_ptr)
should suffice, throwing an exception Type is not compatible.
g_pointer can be contained in std::vector or a plain array, that is basically means
sizeof(g_pointer) // a predetermined constant number,
(Update: This is always true, provided such a class can be correctly implemented, thanks for pointing out.)
I have just found boost::any, a peek into its introduction seems suggeesting that it may be what I want, although it might not be the case. So anyone who is familiar with boost::any is welcomed to comment.
Update: (response to some comments)
A g_pointer type object should be aware of the underlying type of the object to which it points. thus the recover method should always return a pointer of that type.
A general pointer type, meaning a reference to ANY object, IMHO, is a reasonable thing to ask to any language supporting object-oriented paradigm.
Update: Thanks #Caleth, std::any is great.
It is impossible in C++. Because the type of the expression g_ptr.recover() is determined at compile time, it cannot store information of the underlying type, which is determined at runtime.
If you can tolerate expressions like g_ptr.recover<T>(), you can implement g_pointer by wrapping a void* and a const std::type_info& that stores the information of the actual type the pointer points to, e.g.
class g_pointer {
public:
template <class T>
constexpr g_pointer(T *data) noexcept : _data(data), _object_type(typeid(T)) {}
template <class T>
T* recover() const {
if (typeid(T) == _object_type) return static_cast<T*>(_data);
else throw std::bad_cast{};
}
private:
void *_data;
const std::type_info &_object_type;
};
Note this g_pointer behaves like a raw pointer rather than a smart pointer, which means it does not own the object it points to.
There is still a defect in the implementation above: const T* cannot be implicitly converted to void*, thus the general pointer cannot hold const T*. To handle const-qualifiers, you can change the type of _data to const void* and use const_cast when recovering. In addition, recover shall reject to return a pointer to non-const object from a g_pointer holding a pointer to const object. However, typeid operator ignores top const-qualifiers, so we need an additional data member to record whether the pointer points to an originally const object.
class g_pointer {
public:
template <class T>
constexpr g_pointer(T *data) noexcept : _data(data),
_object_type(typeid(T)),
_is_const(std::is_const_v<T>)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ change here
{
}
template <class T>
T* recover() const {
if (
typeid(T) != _object_type ||
(_is_const && !std::is_const_v<T>) // try to obtain T* while const T* is held
) {
throw std::bad_cast{};
}
else return static_cast<T*>(const_cast<void*>(_data));
// ^^^^^^^^^^^^^^^^^ change here
}
private:
const void *_data;
// ^^^^^ change here
const std::type_info &_object_type;
bool _is_const; // <-- record whether the pointer points to const T
};
There's nothing stopping you from using C constructs, like a void*, in C++. It's generally frowned upon, however, because it can open the door for various bugs should the code be used in ways unintended, or the consequences of said actions not being fully documented.
That being said, you're essentially asking to wrap a void* in a class that can then be used in a std::vector and then accessed later.
Here's some code from a framework I wrote some time ago to sort of achieve a similar effect:
generic_ptr.hpp
#include <exception>
#include <typeinfo>
#include <map>
namespace so {
std::map<std::size_t, std::size_t> type_sizes;
template < typename T >
std::size_t type_id()
{
static char tid;
std::size_t sz = reinterpret_cast<std::size_t>(&tid);
so::type_sizes[sz] = sizeof(T);
return sz;
}
template < typename T >
inline std::size_t type_id(const T& t)
{
return so::type_id<T>();
}
template < typename T >
inline std::size_t type_id(const T *const t)
{
return so::type_id<T>();
}
template < typename T, typename C >
inline bool type_of()
{
return so::type_id<T>() == so::type_id<C>();
}
template < typename T, typename C >
inline bool type_of(const C& c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const C *const c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const T& t, const C& c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const T *const t, const C *const c)
{
return so::type_of<T, C>();
}
class generic_ptr
{
public:
generic_ptr() : m_ptr(0), m_id(0) { }
template < typename T >
generic_ptr(T *const obj) :
m_ptr(obj), m_id(so::type_id<T>())
{
}
generic_ptr(const generic_ptr &o) :
m_ptr(o.m_ptr), m_id(o.m_id)
{
}
~generic_ptr()
{
this->invalidate();
}
static generic_ptr null()
{
return generic_ptr();
}
void invalidate()
{
this->m_ptr = 0;
this->m_id = 0;
}
template < typename T >
bool is_type() const
{
return this->m_id == so::type_id<T>();
}
template < typename T >
void gc()
{
delete ((T*)this->m_ptr);
this->invalidate();
}
bool valid() const
{
return (this->m_ptr != 0);
}
operator bool() const
{
return (this->m_ptr != 0);
}
bool operator!() const
{
return (!operator bool());
}
generic_ptr& operator=(const generic_ptr &o)
{
this->m_ptr = o.m_ptr;
this->m_id = o.m_id;
return *this;
}
template < typename T >
const generic_ptr& operator=(T *const obj)
{
this->m_ptr = obj;
this->m_id = so::type_id<T>();
return *this;
}
template < typename T >
operator T *const() const
{
if (this->m_id != so::type_id<T>()) {
throw std::bad_cast();
}
return static_cast<T *const>(
const_cast<void *const>(this->m_ptr)
);
}
template < typename T >
operator const T *const() const
{
if ((this->m_id != so::type_id<T>()) && (this->m_id != so::type_id<const T>())) {
throw std::bad_cast();
}
return static_cast<const T *const>(this->m_ptr);
}
operator void *const() const
{
return const_cast<void*>(this->m_ptr);
}
operator const void *const() const
{
return this->m_ptr;
}
bool operator==(const generic_ptr& o) const
{
return (this->m_ptr == o.m_ptr && this->m_id == o.m_id);
}
bool operator!=(const generic_ptr& o) const
{
return !(*this == o);
}
std::size_t hash() const
{
return this->m_id;
}
private:
const void* m_ptr;
std::size_t m_id;
};
}
Then to use it:
main.cpp
#include <iostream>
#include <vector>
#include "generic_ptr.hpp"
class MyClass {
public:
MyClass() : m_val1(10), m_val2(20), m_val3(10), m_val4(2) {}
MyClass(int a, int b, int c, int d) : m_val1(a), m_val2(b), m_val3(c), m_val4(d) {}
friend std::ostream& operator<<(std::ostream& os, const MyClass& mc)
{
os << mc.m_val1 << " + " <<
mc.m_val2 << " + " <<
mc.m_val3 << " + " <<
mc.m_val4 << " = " <<
(mc.m_val1 + mc.m_val2 + mc.m_val3 + mc.m_val4);
return os;
}
private:
int m_val1;
int m_val2;
int m_val3;
int m_val4;
};
template < typename T >
void print(so::generic_ptr& g_ptr)
{
std::cout << "sizeof = " << so::type_sizes[g_ptr.hash()]
<< ", val = " << *((T*)g_ptr) << std::endl;
}
template < typename T >
void cleanup(so::generic_ptr& g_ptr)
{
delete ((T*)g_ptr);
}
int main(int argc, char* argv[])
{
std::vector<so::generic_ptr> items;
items.push_back(new int(10));
items.push_back(new double(3.14159));
items.push_back(new MyClass());
items.push_back(new char(65));
items.push_back(new MyClass(42,-42,65536,9999));
items.push_back(new int(999));
for (auto i : items) {
if (i.is_type<int>()) { print<int>(i); }
else if (i.is_type<char>()) { print<char>(i); }
else if (i.is_type<double>()) { print<double>(i); }
else if (i.is_type<MyClass>()) { print<MyClass>(i); }
}
int* i = (int*)items[0];
std::cout << "i = " << *i << std::endl;
*i = 500;
std::cout << "i = " << *i << std::endl;
try {
double* d = (double*)items[0];
std::cout << "d = " << *d << std::endl;
} catch (std::bad_cast& ex) {
std::cout << ex.what() << std::endl;
}
for (auto i : items) {
if (i.is_type<int>()) {
print<int>(i);
cleanup<int>(i);
} else if (i.is_type<char>()) {
print<char>(i);
cleanup<char>(i);
} else if (i.is_type<double>()) {
print<double>(i);
cleanup<double>(i);
} else if (i.is_type<MyClass>()) {
print<MyClass>(i);
cleanup<MyClass>(i);
}
}
return 0;
}
Of course, you still have to know the type and keep track of memory, but you could modify the code to handle that; using the operator overloads, you don't need a recover function in this manner, you can just do a cast, like in the print code: *((T*)g_ptr), and can access it via raw pointers, like before the last for..each statement:
int* i = (int*)items[0];
*i = 500;
print<int>(items[0]);
This class also has invalid type casting built in, in the event you try and cast between invalid types:
try {
double* d = (double*)items[0];
// items[0] is an int, so this will throw a std::bad_cast
std::cout << "d = " << *d << std::endl;
} catch (std::bad_cast& ex) {
std::cout << ex.what() << std::endl;
}
To be honest though, convenience could trump security in this instance, so if you need an array of types that are not consistent or that can not be defined using a base class, you might need to rethink what you're trying to achieve in a C++ manner.
I hope that can help you get some clarity.
I recently stumbled across expression templates in C++. There is one thing that I do not quite understand about their implementation, and that is why the base class is necessary (from which all other objects relating to template expression derive in a CRTP manner). A simple example that does addition and scalar multiplication on vectors (objects of type Vec, without base class):
#include <vector>
#include <iostream>
using namespace std;
class Vec
{
vector<double> data;
public:
template<typename E>
Vec(E expr)
{
data = vector<double>(expr.size());
for (int i = 0; i < expr.size(); i++)
data[i] = expr[i];
}
Vec(int size)
{
data = vector<double>(size);
for (int i = 0; i < size; i++)
data[i] = i;
}
double operator [] (int idx) {
return data[idx];
}
int size() { return data.size(); }
bool operator < (Vec &rhs)
{
return (*this)[0] < rhs[0];
}
bool operator > (Vec &rhs)
{
return (*this)[0] > rhs[0];
}
};
template<typename E1, typename E2>
class VecAdd
{
E1 vec_expr1;
E2 vec_expr2;
public:
VecAdd(E1 vec_expr1, E2 vec_expr2) : vec_expr1(vec_expr1), vec_expr2(vec_expr2)
{}
double operator [] (int idx) { return vec_expr1[idx] + vec_expr2[idx]; }
int size() { return vec_expr1.size(); }
};
template<typename E>
class ScalarMult
{
E vec_expr;
double scalar;
public:
ScalarMult(double scalar, E vec_expr) : scalar(scalar), vec_expr(vec_expr)
{}
double operator [] (int idx) { return scalar*vec_expr[idx]; }
int size() { return vec_expr.size(); }
};
template<typename E1, typename E2>
VecAdd<E1, E2> operator + (E1 vec_expr1, E2 vec_expr2)
{
return VecAdd<E1, E2>(vec_expr1, vec_expr2);
}
template<typename E>
ScalarMult<E> operator * (double scalar, E vec_expr)
{
return ScalarMult<E>(scalar, vec_expr);
}
int main()
{
Vec term1(5);
Vec term2(5);
Vec result = 6*(term1 + term2);
Vec result2 = 4 * (term1 + term2 + term1);
//vector<Vec> vec_vector = {result, result2}; does not compile
vector<Vec> vec_vector;
vec_vector = { result2, result }; //compiles
vec_vector.clear();
vec_vector.push_back(result);
vec_vector.push_back(result2); //all this compiles
for (int i = 0; i < result.size(); i++)
cout << result[i] << " ";
cout << endl;
system("pause");
return 0;
}
The code above compiles (except for the indicated line), and it evaluates the simple expressions in the main function without fault. If the expressions get assigned to an object of type Vec and assign their contents to a Vec object, getting destroyed in the process in any case, why is it necessary for a base class? (as shown in this Wikipedia article)
EDIT:
I know this code is a bit messy and bad (copying where unnecessary, etc.) but I am not planning on using this specific code. This is just to illustrate that expression templates work in this example without the CRTP base class - and I am trying to figure out exactly why this base class is necessary.
Your
template<typename E1, typename E2>
VecAdd<E1, E2> operator + (E1 vec_expr1, E2 vec_expr2)
will match for any user-defined types, not merely expression types. When instantiated with non-vector types, it will then likely fail. This interacts very badly with other C++ types, quite possibly including standard library types, which provide their own custom operator + and may rely on inexact matches resolving to their own operator + after implicit conversions.
Making operator + only available for VecExpression<E> avoids that problem.
I have been working on a priority queue using a binary heap and have developed a class for this as shown below.
#include <iostream>
#include <type_traits>
template<class T, int N>
class BinaryHeap{
template<class T1>
class has_less_than_operator
{
private:
class no{};
template<class X>
static auto has(X&& t) -> decltype (t.operator < (t));
static no has(...);
public:
enum {
value = !std::is_same<
decltype(has( std::declval<T1>() )),
no>::value
};
};
static_assert(std::is_copy_assignable<T>::value &&
std::is_copy_constructible<T>::value,
"Must be copy assignable and constructable");
public:
BinaryHeap() : used_(0){
}
BinaryHeap(BinaryHeap const& other) = default;
BinaryHeap& operator = (BinaryHeap const& other) = default;
inline T& max(){
return elements_[FIRST];
}
inline T const & max() const{
return elements_[FIRST];
}
void insert(T const& item){
elements_[++used_] = item;
swim(used_);
}
inline bool full() const{
return used_ == N;
}
void deleteMax(){
std::swap(elements_[used_],elements_[FIRST]);
sink(FIRST);
elements_[used_--] = T();
}
private:
template<class T1>
class has_dereference_operator
{
private:
class no{};
template<class X>
static auto has(X&& t) -> decltype (t.operator * ());
static no has(...);
public:
enum {
value = !std::is_same<
decltype(has( std::declval<T1>() )),
no>::value
};
};
inline bool parent_less(int position,std::integral_constant<int,0> i){
return elements_[ position / 2] < elements_[position];
}
inline bool parent_less(int position,std::integral_constant<int,1> i){
return *(elements_[ position / 2]) < *(elements_[position]);
}
void swim(int position){
while(position > 1 && parent_less(position,std::integral_constant<int, has_dereference_operator<T>::value>()))
{
std::swap(elements_[ position / 2], elements_[position]);
position /= 2;
}
}
inline int which_less(int p1, int p2, std::integral_constant<int,0> i){
return (elements_[ p1] < elements_[p2]) ? p1 : p2;
}
inline int which_less(int p1, int p2, std::integral_constant<int,1> i){
return (*(elements_[ p1]) < *(elements_[p2])) ? p1 : p2;
}
inline int which_greater(int p1, int p2, std::integral_constant<int,0> i){
return (elements_[ p1] < elements_[p2]) ? p2 : p1;
}
inline int which_greater(int p1, int p2, std::integral_constant<int,1> i){
return (*(elements_[ p1]) < *(elements_[p2])) ? p2 : p1;
}
void sink(int position){
while(position * 2 <= used_){
int first = position * 2;
if(first > used_) break;
int greater_child = which_greater(first, first + 1, std::integral_constant<int, has_dereference_operator<T>::value>());
int lesser = which_less(greater_child, position, std::integral_constant<int, has_dereference_operator<T>::value>());
if(lesser == greater_child)
break;
std::swap(elements_[greater_child], elements_[position]);
position = greater_child;
}
}
inline int current_position() const{
return used_ + 1;
}
static const int MAX = N + 1;
static const int FIRST = 1;
static const int LAST = N;
T elements_[MAX];
int used_;
};
int main(int argc, const char * argv[])
{
BinaryHeap<int, 10> b;
b.insert(1);
b.insert(20);
b.insert(21);
b.insert(3);
b.insert(2);
std::cout << "Max: " << b.max() << std::endl;
b.deleteMax();
std::cout << "Max: " << b.max() << std::endl;
return 0;
}
Although I have this working I need to deal with the differences in comparing say a pointer / shared pointer say using dereference operator and values just using them as is. I am currently using SFINAE to do this based on if the class has operator *.
Is this the right way to achieve this?
Blair
The problem with using heuristics like this is that it's not always what the client of your code wants you to do, and you don't provide a way to change the behavior. Someday a client may want to use your class to store pointers and actually sort them with std::less<T> instead of dereferencing (BinaryHeap<void*,32> for example). Even with non-pointers, a client may simply want a differing ordering than that imposed by <.
When the Standard Library needs to perform comparison it usually uses std::less<T> by default but provides a way for the client to override that choice (e.g. std::priority_queue or std::sort). If I was writing your class, I would parameterize it by comparison operator defaulting to std::less<T> just like the Standard Library would. I would also provide a handy dereferencing comparator template to make it easy for clients using pointers to order by pointees.