|
Boost Users : |
Subject: Re: [Boost-users] boost::iterator_property_map causes run-time fatal error in debug mode on VS2008
From: Artyom Arabadji (sneg.vx_at_[hidden])
Date: 2009-05-22 04:35:30
Dmitry Bufistov wrote:
> [Snip a problem with iterator_property_map]
>> I am doing something wrong? I can provide more information if this
>> isn't enough.
>>
>> Arty
>
> Hi Arty,
>
> A complete test case that reproduces the problem would be helpful.
>
> Dmitry
Hi Dmitry,
It took me a while to actually create one. I forgot to mention that I
use property_map in conjunction with BGL. Well, when I used a simple
graph that had no properties attached to vertices/edges the problem
didn't appear. On the other hand, once I added bundled properties (i.e.
structures that represent vertices and edges), I managed to reproduce
the same error:
vector, line 160.
Assertion Failed: Expression("this->_Has_container()", 0)
This corresponds to the earlier call to a function on line 351 in
property_map.hpp:
inline R operator[](key_type v) const { return *(iter + get(index, v)) ; }
Full stack frame from this call is:
////////////////////////
std::_Vector_const_iterator<unsigned int,std::allocator<unsigned int>
>::operator+=(int _Off=47) Line 160
std::_Vector_iterator<unsigned int,std::allocator<unsigned int>
>::operator+=(int _Off=47) Line 376
std::_Vector_iterator<unsigned int,std::allocator<unsigned int>
>::operator+(int _Off=47) Line 382
>
boost::iterator_property_map<std::_Vector_iterator<unsigned
int,std::allocator<unsigned int>
>,boost::vec_adj_list_vertex_id_map<boost::property<enum
boost::vertex_bundle_t,Clip_t,boost::no_property>,unsigned int>,unsigned
int,unsigned int &>::operator[](unsigned int v=47) Line 351
boost::put<boost::iterator_property_map<std::_Vector_iterator<unsigned
int,std::allocator<unsigned int>
>,boost::vec_adj_list_vertex_id_map<boost::property<enum
boost::vertex_bundle_t,Clip_t,boost::no_property>,unsigned int>,unsigned
int,unsigned int &>,unsigned int &,unsigned int,unsigned int>(const
boost::put_get_helper<unsigned int
&,boost::iterator_property_map<std::_Vector_iterator<unsigned
int,std::allocator<unsigned int>
>,boost::vec_adj_list_vertex_id_map<boost::property<enum
boost::vertex_bundle_t,Clip_t,boost::no_property>,unsigned int>,unsigned
int,unsigned int &> > & pa={...}, unsigned int k=47, const unsigned int
& v=27) Line 321
////////////////////////
This happens if program is compiled on VS2008 SP1 in DEBUG mode. If
compiled in Release mode, program works as expected with no errors
(atleast none that I can see). The program uses boost 1.38.0.
Arty
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <deque>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/visitors.hpp>
#include <boost/graph/graph_utility.hpp>
/**
* Singleton design pattern is used to
* to make an instance of a particular class unique.
*/
template< typename X > class Singleton
{
public:
inline static X& getInstance()
{
static X x;
return x;
}
protected:
Singleton( void ) { }
~Singleton( void ) { }
Singleton( const Singleton& ) { }
inline Singleton& operator = ( const Singleton& x )
{
return x;
}
};
/***
* @desc
* PathRecorder class is a template for creating
* path recorders which utilise visitor concepts
* in BGL. I.e. as a search algorithm runs,
* PathRecorder is called to fill in a "predecessor map"
* which records how to get to a specific vertex and stores
* edges with their targets as keys.
***/
template< class Graph, class Vertex, class Edge, class VertexIterator, class EdgeIterator > class PathRecorder
{
typedef typename boost::property_map< typename Graph, boost::vertex_index_t >::type VertexId_PMap;
typedef boost::iterator_property_map< typename std::vector< Vertex >::iterator, VertexId_PMap, typename Vertex, typename Vertex & > PredMap;
typedef boost::iterator_property_map< typename std::vector< Edge >::iterator, VertexId_PMap, typename Edge, typename Edge & > PredEdgeMap;
public:
/**
* @desc
* Update an entry inside a map
**/
void put( Vertex target, Vertex src )
{
boost::put( predMap, target, src );
}
/**
* @desc
* Update an entry inside a map
**/
void put( Vertex target, Edge e )
{
boost::put( predEdgeMap, target, e );
}
/**
* @desc
* Get predecessor (ancestor) of target
**/
Vertex getPredecessor( Vertex target ) const
{
return boost::get( predMap, target );
}
/**
* @desc
* Get Edge to target
**/
Edge getEdge( Vertex target ) const
{
return boost::get( predEdgeMap, target );
}
/**
* @desc
* Get edge from ancestor to sibling (if such edge exists)
**/
Edge PathRecorder :: getEdgeToSibling( Vertex ancestor ) const
{
VertexIterator i, end;
for( boost::tie( i, end ) = boost::vertices( g ); i != end; ++i )
{
Vertex pred = boost::get( predMap, *i );
if( pred == ancestor )
{
return boost::get( predEdgeMap, *i );
}
}
return Edge();
}
/***
* @desc reset path recorder for
* another search
***/
virtual void reset( void )
{
edges.assign( edges.size(), Edge() );
vertices.assign( vertices.size(), Vertex() );
}
protected:
PathRecorder( const Graph &graph ) : g( graph ),
vertices( std::vector< Vertex >( boost::num_vertices( g ) ) ),
edges( std::vector< Edge >( boost::num_vertices( g ) ) ),
vertex_id( boost::get( boost::vertex_index, g ) ),
predMap( vertices.begin(), vertex_id ),
predEdgeMap( edges.begin(), vertex_id )
{
}
const Graph g;
~PathRecorder( void ) { }
PathRecorder( const PathRecorder & ) { }
std::vector< Vertex > vertices;
std::vector< Edge > edges;
VertexId_PMap vertex_id;
PredMap predMap;
PredEdgeMap predEdgeMap;
};
/**
* @desc
* Terminator is a class which simply tells DFS
* to stop once the target vertex is reached.
**/
template< class Vertex, class Graph > struct Terminator
{
Terminator( Vertex v ) : destination( v )
{
}
bool operator()( Vertex v, const Graph &g )
{
return ( v == destination );
}
private:
const Vertex destination;
};
/***
* Vertex Structure (bundled properties)
* This is empty, but in original src code
* this has some data inside.
***/
struct Clip_t
{
Clip_t( void ) { }
};
/***
* Edge Structure (bundled properties)
* This is empty, but in original src code
* this has some data inside.
***/
struct Transition_t
{
Transition_t( void ) { }
Transition_t( double d ) : distance( d )
{
}
double distance;
};
/***
* Type defines for our graph ( Called MotionGraph )
***/
typedef boost::adjacency_list< boost::listS, boost::vecS, boost::bidirectionalS, Clip_t, Transition_t > MotionGraph_t;
typedef boost::graph_traits< MotionGraph_t >::vertex_iterator ClipIterator;
typedef boost::graph_traits< MotionGraph_t >::vertex_descriptor Clip;
typedef boost::graph_traits< MotionGraph_t >::out_edge_iterator TransitionIterator;
typedef boost::graph_traits< MotionGraph_t >::edge_descriptor Transition;
/***
* Singleton graph structure so we can access it anywhere we like
* getBase is a way of passing the graph to BGL algorithm templates
* if you pass this class directly templates will not compile due
* to constructors and destructors being protected.
***/
class MotionGraph : public Singleton< MotionGraph >, public MotionGraph_t
{
friend Singleton< MotionGraph >;
public:
MotionGraph_t *getBase( void )
{
return static_cast< MotionGraph_t * >( &getInstance() );
}
protected:
~MotionGraph() { }
MotionGraph() { }
MotionGraph( const MotionGraph & ) { }
};
/**
@desc
ForwardEdge is a visitor which tries to decide whether
to replace a newly discovered edge with the edge inside
a DFS tree (predecessor map).
Forward Edge is an edge (U,V) and both U, V have already
been discovered by DFS.
**/
struct TestForwardEdge : public boost::base_visitor< TestForwardEdge >
{
typedef boost::on_forward_or_cross_edge event_filter;
template < class Edge, class Graph > void operator()( Edge e, const Graph &g );
};
/**
@desc
PredRecorder adds a tree edge to predecessor map.
Tree edge is the first edge for pair of vertices
(U, V) that was ever discovered.
**/
struct TestPredRecorder : public boost::base_visitor< TestPredRecorder >
{
typedef boost::on_tree_edge event_filter;
template < class Edge, class Graph > void operator()( Edge e, const Graph& g );
};
/***
* Quick test implementation of PathRecorder interface.
***/
class TestPathRecorder : public PathRecorder< MotionGraph_t, Clip, Transition, ClipIterator, TransitionIterator >, public Singleton< TestPathRecorder >
{
friend class Singleton< TestPathRecorder >;
public:
template< typename Container > void search( Container &agenda, Clip start, Clip end )
{
std::vector< unsigned short > c( boost::num_vertices( g ) );
reset();
typedef boost::property_map< MotionGraph_t, boost::vertex_index_t >::type VertexId_PMap;
VertexId_PMap vertex_id = boost::get( boost::vertex_index, g );
typedef boost::iterator_property_map< std::vector< unsigned short >::iterator, VertexId_PMap, unsigned short, unsigned short & > ColourMap;
boost::depth_first_visit(
g,
start,
boost::make_dfs_visitor( boost::make_list( TestPredRecorder(), TestForwardEdge() ) ),
ColourMap( c.begin(), vertex_id ),
Terminator< Clip, MotionGraph_t >( end )
);
getPath( agenda, start, end );
}
template< typename Container > void getPath( Container &agenda, Clip start, Clip end ) const
{
Transition cur = boost::get( predEdgeMap, end );
while( cur.m_eproperty )
{
agenda.push_front( boost::target( cur, g ) );
agenda.push_front( boost::source( cur, g ) );
cur = boost::get( predEdgeMap, boost::source( cur, g ) );
}
if( agenda.empty() || agenda.front() != start )
{
agenda.clear();
}
}
protected:
TestPathRecorder( void ) : PathRecorder( *MotionGraph::getInstance().getBase() ) { }
~TestPathRecorder( void ) { }
TestPathRecorder( const TestPathRecorder &mpr ) : PathRecorder( mpr.g ) { }
};
/**** Definitions of methods for visitor classes declared before TestPathRecorder ****/
template < class Edge, class Graph > void TestPredRecorder :: operator()( Edge e, const Graph &g )
{
// simplified method, simply overwrite
TestPathRecorder::getInstance().put( boost::target( e, g ), boost::source( e, g ) );
TestPathRecorder::getInstance().put( boost::target( e, g ), e );
}
template < class Edge, class Graph > void TestForwardEdge :: operator()( Edge e, const Graph &g )
{
// simplified method, simply overwrite
TestPathRecorder::getInstance().put( boost::target( e, g ), boost::source( e, g ) );
TestPathRecorder::getInstance().put( boost::target( e, g ), e );
}
/******************************/
/*** Roll N-sided die ***/
int roll( int sides )
{
return rand() % sides;
}
/*** Roll N-sided die so that result is different to prev ***/
int rollUnique( int sides, int prev )
{
int r = roll( sides );
while( r == prev )
{
r = roll( sides );
}
return r;
}
/*** Choose a random src and target and attempt to find a path ***/
void randomTest( void )
{
std::deque< Clip > mg;
int start = rand() % boost::num_vertices( MotionGraph::getInstance() );
int end = rand() % boost::num_vertices( MotionGraph::getInstance() );
TestPathRecorder::getInstance().search( mg, start, end );
if( mg.empty() )
{
std::cout << "Destination unreachable" << std::endl;
return;
}
std::cout << "Path from " << start << " to " << end << ":" << std::endl;
for( int i = 0; i < mg.size(); ++i )
{
std::cout << mg.at( i ) << ( i != mg.size() - 1 ? " -> " : "\n" );
}
}
int main( void )
{
srand( time( NULL ) );
// randomly choose number of vertices
int vertices = roll( 500 );
// add vertices
for( int i = 0; i < vertices; ++i )
{
boost::add_vertex( Clip_t(), MotionGraph::getInstance() );
}
// for each vertex V choose a random number of edges and connect them to random vertices (excluding the V itself)
for( int i = 0; i < vertices; ++i )
{
int edges = roll( vertices - 1 );
for( int j = 0; j < edges; j++ )
{
int target = rollUnique( vertices, i );
boost::add_edge( i, target, Transition_t(), MotionGraph::getInstance() );
}
}
// run a random path finding test for half the number of vertices
for( int i = 0; i < vertices / 2; i++ )
{
randomTest();
}
return 0;
}
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net