Finite element c++ implementation - c++

I'm dealing with a code that is really poorly commented, since I need to expand a few functionalities I need to understand what every piece of code does in order to know if my changes will affect it.
The code is dealing with 2D mesh generation (using library Triangle for it) and solving PDEs on it.
Here is the code I don't understand:
void FiniteElement<Integrator, ORDER>::setPhiMaster()
{
Eigen::Matrix<Real,3*ORDER,1> coefficients;
for (auto i=0; i < 3*ORDER; i++)
{
coefficients = MatrixXr::Zero(3*ORDER,1);
coefficients(i) = 1;
for (auto iq=0; iq < Integrator::NNODES; iq++)
{
Real phi = evaluate_point<ORDER>(reference_,Integrator::NODES[iq],coefficients);
phiMapMaster_(i,iq) = phi;
}
}
}
In the end I would like to know what exactly is phiMapMaster_ and whats the possible use of it!
It is a method inside the template class FiniteElement, suppose ORDER=1, and Integrator:
class IntegratorTriangleP2{
public:
static const UInt ORDER = 1;
//Number of nodes
static const UInt NNODES = 3;
//Point locations
static const std::vector<Point> NODES;
static const std::vector<Real> WEIGHTS;
};
const std::vector<Real> IntegratorTriangleP2::WEIGHTS = std::vector<Real>{ {1./3, 1./3, 1./3} };
const std::vector<Point> IntegratorTriangleP2::NODES = std::vector<Point> { {Point(1./6,1./6),Point(2./3,1./6),Point(1./6,2./3)} };
(Point is the same as std::complex), and here is the method evaluate_point that takes as imput a triangle (simply 3 points counter-clockwise ordered), the point (which is internal to the triangle) and the coefficients of the Fourier basis defined on the triangle
template <>
inline Real evaluate_point<1>(const Triangle<3>& t, const Point& point, const Eigen::Matrix<Real,3,1>& coefficients)
{
Eigen::Matrix<Real,3,1> bary_coeff = t.getBaryCoordinates(point);
//getBaryCoordinates retunrs the barycentric coordinates of the point wrt the triangle t
return(coefficients.dot(bary_coeff));
}

Related

Changing old code which is difficult to understand

My main trouble comes from void setWSparsity(const ExponentialSparsityTemplate(& t)(double wSparsity, double SparsityExp));, t.sparsity(p, _decay), and the "SparsityTemplate" of which there are more than one kind. I either get the header definition to compile and not the code or vice-versa. How should these definitions be properly setup?
The example below does not compile but demonstrates what I am struggling with. I would like to have 2 or more different SparsityTemplates and properly use const and private variables. Initialization was missing and I attempted to add that. The sample takes code from a linear algebra library, so namespaces have been removed.
_Ws as does _t comes from a linear algebra namespace
#include <cmath>
#include "matrix.h"
/**
* Sparsity template with exponential increase depending on the time
* index p.
*/
class Deconvolver
{
class SparsityTemplate
{
public:
virtual double sparsity(double p) const;
virtual ~SparsityTemplate() = default;
};
class ExponentialSparsityTemplate: public SparsityTemplate
{
public:
ExponentialSparsityTemplate(double sparsity, double sparsityexp) :
_sparsity(sparsity), _decay(sparsityexp) {}
virtual ~ExponentialSparsityTemplate() = default;
void setWSparsity(const ExponentialSparsityTemplate(& t)(double wSparsity, double SparsityExp));
private:
double _sparsity = 0;
double _decay = 0;
};
void setWSparsity(const ExponentialSparsityTemplate(& t)(double wSparsity, double SparsityExp));
};
int main()
{
double wSparsity = 0.01;
double wSparsityExp = 1.7;
// The Matrix is 10 x 5 and is filled by a generator function
Matrix x(10, 5, );
Deconvolver d(x, 10, 3);
d.setWSparsity(
ExponentialSparsityTemplate(wSparsity, wSparsityExp));
}
void Deconvolver::setWSparsity(
const ExponentialSparsityTemplate(& t)(double wSparsity, double wSparsityExp))
{
_sparsity = wSparsity * std::pow(sparsityexp, p);
for (unsigned int p = 0; p < _t; ++p)
{
for (unsigned int j = 0; j < _wS[p]->cols(); ++j)
{
for (unsigned int i = 0; i < _wS[p]->rows(); ++i)
_wS[p]->at(i, j) = t.sparsity(p, _decay);
}
}
}
The problem was a combination of things:
C++ Virtual function being hidden
https://www.learncpp.com/cpp-tutorial/constructors-and-initialization-of-derived-classes/
In my project some virtual functions had used the same name as those in the initialization list and as I was trying to initialize and remove unused variables, I had disturbed hidden virtual functions which I hadn't fully understood. The exact same names being used made the code very difficult to follow so I went back to the original code and more slowly applied name changes to private variables and member initialization lists, and then made changes to virtual functions. I used () not {} for parameters in point 2 but the example was helpful as the same name was not used.

C++ list insertion strange behavior

I have a class called Particle, and another called ParticleList, which is essentially a List of Particles plus some other functions. I'm noticing some strange behavior when I try to insert a new particle into ParticleList and I'm wondering why that is happening. The classes are defined as:
class Particle {
public:
// Particle data members
int index;
vector<double> r_last;
vector<double> r;
vector<double> v;
double m;
double q;
Particle(int i, double m, double q, int ndim) :
index(i), m(m), q(q) {
r_last.resize(ndim,0);
r.resize(ndim,0);
v.resize(ndim,0);};
Particle() { };
Particle(const Particle& p);
}
and
class ParticleList : public list<Particle> {
public:
int highestIndex;
size_t numParticles;
ParticleList() {highestIndex = 0; numParticles=0;}
/*below are functions that call the List<Particle>::push_back()
and List<Particle>::push_front() functions and increment numParticles*/
void push_back(const Particle& p);
void push_front(const Particle& p);
//some more member functions here
};
Definition of push_back and push_front:
void ParticleList::push_back(const Particle &p) {
numParticles ++;
list<Particle>::push_back(p);
}
void ParticleList::push_front(const Particle &p) {
numParticles ++;
list<Particle>::push_front(p);
}
The trouble comes in when I try to insert a particle as follows:
ParticleList newParticleList;
Particle newParticle(1, 0.5, 0.5, 2);
/*creates a particle with index 1, mass and charge 0.5, and 2 dimensions.
Variables r, r_last and v are set to vectors {0,0}*/
for (int i=0;i<nDim;i++)
newParticle.r[i]=0.5 //just changed newParticle.r, everything else still {0,0}
newParticleList.push_back(newParticle);
For some reason, when I do the last step, the value of the r_last vector for the list member that just got inserted changes to the value of the r vector. So if I print out newParticle.r_last, that'd give me {0,0}, but if print out the member of the list:
auto ii=newParticleList.end();
ii--;
Particle p=*ii;
for(int i=0;i<p.size();i++)
cout<<p.r_last[i];
I get {0.5,0.5}, which is the value of p.r. If I change the r vector after this point, it doesn't affect the value of r_last...only when I push_back does it create this effect. I've tried different computers, removed optimization flags, tried push_front and insert, and it's still the same behavior. Does anyone have ideas on what might be causing this?
Thank you!
Siddharth
Here Particle p=*ii; you create a copy of Particle object. So check your copy constructor Particle(const Particle& p);. May be it incorrectly copies p.r into p.r_last.

C++ should I use pointer or reference?

I would like to have an insight about whenever I should be using references or pointers.
Let's take the example of a Polygon class using a Rectangle class for its internal bounding box.
Polygon.h
class Polygon {
private:
std::list<Point> _points;
Rectangle _boundingBox;
public:
Polygon(const std::list<Point> &);
public:
const std::list<Point> &getPoints() const;
const Rectangle &getBoundingBox() const;
private:
void setBoundingBox();
};
Polygon.cpp
#include <iostream>
#include "Polygon.h"
Polygon::Polygon(const std::list<Point> &points)
{
if (points.size() < polygon::MIN_SIDE + 1) {
throw std::invalid_argument("A polygon is composed of at least 3 sides.");
}
if (points.front() != points.back()) {
throw std::invalid_argument("A polygon must be closed therefore the first point must be equal to the last one.");
}
std::list<Point>::const_iterator it;
for (it = ++points.begin(); it != points.end(); ++it) {
this->_points.push_back(*it);
}
this->setBoundingBox();
}
void Polygon::translate(const std::array<float, 2> &vector)
{
std::list<Point>::iterator it;
for (it = this->_points.begin(); it != this->_points.end(); ++it) {
(*it).setX((*it).getX() + vector[0]);
(*it).setY((*it).getY() + vector[1]);
}
Point topLeft = this->_boundingBox->getTopLeft();
Point bottomRight = this->_boundingBox->getBottomRight();
topLeft.setX(topLeft.getX() + vector[0]);
topLeft.setY(topLeft.getY() + vector[1]);
bottomRight.setX(bottomRight.getX() + vector[0]);
bottomRight.setY(bottomRight.getY() + vector[1]);
}
const std::list<Point> &Polygon::getPoints() const
{
return this->_points;
}
const Rectangle &Polygon::getBoundingBox() const
{
return this->_boundingBox;
}
void Polygon::setBoundingBox()
{
float xMin = this->_points.front().getX();
float xMax = this->_points.front().getX();
float yMin = this->_points.front().getY();
float yMax = this->_points.front().getY();
std::list<Point>::const_iterator it;
for (it = this->_points.begin(); it != this->_points.end(); ++it)
{
Point point = *it;
if (point.getX() < xMin) {
xMin = point.getX();
}
if (point.getX() > xMax) {
xMax = point.getX();
}
if (point.getY() < yMin) {
yMin = point.getY();
}
if (point.getY() > yMax) {
yMax = point.getY();
}
}
this->_boundingBox = new Rectangle(Point(xMin, yMin), Point(xMax, yMax));
}
std::ostream &operator<<(std::ostream &out, const Polygon &polygon)
{
std::list<Point>::const_iterator it;
for (it = polygon.getPoints().begin(); it != polygon.getPoints().end(); ++it) {
out << (*it);
if (it != polygon.getPoints().end()) {
out << " ";
}
}
return out;
}
Rectangle.h
#pragma once
#include <stdexcept>
#include "Point.h"
class Rectangle {
private:
Point _topLeft;
Point _bottomRight;
public:
Rectangle(const Point &, const Point &);
public:
const Point &getTopLeft() const;
const Point &getBottomRight() const;
float getWidth() const;
float getHeight() const;
};
Rectangle.cpp
#include "Rectangle.h"
Rectangle::Rectangle(const Point &topLeft, const Point &bottomRight)
{
if (topLeft.getX() > bottomRight.getX() || topLeft.getY() > bottomRight.getY()) {
throw std::invalid_argument("You must specify valid top-left/bottom-right points");
}
this->_topLeft = topLeft;
this->_bottomRight = bottomRight;
}
const Point &Rectangle::getTopLeft() const
{
return this->_topLeft;
}
const Point &Rectangle::getBottomRight() const
{
return this->_bottomRight;
}
float Rectangle::getWidth() const
{
return this->_bottomRight.getX() - this->_topLeft.getX();
}
float Rectangle::getHeight() const
{
return this->_bottomRight.getY() - this->_topLeft.getY();
}
Point.h
#pragma once
#include <ostream>
#include <cmath>
class Point {
private:
float _x;
float _y;
public:
Point(float = 0, float = 0);
public:
float distance(const Point &);
public:
float getX() const;
float getY() const;
void setX(float);
void setY(float);
};
std::ostream &operator<<(std::ostream &, const Point &);
bool operator==(const Point &, const Point &);
bool operator!=(const Point &, const Point &);
Point.cpp
#include "Point.h"
Point::Point(float x, float y)
{
this->_x = x;
this->_y = y;
}
float Point::distance(const Point &other)
{
return std::sqrt(std::pow(this->_x - other.getX(), 2) + std::pow(this->_y - other.getY(), 2));
}
float Point::getX() const
{
return this->_x;
}
float Point::getY() const
{
return this->_y;
}
void Point::setX(float x)
{
this->_x = x;
}
void Point::setY(float y)
{
this->_y = y;
}
std::ostream &operator<<(std::ostream &out, const Point &point)
{
out << "(" << point.getX() << ", " << point.getY() << ")";
return out;
}
bool operator==(const Point &p1, const Point &p2)
{
return p1.getX() == p2.getX() && p1.getY() == p2.getY();
}
bool operator!=(const Point &p1, const Point &p2)
{
return p1.getX() != p2.getX() || p1.getY() != p2.getY();
}
A lot of questions come with this snippet of code.
This does not compile because obviously whenever we try to create a Polygon, it ends up trying to create a Rectangle with a default constructor which does not exist.
I can't use initializer list because obviously the bounding box depends on some computed values from my list of points.
I could create a default constructor creating two Point(0, 0) by default for the Rectangle but this does not make much sense.
I could use pointers but then I feel this is not the nicest solution as I tend to think this is mostly used for polymorphism in C++ we should prefer reference whenever possible.
How should I then proceed ?
I feel I am missing something out about C++ and could learn a lot from this.
I think your main question is about how to deal with the problem of needing to initialize both std::list<Point> _points; and Rectangle _boundingBox;, while also doing some validation of _points.
The simplest solution is to just give Rectangle a default constructor (or pass two default Points as initializer). Then once you have validated the points argument in the constructor, you calculate the Rectangle based on the points.
A slightly more complicated alternative is to allow the validation function to be invoked from the ctor-initializer list, e.g.:
Polygon::Polygon(std::list<Point> points)
: _points( validate_point_list(points), std::move(points) ), _boundingBox( calculateBoundingBox(_points) )
{
}
where you have functions (which could be free functions):
void validate_point_list(std::list<Point> &points)
{
if (points.size() < polygon::MIN_SIDE + 1)
throw std::invalid_argument("A polygon is composed of at least 3 sides.");
if (points.front() != points.back())
throw std::invalid_argument("A polygon must be closed therefore the first point must be equal to the last one.");
// caller must pass in same first and last point, but we only store one of the two
points.erase( points.begin() );
}
and
Rectangle calculateBoundingBox(std::list<Point> const &_points)
{
// whatever logic you have in setBoundingBox, except return the answer
}
Note that the loop in your Polygon constructor is unnecessarily complicated. You could have just written _points = points; and then erased the extra point (which is O(1) for lists).
Note that I have passed by value and then used std::move. The reason is that if the argument given is a rvalue then it can just be moved right on through into where it's being stored; whereas with the const & version, a copy is stored and then the original is destructed.
I would use const & a lot less than you did. Small objects , such as Point and Rectangle, don't suffer a performance penalty from pass-by-value (and might even be more efficient that way). And as mentioned in the previous paragraph; if your function accepts a parameter and it is going to take a copy of that parameter, it's better to pass by value .
Passing by reference is best only when you are using but not storing the values passed. For example, calculateBoundingBox.
Finally, once you get this working, you might want to think about having the Polygon constructor accept an iterator pair of points range, and/or a std::initializer_list.
I would defined a default constructor for Rectangle class as private and I would make Polygon class a friend of Rectangle class:
class Rectangle {
friend class Polygon;
Point _topLeft;
Point _bottomRight;
Rectangle(); // accessible only to friends
public:
Rectangle(Point const&, Point const&);
...
};
And then in setBoundingBox():
void Polygon::setBoundingBox() {
...
_boundingBox._topLeft = Point(xMin, yMin);
_boundingBox._bottomRight = Point(xMax, yMax);
}
Thus, I wouldn't expose the default constructor of Rectangle and at the same time I would have a concrete object which is more efficient in terms of cache performance.
I feel as though you should have a separate class called BoundingBox that
1) Takes a collection of points in its constructor
2) Is inherited from Rectangle
Meanwhile, Rectangle should have a state, along the lines of NOT_A_RECTANGLE or it could throw an exception. Just be sure you clean up when throwing exceptions from a constructor.
Then you would construct the bounding box as part of the construction of the polygon and you can verify that a bounding box is possible as part of your error checking. (probably rather than 3 sides check, but I am no geometry expert)
BoundingBox would remain a member of Polygon.
This would be more RTTI.
It occurs to me though, that if you translate or rotate the polygon, you've also go to translate or rotate the bounding box. You might want to consider making the list of points its own object and sharing them. This would be a more advanced topic. You can for now get away with just recalculating the bounding box on operations performed upon the polygon.
As to whether to use a reference, a pointer, or pass by value, I don't know that there is a black and white list of things to consider for this, but a few are:
Is the object large enough to even worry about it? A rectangle is 4 floats?
Are there interfaces or base classes you will need to cast to, rather than always using the class itself? If so, you've got no choice but to use a pointer of some sort. The pointer could be unique, shared, weak, etc. depending on the situation. You have to ask yourself who owns it, whats the life time, and are there circular references?
Most people will probably use a reference whenever possible rather than a pointer, but only when passing by value doesn't qualify.
IMO, since you are just "GetBoundingBox", I think it would be simple and more maintainable to just return a copy of the bounding box by value rather than some const reference and definitely more than a pointer.
One solution would be to write a programmatic constructor for Rectangle that takes as its argument a const std::list<Point>&. It could traverse the list once, computing the maximum and minimum x and y. Then, your Polygon constructor would become:
Polygon::Polygon(const std::list<Point> &points)
: _points(points),
: _boundingBox(points)
{
// ...
}
An alternative is to move the code to find the bounding box from a list of points to a helper function, then define a move constructor Rectangle::Rectangle( Rectangle&& x ). In that case, your Polygon constructor would be:
Polygon::Polygon(const std::list<Point> &points)
: _points(points),
: _boundingBox( findBoundingBox(points) )
{
// ...
}
Either way, you could update a bounding box with assignment, so you might want an assignment operator like Rectangle& Rectangle::operator= ( Rectangle&& x ) to make that more efficient. You can skip the Rectangle&& versions if a Rectangle is just Plain Old Data. But if you do this a lot, you might overload Rectangle& findBoundingBox( const std::list<Point>& src, Rectangle& dest ) to update in place with no copying.
On a minor side note, I’d discourage you from using identifiers that begin with underscores, since those names are reserved in the global namespace in C++, and your libraries might declare something named _point.

Basic class design in C++, dealing with co-dependent classes

I have two classes a 3D vector class(Vector3) with an array of 3 floats as its member(float[3]), and a 3 by 3 matrix class that stores 3 of these vectors in another array(Vector3[3]). My vector class requires the matrix class to rotate about an axis, and my matrix class requires vectors for everything. I was using forward declarations and pointers to deal with it, like in this question: What is the best way to deal with co-dependent classes in C++?, but there must be a better way to design this to avoid that altogether. At the moment I declare a pointer to Vector3 on my matrix header file, and then initialize it with new in my implementation file, yet this feels clumsy. Any pointers(no pun) on how to solve this issue?
Edit: I use the vector class to represent a 3D point which I intend to rotate about an arbitrary axis.
The code as I would like it to work:
//Vector3.h
#include "Matrix3.h"
class Matrix3;
class Vector3 {
float xyz[3];
};
//Vector3.cpp
Vector3 Vector3::rotatePoint(Vector3 o, Vector3 a, float theta) {
Vector3 point = (*this);
Vector3 x = Vector3(1,0,0);
Vector3 y = Vector3(0,1,0);
Vector3 z = Vector3(0,0,1);
// Create new coordinate system
Vector3 new_coord[4];
new_coord[0] = o;
new_coord[1] = a.normalize();
unsigned closer_to;
if (a*x < a*y) {
new_coord[2] = (a % x).normalize();
closer_to = 0; // x
}
else {
new_coord[2] = (a % y).normalize();
closer_to = 1; // y
}
new_coord[3] = (a % new_coord[2]).normalize();
// Transform point to new coord system
Matrix3 trans_matrix = Matrix3(new_coord[0], new_coord[1], new_coord[2]);
point = trans_matrix*(point - o);
// Rotate about a by theta degrees
Matrix3 r_m(closer_to, theta);
point = r_m*point;
//Transform back to original coord system
point = (trans_matrix.inverse()*point) + o;
return point;
}
//Matrix3.h
#include "Vector3.h"
class Vector3;
class Matrix3 {
Vector3 rows[3];
}
The code that I use to make it work:
//Vector3.h
class Matrix3;
class Vector3 {
float xyz[3];
};
//Matrix3.h
#include "Vector3.h"
class Vector3;
class Matrix3 {
Vector3 *rows;
}
//Matrix3.cpp
Matrix3::Matrix3() {
rows = new V3[3];
}
I took the advice given by #n.m. and #Chris Dodd's and just removed the Matrix3 include from my Vector header, and into my Vector implementation file, like this:
//Vector3.h
class Vector3 {
float xyz[3];
}
//Vector.cpp
#include "Vector3.h"
#include "Matrix3.h"
//Matrix3.h
#include "Vector3.h"
class Vector3;
class Matrix3 {
Vector3 rows[3];
}
//Matrix3.cpp
#include "Matrix3.h"
Since you're dealing with vectors and matrices, you could define a single matrix class with templates for the dimensions (and maybe the element type) -- the vector class would then (depending on the type of vector) be a matrix with one of the dimension = 1. If you do this, you only end up with one class, one set of code and all the functionality required to do everything you want. You avoid any inter-class dependencies since you only have one class!
The class itself would look something like this:
template<unsigned m, unsigned n, typename T = float>
class matrix {
T e[m * n];
public:
T* operator [](unsigned i) { return e + i; }
T * const operator [](unsigned i) const { return e + i; }
/* repeat for -, *, / -- same pattern */
matrix<m,n>& operator += (matrix<m,n> const& a) {
for (unsigned i = 0; i < m * n; ++i) e[i] += a.e[i];
return *this;
}
};
You should then define any functions which does not take equal sized matrices outside the class (this is why we have the [] operator). Note that the * and / operators work on all elements and do not calculate the usual matrix-matrix multiplication or matrix-matrix division. Instead, you will want to have a invert function and a multiply function.
template<unsigned m, unsigned n, unsigned k>
matrix<m,k> const multiply(matrix<m,n> const& a, matrix<n,k> const& b);
template<unsigned m>
matrix<m,m> const invert(matrix<m,m> const&);
template<unsigned m, unsigned n>
matrix<n,m> const transpose(matrix<m,n> const&);
Note that, if you have a method for inverting a generic non-square matrix, of which I know none, the above template will only work for a square matrix. Filling in the code for the functions I leave to you as an exercise.
To get a 3 vector and a 3x3 matrix, you could typedef them as so:
typedef matrix<3,1,float> vector3f;
typedef matrix<3,3,float> matrix3x3f;
Remember to add comparison operators as well as anything else you need.
If you wish, you could add matrix-scalar and scalar-matrix operators and functions as well (to ease, say, adding a scalar to all the elements in a matrix).
If you want better control over the elements in the [] operators, you could use vectors as the base class and then define matrices as a vector of vectors. This will give you the same advantages but may suit your mindset more easily (a lot of people think of vectors and matrices as being distinct types).

2D matrix and overloading operator() / ugly syntax

I'm using a 2D matrix in one of my projects. It's something like it is suggested at C++ FAQ Lite.
The neat thing is that you can use it like this:
int main()
{
Matrix m(10,10);
m(5,8) = 106.15;
std::cout << m(5,8);
...
}
Now, I have a graph composed of vertices and each vertex has a public (just for simplicity of the example) pointer to 2D matrix like above. Now I do have a pretty ugly syntax to access it.
(*sampleVertex.some2DTable)(0,0) = 0; //bad
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse...
Probably I'm missing some syntactic sugar here due to my inexperience with operator overloading. Is there a better solution?
Consider using references instead of pointers (provided, it can't be null and you can initialize in the constructor).
Consider making a getter or an instance of a matrix wrapper class for a vertex that returns a reference to 2D matrix (provided, it can't be null).
sampleVertex.some2DTable()(0,0) = 0;
sampleVertex.some2DTableWrap(0,0) = 0;
However, to me it sounds like a non-issue to justify going through all the trouble.
If you have a pointer to a Matrix, e.g. as a function parameter that you can't make a reference (legacy code, e.g.), you can still make a reference to it (pseudo code):
struct Matrix {
void operator () (int u, int v) {
}
};
int main () {
Matrix *m;
Matrix &r = *m;
r (1,1);
}
You're basically limited to (*sampleVertex.some2DTable)(0,0). Of course, if you don't need reseating, why not store the actual values in the matrix instead?
Alternatively, make the pointer private and make an accessor (note: the following examples assume a matrix of EntryTypes):
Matrix& Vertex::GetTableRef()
{
return *some2DTable;
}
// or
Matrix::EntryType& Vertex::GetTableEntry(int row, int col)
{
return (*some2DTable)(row,col);
}
// way later...
myVertex.GetTableRef()(0,0) = 0;
// or...
myVertex.GetTableEntry(0,0) = 0;
Or, just define an inline function to do this for you if you can't change the class Vertex:
// in some header file
inline Matrix& GetTableRef(Vertex& v)
{
return *v.some2DTable;
}
// or you could do this
inline Matrix::EntryType& GetTableEntry(Vertex& v, int row, int col)
{
return (*v.some2DTable)(row, col);
}
// later...
GetTableRef(myVertex)(0, 0) = 0;
// or
GetTableEntry(myVertex, 0, 0) = 0;
Finally, don't forget that you don't have to use operator overloading. STL collections implement an at() member function, which is checked, as opposed to operator[] which is unchecked. If you don't mind the overhead of bounds checking, or if you just want to be nonstandard, you could implement at() and then just call myVertex.some2DTable->at(0,0), saving a bit of a syntactic headache altogether.
There is no C++ syntactic sugar that will ease the pain of what you describe:
(*sampleVertex.some2DTable)(0,0) = 0; //bad
sampleVertex.some2DTable->operator()(0,0) = 0; //even worse...
In this situation, I would either have the graph return a reference instead of a pointer, or have the matrix define a function which calls the operator():
inline matrixType &Matrix::get( int x, int y ){ return operator()(x,y); }
Then, the syntax isn't quite as ugly for the vertex example:
sampleVertex.some2DTable->get(0,0) = 0;
I would add a function that returns you a ref like rlbond recommends. For a quick fix or if you don't have control over the source of it, i would go with this:
sampleVertex.some2DTable[0](0,0) = 0; // more readable
That's actually equivalent, because the following holds if a is a pointer to a defined class:
*a == *(a + 0) == a[0]
See this long discussion on comp.lang.c++ about that same problem with good answers.
This is the best way without changing your code:
//some2DTable is a pointer to a matrix
(*sampleVertex.some2DTable)(0,0)
You could also instead make some2DTable a reference to a matrix instead of a pointer to a matrix. Then you would have simplified syntax as in your first code sniplet.
//some2DTable is a reference to a matrix instead of a pointer to a matrix
sampleVertex.some2DTable(0,0)
Or you could keep some2DTable a pointer to a reference and simply store a reference variable to it and use that in the context of your code block.
I'd change the way you get hold of "sampleVertex.some2DTable" so it returns a reference.
Either that or create the reference yourself:
Matrix& m = *sampleVertex.some2DTable;
m(1,2) = 3;
I don't know if it's worth the trouble, but you could do:
class MatrixAccessor {
private:
Matrix2D* m_Matrix;
public:
MatrixAccessor(Matrix2D* matrix) : m_matrix(matrix) { }
double& operator()(int i, int j) const { return (*m_Matrix)(i,j); }
Matrix2D* operator->() const { return m_Matrix; }
void operator=(Matrix2D* matrix) { m_Matrix = matrix; }
};
Provided the original operator() returns a reference (as it is in many matrix classes).
Then you provide that MatrixAccessor in your vertex class:
class Vertex {
Matrix2D* myMatrix;
public:
MatrixAccessor matrix;
Vertex(Matrix2D *theMatrix) : myMatrix(theMatrix), matrix(theMatrix) { }
};
Then you can write:
Vertex v;
v.matrix(1,0) = 13;
v.matrix->SomeOtherMatrixOperation();
EDIT
I added const keywords (thanks to #phresnel for bringing up the topic) in order to make the solution semantically equivalent to a solution only presenting a public Matrix2D-pointer.
An advantage of this solution is that constness could be transferred to the matrix object by adding two non-const versions of the operator()() and operator->() (i.e. the matrix cannot be modified on const vertices) and changing the const ones to return a const double& and const Matrix2D* respectively.
That would not be possible when using a public pointer to the matrix object.
You could implement Matrix::operator (int,int) by calling a member function and use that one directly when dealing with pointers.
class Matrix
{
public:
float ElementAt( int i, int j ) const { /*implement me*/ }
float operator() ( int i, int j ) const { return ElementAt( i, j ); }
...
};
void Foo(const Matix* const p)
{
float value = p->ElementAt( i, j );
...
}
void Bar(const Matrix& m)
{
float value = m(i,j);
}