Logical const in a container in C++ - c++

Edited to include MWE (removing example-lite) and added details about compilation and Valgrind output.
I am using the mutable keyword to achieve the result of lazy evaluation and caching a result. This works fine for a single object, but doesn't seem to work as expected for a collection.
My case is more complex, but let's say I have a triangle class that can calculate the area of a triangle and cache the result. I use pointers in my case because the thing being lazily evaluated is a more complex class (it is actually another instance of the same class, but I'm trying to simplify this example).
I have another class that is essentially a collection of triangles. It has a way to calculate the total area of all the contained triangles.
Logically, tri::Area() is const -- and mesh::Area() is const. When implemented as above, Valgrind shows a memory leak (m_Area).
I believe since I am using a const_iterator, the call to tri::Area() is acting on a copy of the triangle. Area() is called on that copy, which does the new, calculates the area, and returns the result. At that point, the copy is lost and the memory is leaked.
In addition, I believe this means the area is not actually cached. The next time I call Area(), it leaks more memory and does the calculation again. Obviously, this is non-ideal.
One solution would be to make mesh::Area() non-const. This isn't great because it needs to be called from other const methods.
I think this might work (mark m_Triangles as mutable and use a regular iterator):
However, I don't love marking m_Triangles as mutable -- I'd prefer to keep the compiler's ability to protect the constiness of m_Triangles in other un-related methods. So, I'm tempted to use const_cast to localize the ugly to just the method that needs it. Something like this (mistakes likely):
Not sure how to implement with const_cast -- should I be casting m_Triangles or this? If I cast this, is m_Triangles visible (since it is private)?
Is there some other way that I'm missing?
The effect I want is to keep mesh::Area() marked const, but have calling it cause all the tris calculate and cache their m_Area. While we're at it -- no memory leaks and Valgrind is happy.
I've found plenty of examples of using mutable in an object -- but nothing about using that object in a collection from another object. Links to a blog post or tutorial article on this would be great.
Thanks for any help.
Update
From this MWE, it looks like I was wrong about the point of the leak.
The code below is Valgrind-clean if the call to SplitIndx() is removed.
In addition, I added a simple test to confirm that the cached value is getting stored and updated in the container-stored objects.
It now appears that the call m_Triangles[indx] = t1; is where the leak occurs. How should I plug this leak?
#include <cmath>
#include <map>
#include <cstdio>
class point
{
public:
point()
{
v[0] = v[1] = v[2] = 0.0;
}
point( double x, double y, double z )
{
v[0] = x; v[1] = y; v[2] = z;
}
double v[3];
friend point midpt( const point & p1, const point & p2 );
friend double dist( const point & p1, const point & p2 );
friend double area( const point & p1, const point & p2, const point & p3 );
};
point midpt( const point & p1, const point & p2 )
{
point pmid;
pmid.v[0] = 0.5 * ( p1.v[0] + p2.v[0] );
pmid.v[1] = 0.5 * ( p1.v[1] + p2.v[1] );
pmid.v[2] = 0.5 * ( p1.v[2] + p2.v[2] );
return pmid;
}
double dist( const point & p1, const point & p2 )
{
double dx = p2.v[0] - p1.v[0];
double dy = p2.v[1] - p1.v[1];
double dz = p2.v[2] - p1.v[2];
return sqrt( dx * dx + dy * dy + dz * dz );
}
double area( const point & p1, const point & p2, const point & p3 )
{
double a = dist( p1, p2 );
double b = dist( p1, p3 );
double c = dist( p2, p3 );
// Place in increasing order a, b, c.
if ( a < b )
{
std::swap( a, b );
}
if ( a < c )
{
std::swap( a, c );
}
if ( b < c )
{
std::swap( b, c );
}
if ( c-(a-b) < 0.0 )
{
// Not a real triangle.
return 0.0;
}
return 0.25 * sqrt( ( a + ( b + c ) ) * ( c - ( a - b ) ) * ( c + ( a - b ) ) * ( a + ( b - c ) ) );
}
class tri
{
public:
tri()
{
m_Area = NULL;
}
tri( const point & p1, const point & p2, const point & p3 )
{
m_P1 = p1; m_P2 = p2; m_P3 = p3;
m_Area = NULL;
}
~tri() {
delete m_Area;
}
tri( const tri & t )
{
m_P1 = t.m_P1;
m_P2 = t.m_P2;
m_P3 = t.m_P3;
if ( t.m_Area )
{
m_Area = new double( *(t.m_Area) );
}
else
{
m_Area = NULL;
}
}
tri & operator=( const tri & t )
{
if ( this != &t )
{
m_P1 = t.m_P1;
m_P2 = t.m_P2;
m_P3 = t.m_P3;
if ( t.m_Area )
{
m_Area = new double( *(t.m_Area) );
}
else
{
m_Area = NULL;
}
}
return *this;
}
bool KnowsArea() const
{
if ( !m_Area ) return false;
return true;
}
void SetPts( const point & p1, const point & p2, const point & p3 )
{
m_P1 = p1; m_P2 = p2; m_P3 = p3;
delete m_Area;
m_Area = NULL;
}
double Area() const
{
if ( !m_Area )
{
m_Area = new double;
*m_Area = area( m_P1, m_P2, m_P3 );
}
return *m_Area;
}
void Split( tri & t1, tri & t2 )
{
point p4 = midpt( m_P2, m_P3 );
t1.SetPts( m_P1, m_P2, p4 );
t2.SetPts( m_P1, p4, m_P3 );
}
private:
point m_P1;
point m_P2;
point m_P3;
mutable double * m_Area;
};
class mesh
{
public:
double Area() const
{
double area = 0;
std::map<int,tri>::const_iterator it;
for (it=m_Triangles.begin(); it!=m_Triangles.end(); ++it)
{
area += it->second.Area();
}
return area;
}
std::map<int, tri> m_Triangles;
int KnownArea() const
{
int count = 0;
std::map<int,tri>::const_iterator it;
for (it=m_Triangles.begin(); it!=m_Triangles.end(); ++it)
{
if ( it->second.KnowsArea() ) count++;
}
return count;
}
void SplitIndx( int indx )
{
tri t1, t2;
m_Triangles[indx].Split( t1, t2 );
m_Triangles[indx] = t1;
m_Triangles[m_Triangles.size()+1] = t2;
}
int NumTri() const
{
return m_Triangles.size();
}
};
int main( void )
{
point p1( 0, 0, 0 );
point p2( 1, 0, 0 );
point p3( 0, 1, 0 );
point p4( 1, 1, 0 );
point p5( 3, 4, 0 );
tri t1( p1, p2, p3 );
tri t2( p1, p2, p4 );
tri t3( p1, p3, p4 );
tri t4( p1, p3, p5 );
tri t5( p1, p4, p5 );
mesh m;
m.m_Triangles[1] = t1;
m.m_Triangles[2] = t2;
m.m_Triangles[3] = t3;
m.m_Triangles[4] = t4;
m.m_Triangles[5] = t5;
printf( "Known areas before total %d of %d\n", m.KnownArea(), m.NumTri() );
double area = m.Area();
printf( "Total area is %f\n", area );
printf( "Known areas after total %d of %d\n", m.KnownArea(), m.NumTri() );
printf( "Splitting\n" );
m.SplitIndx( 3 );
printf( "Known areas before total %d of %d\n", m.KnownArea(), m.NumTri() );
area = m.Area();
printf( "Total area is %f\n", area );
printf( "Known areas after total %d of %d\n", m.KnownArea(), m.NumTri() );
return 0;
}
Compiled with:
clang++ -Wall -std=c++11 -stdlib=libc++ mwe.cpp -o mwe
Or:
g++ -Wall -std=c++11 mwe.cpp -o mwe
Valgrind output (from clang):
$ valgrind --track-origins=yes --leak-check=full ./mwe
==231996== Memcheck, a memory error detector
==231996== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==231996== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==231996== Command: ./mwe
==231996==
Known areas before total 0 of 5
Total area is 3.500000
Known areas after total 5 of 5
Splitting
Known areas before total 4 of 6
Total area is 3.500000
Known areas after total 6 of 6
==231996==
==231996== HEAP SUMMARY:
==231996== in use at exit: 8 bytes in 1 blocks
==231996== total heap usage: 14 allocs, 13 frees, 1,800 bytes allocated
==231996==
==231996== 8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==231996== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==231996== by 0x48E3BA7: operator new(unsigned long) (in /usr/lib/llvm-10/lib/libc++.so.1.0)
==231996== by 0x4028A8: tri::Area() const (in /home/ramcdona/Desktop/mwe)
==231996== by 0x401E57: mesh::Area() const (in /home/ramcdona/Desktop/mwe)
==231996== by 0x4017A9: main (in /home/ramcdona/Desktop/mwe)
==231996==
==231996== LEAK SUMMARY:
==231996== definitely lost: 8 bytes in 1 blocks
==231996== indirectly lost: 0 bytes in 0 blocks
==231996== possibly lost: 0 bytes in 0 blocks
==231996== still reachable: 0 bytes in 0 blocks
==231996== suppressed: 0 bytes in 0 blocks
==231996==
==231996== For lists of detected and suppressed errors, rerun with: -s
==231996== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Built with gcc, the Valgrind output was essentially the same.

As pointed out by #Jarod42, the assignment operator as written is the source of the leak.
The cache and mutable are all working as expected.
The corrected code should read:
tri & operator=( const tri & t )
{
if ( this != &t )
{
m_P1 = t.m_P1;
m_P2 = t.m_P2;
m_P3 = t.m_P3;
delete m_Area;
if ( t.m_Area )
{
m_Area = new double( *(t.m_Area) );
}
else
{
m_Area = NULL;
}
}
return *this;
}
The approach suggested by #TedLyngmo would also work. In fact, it would avoid these sorts of problems entirely. However, I was looking to understand why the existing code did not work.

One way to avoid making it mutable is to make it always point at the the data cache, which could be a std::optional<double>.
You'd then create and store a std::unique_ptr<std::optional<double>> that you keep for the tri object's lifetime.
Example:
#include <memory> // std::unique_ptr / std::make_unique
#include <optional> // std::optional
class tri {
public:
using cache_type = std::optional<double>;
tri() : m_Area(std::make_unique<cache_type>()) {} // create the cache
tri(const tri& rhs) : // copy constructor
m_Area(std::make_unique<cache_type>(*rhs.m_Area)),
m_P1(rhs.m_P1), m_P2(rhs.m_P2), m_P3(rhs.m_P3)
{}
tri(tri&&) noexcept = default; // move constructor
tri& operator=(const tri& rhs) { // copy assignment
m_Area = std::make_unique<cache_type>(*rhs.m_Area);
m_P1 = rhs.m_P1;
m_P2 = rhs.m_P2;
m_P3 = rhs.m_P3;
return *this;
}
tri& operator=(tri&& rhs) noexcept = default; // move assignment
// no user-defined destructor needed
void SetPts(const point& p1, const point& p2, const point& p3) {
m_P1 = p1;
m_P2 = p2;
m_P3 = p3;
m_Area->reset(); // the cache is not up to date anymore
}
double Area() const {
if(!*m_Area) *m_Area = CalcArea(); // set the cached value
return m_Area->value(); // return the stored value
}
private:
std::unique_ptr<cache_type> m_Area; // mutable not needed
point m_P1;
point m_P2;
point m_P3;
double CalcArea() const {
// the calculation
}
};

Related

Retrieving values from map <<object>, int> exhibits strange behavior

I am reproducing a problem I have with maps in a project, or which I implemented the following debugging code. I am having trouble retrieving the integer values in a map from Point2 objects as keys.
I have implemented the overloading of the Point2 objects as shown in the code. Point2 objects are ordered according to the value of _label.
The map _mapExtendedPointToIntersectionLabel gets its elements inserted successfully, but lines 116,120,124 and 128 behave strangely.They should just assign the value of the corresponding POint2 object to the corresponding variables.
-line 116 does an insertion into the map, as shown in the figure and label1 is set to 0 instead of getting its value 27;
-the other 3 lines just set the variables label2, label3 and label4 to 0 instead of to the values 17,47 and 37;
What is going on? If I am doing something wrong, how do I fix it to get the desired behavior?
class Point2
{
private:
double _coordinates[2];
int _label = -2;
public:
Point2(const double x = 0.0, const double y = 0.0){_coordinates[0]=x;_coordinates[1]=y;}
int getFirstCoordinate() const{ return _coordinates[0];}
int getSecondCoordinate() const{ return _coordinates[1];}
int getPointLabel() const{ return _label;}
void setPointLabel(const int label){_label =label;}
friend bool operator< (const Point2& p1, const Point2& p2 ){
return p1._label < p2._label;
}
Point2& operator =(const Point2 &point)
{
_coordinates[0] = point.getFirstCoordinate();
_coordinates[1] = point.getSecondCoordinate();
_label = point.getPointLabel();
return *this;
}
Point2(const Point2 &p1){
_coordinates[0]=p1._coordinates[0];
_coordinates[1]=p1._coordinates[1];
_label = p1._label;
}
};
int main(){
Point2 _intersectionVertices[4];
_intersectionVertices[0]= Point2(10,20);
_intersectionVertices[1]= Point2(11,21);
_intersectionVertices[2]= Point2(12,21);
_intersectionVertices[3]= Point2(13,31);
Point2 p1 = Point2(10,20);
Point2 p2 = Point2(11,21);
Point2 p3 = Point2(12,31);
Point2 p4 = Point2(13,41);
p1.setPointLabel(1);
p2.setPointLabel(2);
p3.setPointLabel(3);
p4.setPointLabel(4);
int intersectionId ;
map<Point2, int>_mapExtendedPointToIntersectionLabel;
_mapExtendedPointToIntersectionLabel.insert(pair<Point2, int>(p1,27));
_mapExtendedPointToIntersectionLabel.insert(pair<Point2, int>(p2,17));
_mapExtendedPointToIntersectionLabel.insert(pair<Point2, int>(p3,47));
_mapExtendedPointToIntersectionLabel.insert(pair<Point2, int>(p4,37));
intersectionId = 0;
int label1 = _mapExtendedPointToIntersectionLabel[_intersectionVertices[intersectionId]]; //line 116 //I placed a debug point here
cout <<"Label1 is"<<label1<<endl;
intersectionId = 1;
int label2 = _mapExtendedPointToIntersectionLabel[_intersectionVertices[intersectionId]];//line 120
cout <<"Label2 is"<<label2<<endl;
intersectionId = 2;
int label3 = _mapExtendedPointToIntersectionLabel[_intersectionVertices[intersectionId]];//line 124
cout <<"Label3 is"<<label3<<endl;
intersectionId = 3;
int label4 = _mapExtendedPointToIntersectionLabel[_intersectionVertices[intersectionId]];//line 128
cout <<"Label4 is"<<label4<<endl;
cout<<"the end"<<endl;
return 0;
}
Before line 116:
After line 116:
friend bool operator< (const Point2& p1, const Point2& p2 )
{
return p1._label < p2._label;
}
The defined strict weak ordering of this custom class, that's used as a key, in the map, compares each class instance's _label value in order to determine thir strict weak ordering.
The shown code initializes p1 through p4's _labels to unique values, so they are correctly compared for uniqueness.
But for the map lookup is concerned, it uses the instances in the _intersectionVertices array, which all have their _labels at their default values of -2.
As far as the map is concerned, none of those values, from the _intersectionVertices array, are in the map. Because to determine that, only their _labels get compared, for the reasons explained above.

push back multiple types of data in a vector in c++

Let's say I have a vector full of points like this:
vector<Point3f> cluster_points
Now I am getting distance between 2 points for each point in the vector. I want to store all these data in a container like below:
{distance, (both point's index number from *cluster_points*)}
e.g.
{70.54, (0,1)};
{98.485, (1,2)};
{87.565, (2,3)};
{107.54, (3,4)};
How can I do this in C++11?
In C++14:
struct DistanceBetweenPoints
{
double distance = {};
size_t p1 = {};
size_t p2 = {};
};
std::vector<DistanceBetweenPoints> foo;
foo.push_back({ 70.54, 0, 1 });
//...
EDIT
Just like Khouri Giordano noted in the comments section, this isn't supported in C++11 because when using in-class initialization, it becomes a non-POD type and you lose aggregate construction. See his answer for C++11 compatible solutions.
Store the data in a std::vector<std::tuple<float, std::pair<size_t, size_t>>>.
No need for 5 or 20 or 50 new lines of code.
Make a structure to hold the things you want to hold. Give it appropriate constructors.
struct distance_indexes_t
{
double distance;
size_t p1, p2;
distance_indexes_t()
: distance(), p1(), p2()
{}
distance_indexes_t( double distance, size_t p1, size_t p2 )
: distance( distance ), p1( p1 ), p2( p2 )
{}
};
distance_indexes_t di1; // zeros
distance_indexes_t di2{ 3.5, 4, 5 };
OR
struct distance_indexes_t
{
double distance = 0;
size_t p1 = 0, p2 = 0;
distance_indexes_t() = default; // Not necessary, but nice for clarity.
distance_indexes_t( double distance, size_t p1, size_t p2 )
: distance( distance ), p1( p1 ), p2( p2 )
{}
};
distance_indexes_t di1; // zeros
distance_indexes_t di2{ 3.5, 4, 5 };
Both will use the default copy constructor and assignment operator. Move constructor and move operator don't matter here, but you'll get those defaults too.
Alternatively, for C++14:
struct distance_indexes_t
{
double distance = 0;
size_t p1 = 0, p2 = 0;
};
distance_indexes_t di1; // zeros
distance_indexes_t di2{ 3.5, 4, 5 };
Alternatively, for C++03 and earlier:
struct distance_indexes_t
{
double distance;
size_t p1, p2;
};
distance_indexes_t di1; // random content
distance_indexes_t di2{ 3.5, 4, 5 };

Spherical coordinate representation (using Ogre maths constructs) fails tests

In my project I need to position things around other things in a spherical way, so I figured I needed a spherical coordinate representation. I'm using Ogre3D graphic rendering engine which provide some maths constructs which at the moment I use everywhere in my project.
The source code of Ogre is available there, in the 1.9 branch as I'm using an old version of it. I checked yesterday that the maths types didn't change in a long time so my code is relying on the code you can see there, in particular Maths, Vector3 and Quaternion. Here I'm assuming that Ogre's code is correct, but you never know, which is why I'm pointing to it as my code is relying heavily on it. Also know that the referential used in Ogre is right handed, with -Z being the default orientation of any entity, +Y being up of the screen, +X being the right side of the screen, and +Z being the direction toward you, -Z being the direction toward the screen.
Now, here is the spherical coordinate code I have a problem with:
#ifndef HGUARD_NETRUSH_ZONEVIEW_SPHERICAL_HPP__
#define HGUARD_NETRUSH_ZONEVIEW_SPHERICAL_HPP__
#include <iosfwd>
#include <OgreMath.h>
#include <OgreVector3.h>
#include <OgreQuaternion.h>
#include "api.hpp"
namespace netrush {
namespace zoneview {
/** Spherical coordinates vector, used for spherical coordinates and transformations.
Some example values:
( radius = 1.0, theta = 0.0deg , phi = 0.0deg ) <=> Y unit vector in cartesian space
( radius = 1.0, theta = 90.0deg, phi = 0.0deg ) <=> Z unit vector in cartesian space
( radius = 1.0, theta = 90.0deg , phi = 90.0deg ) <=> X unit vector in cartesian space
*/
struct SphereVector
{
Ogre::Real radius; ///< Rho or Radius is the distance from the center of the sphere.
Ogre::Radian theta; ///< Theta is the angle around the x axis (latitude angle counterclockwise), values range from 0 to PI.
Ogre::Radian phi; ///< Phi is the angle around the y axis (longitude angle counterclockwise), values range from 0 to 2PI.
NETRUSH_ZONEVIEW_API static const SphereVector ZERO;
NETRUSH_ZONEVIEW_API static const SphereVector UNIT_X;
NETRUSH_ZONEVIEW_API static const SphereVector UNIT_Y;
NETRUSH_ZONEVIEW_API static const SphereVector UNIT_Z;
NETRUSH_ZONEVIEW_API static const SphereVector NEGATIVE_UNIT_X;
NETRUSH_ZONEVIEW_API static const SphereVector NEGATIVE_UNIT_Y;
NETRUSH_ZONEVIEW_API static const SphereVector NEGATIVE_UNIT_Z;
SphereVector() = default;
SphereVector( Ogre::Real radius, Ogre::Radian theta, Ogre::Radian phi )
: radius( std::move(radius) ), theta( std::move(theta) ), phi( std::move(phi) )
{}
explicit SphereVector( const Ogre::Vector3& cartesian_vec )
{
*this = from_cartesian( cartesian_vec );
}
void normalize()
{
using namespace Ogre;
while( phi > Degree(360.f) ) phi -= Degree(360.f);
while( theta > Degree(180.f) ) theta -= Degree(180.f);
while( phi < Radian(0) ) phi += Degree(360.f);
while( theta < Radian(0) ) theta += Degree(180.f);
}
SphereVector normalized() const
{
SphereVector svec{*this};
svec.normalize();
return svec;
}
/** #return a relative Cartesian vector coordinate from this relative spherical coordinate. */
Ogre::Vector3 to_cartesian() const
{
using namespace Ogre;
const auto svec = normalized();
Vector3 result;
result.x = radius * Math::Sin( svec.phi ) * Math::Sin( svec.theta );
result.z = radius * Math::Cos( svec.phi ) * Math::Sin( svec.theta );
result.y = radius * Math::Cos( svec.theta );
return result;
}
/** #return a relative spherical coordinate from a cartesian vector. */
static SphereVector from_cartesian( const Ogre::Vector3& cartesian )
{
using namespace Ogre;
SphereVector result = SphereVector::ZERO;
result.radius = cartesian.length();
if( result.radius == 0 )
return result;
result.phi = Math::ATan2( cartesian.x, cartesian.z );
result.theta = Math::ATan2( Math::Sqrt( Math::Sqr( cartesian.x ) + Math::Sqr( cartesian.z ) ), cartesian.y );
result.normalize();
return result;
}
friend SphereVector operator-( const SphereVector& value )
{
SphereVector result;
result.radius = -value.radius;
result.theta = -value.theta;
result.phi = -value.phi;
return result;
}
friend SphereVector operator+( const SphereVector& left, const SphereVector& right )
{
SphereVector result;
result.radius = left.radius + right.radius;
result.theta = left.theta + right.theta;
result.phi = left.phi + right.phi;
return result;
}
friend SphereVector operator-( const SphereVector& left, const SphereVector& right )
{
return left + (-right);
}
SphereVector& operator+=( const SphereVector& other )
{
*this = *this + other;
return *this;
}
SphereVector& operator-=( const SphereVector& other )
{
*this = *this - other;
return *this;
}
/// Rotation of the position around the relative center of the sphere.
friend SphereVector operator*( const SphereVector& sv, const Ogre::Quaternion& rotation )
{
const auto cartesian_vec = sv.to_cartesian();
const auto rotated_vec = rotation * cartesian_vec;
SphereVector result { rotated_vec };
result.normalize();
return result;
}
/// Rotation of the position around the relative center of the sphere.
friend SphereVector operator*( const Ogre::Quaternion& rotation, const SphereVector& sv ) { return sv * rotation; }
/// Rotation of the position around the relative center of the sphere.
SphereVector& operator*=( const Ogre::Quaternion& rotation )
{
*this = *this * rotation;
return *this;
}
friend bool operator==( const SphereVector& left, const SphereVector& right )
{
return Ogre::Math::RealEqual( left.radius, right.radius )
&& left.phi == right.phi
&& left.theta == right.theta
;
}
friend bool operator!=( const SphereVector& left, const SphereVector& right )
{
return !( left == right );
}
};
inline std::ostream& operator<<( std::ostream& out, const SphereVector& svec )
{
out << "{ radius = " << svec.radius
<< ", theta = " << svec.theta
<< ", phi = " << svec.phi
<< " }";
return out;
}
inline bool real_equals( const SphereVector& left, const SphereVector& right, Ogre::Real tolerance = 1e-03 )
{
using namespace Ogre;
return Math::RealEqual( left.radius, right.radius, tolerance )
&& Math::RealEqual( left.theta.valueAngleUnits(), right.theta.valueAngleUnits(), tolerance )
&& Math::RealEqual( left.phi.valueAngleUnits(), right.phi.valueAngleUnits(), tolerance )
;
}
}}
#endif
The constants are defined in the cpp:
#include "spherical.hpp"
namespace netrush {
namespace zoneview {
const SphereVector SphereVector::ZERO( 0.f, Ogre::Radian( 0.f ), Ogre::Radian( 0.f ) );
const SphereVector SphereVector::UNIT_X( Ogre::Vector3::UNIT_X );
const SphereVector SphereVector::UNIT_Y( Ogre::Vector3::UNIT_Y );
const SphereVector SphereVector::UNIT_Z( Ogre::Vector3::UNIT_Z );
const SphereVector SphereVector::NEGATIVE_UNIT_X( Ogre::Vector3::NEGATIVE_UNIT_X );
const SphereVector SphereVector::NEGATIVE_UNIT_Y( Ogre::Vector3::NEGATIVE_UNIT_Y );
const SphereVector SphereVector::NEGATIVE_UNIT_Z( Ogre::Vector3::NEGATIVE_UNIT_Z );
}}
The failing test (the lines marked ast FAILURE):
inline bool check_compare( SphereVector left, SphereVector right )
{
std::cout << "----"
<< "\nComparing "
<< "\n Left: " << left
<< "\n Right: " << right
<< std::endl;
return real_equals( left, right );
}
// ...
TEST( Test_SphereVector, axe_rotation_quaternion )
{
using namespace Ogre;
const auto init_svec = SphereVector::NEGATIVE_UNIT_Z;
static const auto ROTATION_TO_X = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::UNIT_X );
static const auto ROTATION_TO_Y = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::UNIT_Y );
static const auto ROTATION_TO_Z = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::UNIT_Z );
static const auto ROTATION_TO_NEGATIVE_X = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::NEGATIVE_UNIT_X );
static const auto ROTATION_TO_NEGATIVE_Y = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::NEGATIVE_UNIT_Y );
static const auto ROTATION_TO_NEGATIVE_Z = Vector3::NEGATIVE_UNIT_Z.getRotationTo( Vector3::NEGATIVE_UNIT_Z );
static const auto ROTATION_360 = ROTATION_TO_Z * 2;
const auto svec_x = init_svec * ROTATION_TO_X;
const auto svec_y = init_svec * ROTATION_TO_Y;
const auto svec_z = init_svec * ROTATION_TO_Z;
const auto svec_nx = init_svec * ROTATION_TO_NEGATIVE_X;
const auto svec_ny = init_svec * ROTATION_TO_NEGATIVE_Y;
const auto svec_nz = init_svec * ROTATION_TO_NEGATIVE_Z;
const auto svec_360 = init_svec * ROTATION_360;
EXPECT_TRUE( check_compare( svec_x.to_cartesian() , Vector3::UNIT_X ) );
EXPECT_TRUE( check_compare( svec_y.to_cartesian() , Vector3::UNIT_Y ) );
EXPECT_TRUE( check_compare( svec_z.to_cartesian() , Vector3::UNIT_Z ) );
EXPECT_TRUE( check_compare( svec_nx.to_cartesian() , Vector3::NEGATIVE_UNIT_X ) );
EXPECT_TRUE( check_compare( svec_ny.to_cartesian() , Vector3::NEGATIVE_UNIT_Y ) );
EXPECT_TRUE( check_compare( svec_nz.to_cartesian() , Vector3::NEGATIVE_UNIT_Z ) );
EXPECT_TRUE( check_compare( svec_360.to_cartesian(), Vector3::NEGATIVE_UNIT_Z ) ); // FAILURE 1
EXPECT_TRUE( check_compare( svec_x , SphereVector::UNIT_X ) );
EXPECT_TRUE( check_compare( svec_y , SphereVector::UNIT_Y ) ); // FAILURE 2
EXPECT_TRUE( check_compare( svec_z , SphereVector::UNIT_Z ) );
EXPECT_TRUE( check_compare( svec_nx , SphereVector::NEGATIVE_UNIT_X ) );
EXPECT_TRUE( check_compare( svec_ny , SphereVector::NEGATIVE_UNIT_Y ) ); // FAILURE 3
EXPECT_TRUE( check_compare( svec_nz , SphereVector::NEGATIVE_UNIT_Z ) );
EXPECT_TRUE( check_compare( svec_360, SphereVector::NEGATIVE_UNIT_Z ) ); // FAILURE 4
}
Excerpt from the test report:
Failure 1:
4> ----
4> Comparing
4> Left: Vector3(9.61651e-007, -3.0598e-007, 7)
4> Right: Vector3(0, 0, -1)
4>e:\projects\games\netrush\netrush_projects\projects\netrush\zoneview\tests\spherevector.cpp(210): error : Value of: check_compare( svec_360.to_cartesian(), Vector3::NEGATIVE_UNIT_Z )
4> Actual: false
4> Expected: true
Failure 2:
4> ----
4> Comparing
4> Left: { radius = 1, theta = Radian(1.4783e-007), phi = Radian(5.65042) }
4> Right: { radius = 1, theta = Radian(0), phi = Radian(0) }
4>e:\projects\games\netrush\netrush_projects\projects\netrush\zoneview\tests\spherevector.cpp(213): error : Value of: check_compare( svec_y , SphereVector::UNIT_Y )
4> Actual: false
4> Expected: true
Failure 3:
4> ----
4> Comparing
4> Left: { radius = 1, theta = Radian(3.14159), phi = Radian(5.82845) }
4> Right: { radius = 1, theta = Radian(3.14159), phi = Radian(0) }
4>e:\projects\games\netrush\netrush_projects\projects\netrush\zoneview\tests\spherevector.cpp(216): error : Value of: check_compare( svec_ny , SphereVector::NEGATIVE_UNIT_Y )
4> Actual: false
4> Expected: true
Failure 4:
4> Comparing
4> Left: { radius = 7, theta = Radian(1.5708), phi = Radian(1.37379e-007) }
4> Right: { radius = 1, theta = Radian(1.5708), phi = Radian(3.14159) }
4>e:\projects\games\netrush\netrush_projects\projects\netrush\zoneview\tests\spherevector.cpp(218): error : Value of: check_compare( svec_360, SphereVector::NEGATIVE_UNIT_Z )
4> Actual: false
4> Expected: true
The same code on gist: https://gist.github.com/Klaim/8633895 with constants defined there: https://gist.github.com/Klaim/8634224
Full tests (using GTest): https://gist.github.com/Klaim/8633917
Full test report: https://gist.github.com/Klaim/8633937
(I can't put it here because of the text size limitation)
As you can see in the error report, there is 4 errors. I just can't find a solution for these, so maybe someone here could point me to what I'm doing wrong. I believe the problem could be from the test itself, but I'm not sure at all. Also, note that there are tests missing that I plan to add. The
The api.hpp include only expose the macros for shared library symbol export/import, used for constants.
This code is supposed to be extracted to be provided as a separate small open source library.
What I'm asking is: is this code incorrect? Or is my test incorrect?

Error Passing `const` as `this` argument of `const double` discards qualifiers

I have to work within some libraries and no matter what i do i keep getting the following error with this code.
passing `const amko::problem::launch' as 'this'argument of 'const double amko::problem::launch::ratio(double, double)' discards qualifiers
namespace amko { namespace problem {
launch::launch():base( 0.0, 20.0, 1 ) {}
base_ptr launch::clone() const
{
return base_ptr(new launch(*this));
}
const double launch::ratio( const double a, const double b)
{
const double area = a*b;
const double circumference = 2*a+2*b;
const double ratio = circumference/area;
return ratio;
}
void launch::objfun_impl(fitness_vector &f, const decision_vector &xv) const
{
amko_assert(f.size() == 1 && xv.size() == get_dimension());
const double x = xv[0];
const double y = launch::ratio(x,5);
f[0] = y;
}
while the following piece of code worked just fine.
namespace amko { namespace problem {
initialValueProblem::initialValueProblem():base( 0.0, 20.0, 1 ) {}
base_ptr initialValueProblem::clone() const
{
return base_ptr(new initialValueProblem(*this));
}
Eigen::VectorXd initialValueProblem::computeDerivative( const double time, const Eigen::VectorXd& state )
{
Eigen::VectorXd stateDerivative( 1 );
stateDerivative( 0 ) = state( 0 ) - std::pow( time, 2.0 ) + 1.0;
return stateDerivative;
}
void initialValueProblem::objfun_impl(fitness_vector &f, const decision_vector &xv) const
{
amko_assert(f.size() == 1 && xv.size() == get_dimension());
const double x = xv[0];
double intervalStart = 0.0;
double intervalEnd = 10.0;
double stepSize = 0.1;
Eigen::VectorXd initialState_;
initialState_.setZero( 1 );
initialState_( 0 ) = x;
numerical_integrators::EulerIntegratorXd integrator( boost::bind( &initialValueProblem::computeDerivative,
const_cast<initialValueProblem*>( this ), _1, _2 ), intervalStart, initialState_ );
Eigen::VectorXd finalState = integrator.integrateTo( intervalEnd, stepSize );
f[0] = fabs( finalState( 0 ) - 11009.9937484598 );
}
Thank you!
launch::objfun_impl is a const member function, it cannot change members or call other functions that do. That means it can't call non-const non-static member functions such as launch::ratio.
Because launch::ratio doesn't appear to access members at all, just its arguments, the simplest fix is to make it a static member function by changing the prototype inside the class definition:
static /* <- ADDED static HERE */ double launch::ratio(const double a, const double b);
The problem is that your ratio member function is not const, even though you are not modifying any member of the object (why is it a member function at all?). Inside objfun_impl you are calling ratio. Now, objfun_impl is const, and thus promises not to modify the object, but calling ratio would break that promise.

Vector of pointers problem

I am having quite a bit of trouble with trying to push_back an object of my custom class to a vector of pointers with my custom class as the type. Please see the code below along with the error received. I am using Eclipse with the CDT plugin and OpenCV on windows xp.
I have spent so much time trying to find an answer but to no avail!
ps I am a student and pointers etc are not my thing!
std:: vector<RoadLine>* LaneChangeDetector::roadLines(IplImage* img_8uc1, IplImage* img_8uc3, IplImage* img_edge, std::vector <RoadLine>* roadVector){
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines = 0;
CvMemStorage* roadStorage = cvCreateMemStorage(0);
CvSeq* roadLines = 0;
// Probabalistic Hough transform returns line segments from edge detected image
lines = cvHoughLines2( img_edge, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 200, 200 );
// Sequence roadlines, lines with correct slope are added to this sequence
roadLines = cvCreateSeq(0, lines->header_size, lines->elem_size, roadStorage);
// slope
double m = 0.0;
// Point of intersection
CvPoint poi;
for(int i = 0; i < lines->total; i++ ){
CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
CvPoint pt1 = line[0];
CvPoint pt2 = line[1];
double x1 = double(pt1.x);
double y1 = double(pt1.y);
double x2 = double(pt2.x);
double y2 = double(pt2.y);
if(pt1.x == pt2.x){
m = 1.0;
}
else{
m = (double(y2 - y1)/(double(x2 - x1)));
}
if( ((m>0.45) && (m<0.75)) || ((m<-0.45) && (m>-0.75)) ){
// If the slope is between measured parameters add to roadLines sequence for further analysis
cvSeqPush(roadLines, line);
}
}
// otherRoadLine used for comparison
CvPoint* otherRoadLine;
for(int a=0; a<roadLines->total; a++){
CvPoint* roadLine = (CvPoint*)cvGetSeqElem(roadLines,a);
CvPoint rl1 = roadLine[0];
CvPoint rl2 = roadLine[1];
int lineCount = 0;
if(a>0){
// Test the current line against all the previous lines in the sequence.
// If the current line is far enough away from all other lines then draw it
for(int b=0; b<a; b++){
otherRoadLine = (CvPoint*)cvGetSeqElem(roadLines,b);
if((roadLine->x > ((otherRoadLine->x) + 200)) || (roadLine->x < ((otherRoadLine->x) - 200)) ){
lineCount++;
}
}
if(lineCount == a){
cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
RoadLine myLine = RoadLine(roadLine, 1);
roadVector->push_back(myLine); //ERROR OCCURS HERE
cvShowImage("Plate Detection", img_final);
cvWaitKey(0);
}
}
else{
cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
RoadLine myLine = RoadLine(roadLine, 1);
roadVector->push_back(myLine //ERROR OCCURS HERE
cvShowImage("Plate Detection", img_final);
cvWaitKey(0);
}
}
if(roadVector->size() >= 2){
int pos = 0;
RoadLine line1 = roadVector->at(pos);
RoadLine line2 = roadVector->at(pos + 1);
CvPoint* A = line1.line;
CvPoint p1 = A[0];
CvPoint p2 = A[1];
int A1 = p1.y - p2.y;
int B1 = p1.x - p2.x;
int C1 = (p1.x*p2.y) - (p1.y*p2.x);
CvPoint* B = line2.line;
CvPoint p3 = B[0];
CvPoint p4 = B[1];
int A2 = p3.y - p4.y;
int B2 = p3.x - p4.x;
int C2 = (p3.x*p4.y) - (p3.y*p4.x);
int det = A2*B1 - A1*B2;
if(det == 0){
printf("Lines are parallel");
}
else{
int x = ( C1*(p3.x - p4.x) - (p1.x - p2.x)*C2 )/det;
int y = ( C1*(p3.y - p4.y) - (p1.y - p2.y)*C2 )/det;
poi.x = x;
poi.y = y;
horizon = poi.x;
cvCircle(img_final, poi, 10, CV_RGB(255, 0, 0), 2, CV_AA, 0);
}
}
cvShowImage("Plate Detection", img_final);
cvWaitKey(0);
return roadVector;
}
The custom class RoadLine can be seen here
#include <cv.h>
class RoadLine{
private:
CvPoint* line;
int lane;
public:
RoadLine(CvPoint*, int);
};
RoadLine::RoadLine(CvPoint* aLine, int aLane){
line = aLine;
lane = aLane;
}
From debugging i can see that "std::vector <RoadLine>* roadVector" is being intialised correctly.
Here is what Eclipse tells me:
3 std::vector<RoadLine, std::allocator<RoadLine> >::push_back() F:\MinGW\include\c++\3.4.5\bits\stl_vector.h:560 0x0043e3f9
4 void std::_Construct<RoadLine, RoadLine>() F:\MinGW\include\c++\3.4.5\bits\stl_construct.h:81 0x0044015d
And the program jumps to this section of code in stl_construct.h
template<typename _T1, typename _T2>
inline void
_Construct(_T1* __p, const _T2& __value)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_]allocator::construct
::new(static_cast<void*>(__p)) _T1(__value); //DEBUG THROWS ME TO THIS LINE
}
Again any help would be greatly appreciated.
Cheers
Pat
You do not use vector of pointers, but vector of objects. In that case, your class needs to have a copy constructor, as push_back stores a copy of object.
As a general debugging advice, try to boil down the problem by removing as much code as you can and still see incorrect behaviour. Try to find the simplest example that fails.
Your new RoadLine class will certainly lead to disaster :
RoadLine::RoadLine(CvPoint* aLine, int aLane){
line = aLine;
lane = aLane;
}
RoadLine::RoadLine(const RoadLine & myRoadLine){
line = myRoadLine.line;
lane = 1;
}
RoadLine::~RoadLine(){
delete line;
}
code using it :
if(lineCount == a){
cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
RoadLine myLine = RoadLine(roadLine, 1);//create object on the Stack
roadVector->push_back(myLine); //Push COPY of myLine
cvShowImage("Plate Detection", img_final);
cvWaitKey(0);
}//Stack-based object "myLine" is automatically destroyed here (leaves scope)
the automatic destruction of "myLine" will delete "myLine.line" (in RoadLine's dtor)
but "myLine.line" is still referenced in the vector (you just pushed it).
You have to either make a DEEP COPY of line (as others suggested), something like this :
RoadLine::RoadLine(const RoadLine & myRoadLine){
line = new CvPoint(*myRoadLine.line);//assuming CvPoint can be copy-constructed
lane = 1;
}
Or use a CvLine object rather than a pointer (or something else, need more context)
EDIT :
Dirk Gently's copy-ctorhas a bug, because it leaks memory to the former "line"-member
should be :
RoadLine& operator=(const RoadLine & o){
if (this != &o) { //Remember to check for self-assignment.
delete []line;//delete[] vs. delete !
line = 0;//if next line throws at least we won't double-delete line
line = new CvPoint[ 2 ]; //this might throw ! should catch (or redesign to get rid of new (prefered)
line[ 0 ] = o.line[ 0 ];
line[ 1 ] = o.line[ 1 ];
lane = o.lane;
}
return *this;
}
//consistent constructor !
RoadLine::RoadLine(CvPoint* aLine, int aLane)
:line(new CvPoint[2]),//might throw, but its better to throw in initializer ! (if you just have one pointer it might be ok to do it like this)
lane(aLane)
{
line[0] = aLine[0];
line[1] = aLine[1];
}
RoadLine::~RoadLine(){
delete[] line;//also use delete[] vs. normal delete here !
}
EDIT 2 : I almost forgot that I had an idea why it crashes ! maybe you try to build a pair with last and last+1 CvPoint (like this obviously false code)?
CvPoint Pnts[2] = {CvPoint(0,0),CvPoint(1,1)};
Roadline Line(&Pnts[1],1);//tries to access Pnts[2] which is one past end !
Your RoadLine class lacks a proper copy-ctor. Now, since you have a member that points to a CvPoint object you have create a copy of the pointer every time you push_back. This is probably not desirable.
RoadLine::RoadLine(const RoadLine & o){
line = new CvPoint[ 2 ];
line[ 0 ] = o.line[ 0 ];
line[ 1 ] = o.line[ 1 ];
lane = o.lane;
}
RoadLine& operator=(const RoadLine & o){
if (this != &o) { //Remember to check for self-assignment.
line = new CvPoint[ 2 ];
line[ 0 ] = o.line[ 0 ];
line[ 1 ] = o.line[ 1 ];
lane = o.lane;
}
return *this;
}
Shorten your code: Try to isolate the problem:
int main() {
CvPoint pa[] = { CvPoint(0, 0), CvPoint(100, 100) };
RoadLine rl1(pa, 1);
vector<RoadLine> v;
v.push_back(rl1);
return 0;
}
Does this crash?
The trick with C++ is to imagine the "~" key as big and red and that alarm bells will sound whenever you press it, ie. whenever you're thinking of adding a destructor to a class.
If you're adding a destructor then you NEED a copy constructor and assignment operator. No exceptions. Even if you're not going to copy the object you should still declare them in the private section so the compiler will give errors if they're used accidentally.
You should also use a reference counted pointer instead of a raw C-style pointer whenever the lifetime of an object is being controlled (in C++-speak this is "RAII"). If you did this the destructor would vanish from RoadLine, and, magically, so would your problem.
You don't have a vector of pointers.
std::vector<RoadLine>* roadVector
is a pointer to a vector of RoadLine objects. If you want a vector of pointers, you should do:
std::vector<RoadLine*> roadVector
That may help you (since the vector won't be invoking copy constructors any more), but you should still look at sorting those out as others have suggested.
These kinds of errors are usually caused by incorrect memory-management. Sadly, you haven't posted the way how do you manage your memory.
If you can get it run on a linux system, you can try running your program under valgrind, which helps to track down incorrect memory accesses/freeing. Unfortunately, valgrind is not available under windows, but there may be substitutes.
i have changed my class definition of RoadLine to:
#include <cv.h>
class RoadLine{
private:
int lane;
public:
CvPoint* line;
RoadLine(CvPoint*, int);
RoadLine(const RoadLine &);
~RoadLine();
RoadLine& operator=(const RoadLine & o);
};
RoadLine::RoadLine(CvPoint* aLine, int aLane){
line = aLine;
lane = aLane;
}
RoadLine::RoadLine(const RoadLine & myRoadLine){
line = new CvPoint[ 2 ]; // CRASHES HERE
line[ 0 ] = myRoadLine.line[ 0 ];
line[ 1 ] = myRoadLine.line[ 1 ];
//line = new CvPoint(*myRoadLine.line);
lane = myRoadLine.lane;
}
RoadLine::~RoadLine(){
delete line;
}
RoadLine& RoadLine::operator=(const RoadLine & o){
if (this != &o) { //Remember to check for self-assignment.
line = new CvPoint[ 2 ];
line[ 0 ] = o.line[ 0 ];
line[ 1 ] = o.line[ 1 ];
lane = o.lane;
}
return *this;
}
This is the current version of the RoadLine class
This is how I am implementing the class:
else{
cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
RoadLine myLine(roadLine, 1);
roadVector->push_back(myLine); // FROM HERE
cvShowImage("Plate Detection", img_final);
cvWaitKey(0);
}
When push_back is called it calls the copy constructor but the program crashes where highlighted above
What difference is made by the fact that my vector is defined;
std::vector<RoadLine>* roadVector
and that i have a CvPoint* not CvPoint[]
sorry if these seem very basic questions