Can you use a template class to store data in a vertex of a boost graph?
Has anyone used a template class to store information in a vertex of a boost graph? I am looking for a way in which to store a variety of data types in a boost graph. So that if a sample class like following: template <typename T> class Data { ... }; It can be used to data such that a Data<int> and a Data<float> can be stored at a vertex in the graph. Is what I desire possible? Stephen
Here is an example that I believe represents the problem. The problem
comes down to how to define the vertex property. What I have so far,
which is incorrect, is:
typedef property< vertex_index_t, uint32_t,
property< vertex_name_t, boost::shared_ptr<Data> > >
VertexProperty;
typedef adjacency_list
You can't use templates in this manner. Ask yourself, which type of Data object do you expect boost.graph to instanciate when a new vertex is created? It can only make one choice. If I was doing this, and was concerned about memory consumption, I'd consider using a union attribute in Data, along with a type attribute. Stephen torri wrote:
Here is an example that I believe represents the problem. The problem comes down to how to define the vertex property. What I have so far, which is incorrect, is:
typedef property< vertex_index_t, uint32_t, property< vertex_name_t, boost::shared_ptr<Data> > > VertexProperty;
typedef adjacency_list
// VertexProperties Graph_Type; Data, in my example code attached, is a template so this of course is the incorrect way to use a template. Hopefully this together with my original post will help me to learn more about C++, boost and the correct way to solve this problem.
Stephen
------------------------------------------------------------------------
#define BOOST_SPIRIT_DEBUG 1 #define PHOENIX_LIMIT 6 #define BOOST_SPIRIT_CLOSURE_LIMIT 6
#include
#include #include #include #include #include #include #include <fstream> #include <iostream> #include <sstream> using namespace boost; using namespace boost::spirit;
//======== my data struture
template <class T> class ToString { public:
static const T Convert (T val) { return val; } };
template <class T> class Data { public: Data () {}
Data (const T val) : m_value (val) {}
virtual ~Data(){}
const T get_Value (void) const { return m_value; }
virtual const std::string get_Name (void) const = 0;
const std::string get_Label (void) { std::stringstream out; out << this->get_Name() << std::endl << "Value: " << ToString<T>::Convert(m_value) << std::endl; return out.str(); }
private:
T m_value;
};
class Unsigned_Int_Data : public Data
{ public: Unsigned_Int_Data (uint32_t val) : Data (val) {} ~Unsigned_Int_Data(){}
virtual const std::string get_Name (void) const { return "Unsigned Int Data"; } };
class Float_Data : public Data<float> { public: Float_Data (float val) : Data<float> (val) {}
~Float_Data(){}
virtual const std::string get_Name (void) const { return "Float Data"; }
};
class Data_Factory { private:
static boost::shared_ptr
m_factory; public:
boost::shared_ptr< Data
> get_Uint (uint32_t val) { return boost::shared_ptr< Data > ( new Unsigned_Int_Data(val)); } boost::shared_ptr< Data<float> > get_Float (float val) { return boost::shared_ptr< Data<float> > ( new Float_Data (val) ); }
static boost::shared_ptr
get_Factory () { if (m_factory.get() == 0) { m_factory.reset (new Data_Factory()); } return m_factory; } private:
Data_Factory () {}
};
boost::shared_ptr
Data_Factory::m_factory; class Data_Graph { public:
typedef property< vertex_index_t, uint32_t, property< vertex_name_t, boost::shared_ptr<Data> > > VertexProperty;
typedef adjacency_list
// VertexProperties Graph_Type; typedef graph_traits
::vertex_descriptor Vertex; typedef graph_traits ::edge_descriptor Edge; typedef std::map IdVertexMap; Data_Graph () {}
void add_Source ( boost::shared_ptr<Data> obj_ptr ) { bool found; IdVertexMap::iterator pos; Vertex node;
boost::tie (pos, found) = m_index.insert (std::make_pair(obj_ptr->get_ID(), node));
if (found) { // Insertion was successful // Make a new vertex and return handle to 'node' node = add_vertex(m_graph);
// Get list of Datas boost::property_map
::type clist = get(vertex_name, m_graph); // Assign component to vertex position clist[node] = obj_ptr; // Add vertex handle to map position pos->second = node;
// Get list of indexes boost::property_map
::type ilist = get(vertex_index, m_graph); ilist[node] = obj_ptr->get_ID(); } else { std::cerr << "ERROR: Duplicate source found. Skipping Data" << std::endl; return; } }
void add_Data (uint32_t parent_id, boost::shared_ptr<Data> obj_ptr) { IdVertexMap::iterator pos; IdVertexMap::iterator cpos; Vertex child_node; bool cfound;
pos = m_index.find (parent_id);
if (pos != m_index.end()) { boost::tie (cpos, cfound) = m_index.insert (std::make_pair(obj_ptr->get_ID(), child_node));
if (cfound) { // Insertion was successful // Make a new vertex and return handle to 'node' child_node = add_vertex(m_graph);
// Get list of Datas boost::property_map
::type clist = get(vertex_name,m_graph); // Assign component to vertex position clist[child_node] = obj_ptr;
// Add vertex handle to map position cpos->second = child_node;
// Get list of indexes boost::property_map
::type ilist = get(vertex_index, m_graph); ilist[child_node] = obj_ptr->get_ID();
} else { std::cerr << "ERROR: Duplicate child found. Skipping Data #" << obj_ptr->get_ID() << std::endl << obj_ptr->get_Name() << std::endl; return; } } else { std::cerr << "ERROR: Cannot find parent. Skipping adding component #" << obj_ptr->get_ID() << std::endl << obj_ptr->get_Name() << std::endl; return; }
// Add edge from parent to child Edge link;
if (! (add_edge ( pos->second, child_node, m_graph).second)) { std::cerr << "ERROR: Duplicate edge exists from " << parent_id << " to " << obj_ptr->get_ID() << std::endl; return; } }
const Graph_Type& get_Graph () { return m_graph; }
boost::shared_ptr<Data> get_Data (const Vertex& node) { // Get list of Datas boost::property_map
::type clist = get(vertex_name, m_graph); // Assign component to vertex position return clist[node]; } private:
Graph_Type m_graph;
IdVertexMap m_index;
};
class Data_Writer { public:
Data_Writer (const boost::shared_ptr
g) : m_graph (g) {} std::string operator[](const Data_Graph::Vertex& v) const { boost::shared_ptr<Data> obj_ptr = m_graph->get_Data(v); return obj_ptr->get_Label(); }
private:
const boost::shared_ptr
m_graph; }; class Graph_Grammar : public grammar
{ public: Graph_Grammar (boost::shared_ptr
data) : m_data (data) {} struct string_closure : closure
{ member1 text; }; struct source_closure : closure
{ member1 node_id; member2 name; }; struct node_closure : closure
{ member1 child_id; member2 name; member3 parent_id; }; static boost::shared_ptr<Data> get_Data (const uint32_t id, const std::string name, const uint32_t = 0) { boost::shared_ptr
factory = Data_Factory::get_Factory(); boost::shared_ptr<Data> obj_ptr;
if (name.compare("uint") == 0) { obj_ptr = factory->get_Uint (id); } else if (name.compare("float") == 0) { obj_ptr = factory->get_Float (id); }
return obj_ptr; }
struct construct_Data_ {
template
struct result { typedef void type; }; template
void operator()(Data& data, const ID& id, const Name& name, const Parent_ID& pid) const { boost::shared_ptr<Data> obj_ptr = Graph_Grammar::get_Data (id,name,pid); data->add_Data ( pid, obj_ptr ); } }; phoenix::function
construct_Data; struct construct_Source_ {
template
struct result { typedef void type; }; template
void operator()(Data& data, const ID& id, const Name& name) const { boost::shared_ptr<Data> obj_ptr = Graph_Grammar::get_Data (id,name); data->add_Source ( obj_ptr ); } }; phoenix::function
construct_Source; template <typename ScannerT> struct definition {
rule<ScannerT> graph; rule
source_node; rule node; rule name; definition(Graph_Grammar const& self) { graph = source_node >> while_p (~epsilon_p(end_p)) [ node ];
source_node = as_lower_d[str_p("source")] >> ch_p('{') >> uint_p [source_node.node_id = phoenix::arg1] >> ch_p(',') >> name [source_node.name = phoenix::arg1] >> str_p("};") [ self.construct_Source (self.m_data, source_node.node_id, source_node.name) ];
// Read this and add a semantic action to create a // File_Data_Source_Config object. Add to m_data. node = as_lower_d[str_p("node")] >> ch_p('{') >> uint_p [node.child_id = phoenix::arg1] >> ch_p(',') >> name [node.name = phoenix::arg1] >> ch_p (',') >> uint_p[node.parent_id = phoenix::arg1] >> str_p("};") [self.construct_Data (self.m_data, node.child_id, node.name, node.parent_id)];
name = ch_p('"') >> (alnum_p >> *(alnum_p | ch_p('_'))) [ name.text = phoenix::construct_std::string(phoenix::arg1, phoenix::arg2) ] >> ch_p('"');
BOOST_SPIRIT_DEBUG_NODE(graph); BOOST_SPIRIT_DEBUG_NODE(source_node); BOOST_SPIRIT_DEBUG_NODE(node); BOOST_SPIRIT_DEBUG_NODE(name); }
rule<ScannerT> const& start() const { return graph; } };
mutable boost::shared_ptr
m_data; }; template <typename Grammar> bool parse_i (const std::string & file, const Grammar& grammar) { boost::spirit::file_iterator<> first(file);
if (!first) { std::cerr << "Configuration_Parser: Unable to open file '" << file << "'!" << std::endl; return false; }
const boost::spirit::file_iterator<> last = first.make_end();
typedef typename boost::spirit::position_iterator < boost::spirit::file_iterator<> > iterator_t;
const parse_info
info = boost::spirit::parse(iterator_t(first, last, file), iterator_t(), grammar, boost::spirit::space_p | boost::spirit::comment_p("#")); if (!info.full) { const boost::spirit::file_position fp = info.stop.get_position();
std::cerr << "Parsing of file '" << fp.file << "' failed at line " << fp.line << ", column " << fp.column << ".\n"; }
return info.full; }
int main () {
// read Graph2 and all internal properties boost::shared_ptr
m_data (new Data_Graph()); Graph_Grammar grmr (m_data); std::ofstream file ("test.txt"); file << "source{0,\"uint\"};" << std::endl; file << "node{1,\"float\",0};" << std::endl; file << "node{2,\"uint\",0};" << std::endl; file << "node{3,\"uint\",1};" << std::endl; file.close();
if (!parse_i("test.txt", grmr)) { std::cerr << "Invalid configuration file format." << std::endl; }
std::ofstream gfile ("test.dot"); write_graphviz (gfile, m_data->get_Graph(), make_label_writer(Data_Writer(m_data)));
return 0; }
------------------------------------------------------------------------
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On Sun, 2004-11-14 at 10:42 -0500, Jeffrey Holle wrote:
You can't use templates in this manner. Ask yourself, which type of Data object do you expect boost.graph to instanciate when a new vertex is created? It can only make one choice.
I know that this was broken. I made the declaration that it was.
If I was doing this, and was concerned about memory consumption, I'd consider using a union attribute in Data, along with a type attribute.
Can you provide a simple example using the two types I gave in this example: float and uint32_t? Stephen
Something like: class Data { Data(float item) : type(float),float_datum(item) {} Data(uint32_t item) : type(uint32_t),uint32_datum(item) {} private: enum { float, uint32_t} type; union { float float_datum; uint32_t uint32_datum; }; } This is called an anonymous union. The caviot is C++ does allow classes within unions. Its because the compiler wouldn't know which default constructor to call... If you need to handle classes, its time to start using pointers too. Stephen torri wrote:
On Sun, 2004-11-14 at 10:42 -0500, Jeffrey Holle wrote:
You can't use templates in this manner. Ask yourself, which type of Data object do you expect boost.graph to instanciate when a new vertex is created? It can only make one choice.
I know that this was broken. I made the declaration that it was.
If I was doing this, and was concerned about memory consumption, I'd consider using a union attribute in Data, along with a type attribute.
Can you provide a simple example using the two types I gave in this example: float and uint32_t?
Stephen
boost.variant :)
On Sun, 14 Nov 2004 17:54:51 -0500, Jeffrey Holle
Something like:
class Data { Data(float item) : type(float),float_datum(item) {} Data(uint32_t item) : type(uint32_t),uint32_datum(item) {} private: enum { float, uint32_t} type; union { float float_datum; uint32_t uint32_datum; }; }
This is called an anonymous union. The caviot is C++ does allow classes within unions. Its because the compiler wouldn't know which default constructor to call... If you need to handle classes, its time to start using pointers too.
Stephen torri wrote:
On Sun, 2004-11-14 at 10:42 -0500, Jeffrey Holle wrote:
You can't use templates in this manner. Ask yourself, which type of Data object do you expect boost.graph to instanciate when a new vertex is created? It can only make one choice.
I know that this was broken. I made the declaration that it was.
If I was doing this, and was concerned about memory consumption, I'd consider using a union attribute in Data, along with a type attribute.
Can you provide a simple example using the two types I gave in this example: float and uint32_t?
Stephen
Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- Cory Nelson http://www.int64.org
On Sun, 2004-11-14 at 18:08 -0800, Cory Nelson wrote:
boost.variant :)
Possible. Let me expand my example. The Data class is really the mechanism I want to use to pass data between Components. A Component is a virtual class that is inherited by classes that implement the Component interface. Each Component class performs a action (e.g. Detect file type) when the function process() is called and returns the results (e.g. Data<string>) when the outcome() function is called. These two functions are called by a visitor which walks the graph of Component objects. A simple graph example is: A --> B --> ... The Component 'A' performs an action which generates a 'std::string' as a result. When the graph visitor calls A.process() then this result is stored in the A class object. The graph visitor takes the result via outcome as a Datastd::string and initializes B.init (Datastd::string). The visitor continues on until the end. Here is how I am defining the Component class: class Component { public: // Boost::variant? - need to store multiple types in m_data void init (uint32_t id, Data<...> data); // run child class action (call process) void run (void); // Virtual function child class must support. Using input stored in // m_data do some action and store the result internally. virtual void process (void) = 0; // get child results // Boost::variant? - need to return the results. The return type is // dependent on the child class's function. In 'A' the return type is // a std::string. Call outcome() in child Data<...> result (void); // Virtual function child class must support. return results virtual Data<...> outcome (void) = 0; private: // Boost::variant? - need to store // multiple types of inputs maybe here depending on the child class's // function. For example, 'A' needs a 'unsigned int' and 'std::string' // to work. std::list< Data<...> > m_data; }; So input Data element types (e.g. int, std::string) depend on the output of the previous Component in the graph connected to the present Component. The output Data element type (e.g. int, std::string) depends on the implementation of the child class, e.g. result of process() retrieved by calling outcome(). I think the boost::variant might work. I want to see if this further detailing of the example help to confirm this. Stephen
participants (3)
-
Cory Nelson
-
Jeffrey Holle
-
Stephen torri