|
Boost : |
From: JOAQUIN LOPEZ MU?Z (joaquin_at_[hidden])
Date: 2005-02-02 16:27:46
Hi Alexander,
----- Mensaje original -----
De: Alexander <rhjrjlbk2002_at_[hidden]>
Fecha: Miércoles, Febrero 2, 2005 8:38 pm
Asunto: [boost] Re: Help needed with BGL & multi_index_container
> Hi Joaquin and Jeremy,
>
> Below you may find latest version of my test program.
> This time it compiles, links and works :-). I have to
> say - it was not easy to make it working.
>
> The only trouble I have is clumsy code in templated
> ItemHolder::operator()(const P&p) and correspondent call
> to template ItemHolder::get_int(...). See my comments
> below.
>
> I'm looking for advice to make it nicer.
I think I've got something, please read below.
>
> Replacement of "const P& p" to "void*" breaks VS 7.1.
>
[...]
>
> > From: Joaquin M Lopez Munoz <joaquin <at> tid.es>
> > Date: Tue, 01 Feb 2005 22:51:12 +0000
> > MG::vertex_descriptor vd1 =
> > add_vertex( ItemHolder( 1, Item(1) ), mg );
>
> In this line following is happen:
>
> 1. adjacency_list allocates (with new) a new
> stored_vertex structure to store property
> element of type ItemHolder (yes, using my initialization
> value), fields to store incoming, outgoing edges and
> other fields.
>
> 2. This pointer is cast to a void *, to be used as
> the new vertex descriptor.
>
> 3. The void * is fed to the multi_index_container used
> for vertex storage.
>
> 4. Now, inside ItemHolderKey, that "void *" must be converted to back
> to stored_vertex type, defined in base class of adajcent_list. And
> thisI find as a real threat to break everything. To my regret, I
> can't spend
> more
> time - I have to put all that into my project.
>
> > Well, all in all I must say this is a glorious hack :)
>
> Thank you! Indeed, you did helped me.
>
> > Probably, the only sensible approach is to write from
> > scratch a graph-compliant structure that uses
>
> I already did that couple of times. I do not want that anymore.
> Writing, testing, documenting, helping colleques to numerous
> suboptimal versions of the same component, scattered through
> number of versions of different products... No, thank you.
>
> > That said, the thing seems to work and may suffice
> > for your purposes.
>
> I see that as a quite general use case.
>
> > It'd be great if some BGL expert (Jeremy?) jumped
> > in and comment on this.
>
> Yes, that would be great.
>
> Kind Regards,
> Alexander
>
> -------------- sample code --------------
> #include <iostream>
>
> #include <boost/multi_index_container.hpp>
> #include <boost/multi_index/ordered_index.hpp>
> #include <boost/multi_index/sequenced_index.hpp>
>
> #include <boost/graph/adjacency_list.hpp>
>
> using namespace boost;
> using namespace boost::multi_index;
>
> using namespace std;
>
> template<class Index>
> struct MICSelector
> {
> typedef Index index;
> };
>
> struct Item
> {
> Item(int _a=0, int _b=0, int _c=0)
> : a(_a), b(_b), c(_c) {}
>
> int a,b,c;
>
> friend std::ostream& operator<<
> (std::ostream& os, const Item& item)
> {
> return os << " a: " << item.a <<
> " b: " << item.b << " c: " <<
> item.c << " ";
> }
> };
>
> struct ItemHolder
> {
> ItemHolder(int id=0, const Item item=Item())
> : m_ID(id), m_item(item) {}
>
> int m_ID;
> Item m_item;
>
> friend std::ostream& operator<<
> (std::ostream& os, const ItemHolder& item)
> {
> return os << " ID: " << item.m_ID
> << " Item( " << item.m_item << ") ";
> }
> };
>
> struct ItemHolderKey
> {
> typedef int result_type;
> int operator()(const ItemHolder& v) const { return v.m_ID; }
>
> template<class P, class Derived, class Config, class Base>
> int get_int(const P& p, const adj_list_impl<Derived, Config, Base>&)
> const;
>
> template<class P>
> int operator()(const typename P& p) const;
> };
The problem with your approach to implementing this
oeprator() is that you've got a circular dependency
between ItemHolderKey, which is nasty to say the least.
Since you're hacking your way through this, I guess
you won't mind relying on just another undocumented
feature to solve this in a nicer manner. Reviewing the
BGL source, seems like the container is not instantiated
with void*s but rather with the actual stored_vertex pointer.
stored_vertex must be one of the following types:
boost::graph::detail::adj_list_gen<...>::config::seq_stored_vertex;
boost::graph::detail::adj_list_gen<...>::config::bidir_seq_stored_vertex
;
boost::graph::detail::adj_list_gen<...>::config::rand_stored_vertex;
boost::graph::detail::adj_list_gen<...>::config::bidir_rand_stored_verte
x;
So you just can provide overloads for all of them and
get rid of the "static MG m" dependency and the get_int() memfun:
template <class Graph, class VertexListS, class OutEdgeListS,
class DirectedS, class VertexProperty, class EdgeProperty,
class GraphProperty, class EdgeListS>
int operator()(
const
boost::graph::detail::adj_list_gen<...>::config::seq_stored_vertex* p)
const
{
return p->m_property.m_value.m_ID;
}
//etc for the other stored_vertex types
See what I mean? You can even do better and provide
an adaptor that takes a general key extractor and performs
the surgery:
template<typename KeyExtractor>
struct from_adjacency_list: private KeyExtractor
{
typedef KeyExtractor::result_type result_type;
// we forward to KeyExtractor for the general case
template<typename Value>
const result_type& operator()(const Value& v)
{
return KeyExtractor::operator()(v);
}
// overloads for the various stored_vertex types
template <class Graph, class VertexListS, class OutEdgeListS,
class DirectedS, class VertexProperty, class EdgeProperty,
class GraphProperty, class EdgeListS>
const result_type& operator()(
const
boost::graph::detail::adj_list_gen<...>::config::seq_stored_vertex* p)
const
{
return KeyExtractor::operator()(p->m_property.m_value);
}
// etc
};
Now you can use this adaptor in a fairly general way:
// No need to define ItemHolderKey.
// No dependecies from the particular adjacency_list you're
// instantiating.
typedef indexed_by
<
sequenced<>,
ordered_unique<
from_adjacency_list<member<ItemHolder,int,&ItemHolder::m_ID> >
>
// and other indices
> Index;
I haven't actually tried this, but I think it should work and
provide with a much more comfortable way to define your
embedded MICs.
[...]
> It is correct that I use undocumented features of BGL to
> fuse MIC and BGL. OTOH, it is the way how std::list,
> std::vector etc. are used. It would be a surprise if it
> is gonna change. One could say that it is
> documentation issue, rather then the code.
Well, the thing should work but won't be surprised if
everthing goes to heck when ugrading to Boost 1.3x.
As for this being a documentation issue, I'm afraid
too much detail is relied on to think realistically
BGL authors will concede to open it to the public.
A most viable solution (applicable not only to
MIC but also to any other container) would be
to have a graph class modeled after adjacency_list
with the following aspect:
template<
typename ElementType,
typename EdgeList,
typename VertexList,
...
>
element_adjacency_list;
so that it guarantees that the edge container is
instantiated with elements of type ElementType*.
This is not very hard to achieve if the actual
stored_vertex is defined like this:
template<typename ElementType>
struct element_pointer
{
ElementType* ptr;
};
template<typename ElementType,...>
struct stored_vertex: public element_pointer<ElementType>
{
// rest of the payload (properties, pointer to edge list, etc.
};
// from stored_vertex* to ElementType*
stored_vertex<...>* sv;
ElementType* ptr=sv->ptr;
// from ElementType* to stored_vertex
// this is portable!
ElementType* ptr;
element_pointer<ElementType>* eptr=
reinterpret_cast<element_pointer<ElementType>*>(
reinterpret_cast<char*>(ptr)-
offsetof(element_pointer<ElementType>,ptr)
);
stored_vertex<...>* sv=static_cast<stored_vertex<...>*>(eptr);
Implementing this should be mostly cut&paste from the
original adjacency_list, so, who knows, maybe you can
convince BGL authors to do it :)
Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk