From: Ted Christiansen, P.E. <[email protected]> - 31 Jan 1999
> I am trying to store c4_storage, c4_view, etc. as data members > in classes, and I don't know what I am doing wrong. > I would like: the Model object to contain the c4_Storage object > the Nodeview object to contain the c4_View, property and > c4_Row objects. [...] > Model::create(char *filename) > { > c4_Storage storage ("test.dat", true); > this->mptr_storage = &storage; > cout << "Created storage object\n"; > }
The problem with this is that you are allocating a c4_Storage object on the stack, are then storing a pointer to it in Model, and when create exists, it will destroy the storage - and leave you with a dangling pointer.
Here's one way to do it:
Model::create(char *filename) { mptr_storage = c4_Storage ("test.dat", true); cout << "Created storage object\n"; }
Also, be sure to clean it up when the Model object is destroyed:
Model::~Model() { delete mptr_storage; } > Nodeview::Commit() > { > c4_Storage *storage > storage = this->mptr_storage; > c4_View *view; > view = this->mptr_vNodes; > > c4_Row *row; > row = this->mptr_row; > c4_IntProp *pLabel; > pLabel = this->mptr_pLabel; > // ERROR: term does not evaluate to a function > pLabel (row) = this->m_label; > > // ERROR: left of '.Add' must have class/struct/union type > view.Add(row); > // ERROR: left of '.Store' must have class/struct/union type > storage.Store("nodes", view); > // ERROR: left of '.Commit' must have class/struct/union type > storage.Commit(); > }
One way to resolve this, is to change your code to:
Nodeview::Commit() { c4_Storage& storage = *mptr_storage; c4_View& view = *mptr_vNodes; c4_Row& row = *mptr_row; c4_IntProp& pLabel = *mptr_pLabel; pLabel (row) = m_label; view.Add(row); storage.Store("nodes", view); storage.Commit(); }
Note that "this->" is not needed in C++, and rarely used.
But this code is still a bit complex, due to the fact that you are using pointers. You can do more assignments with Metakit and C++ that might be apparent. This not only is simpler to write, it also avoids problems with dangling pointers. In your code, you used:
> c4_View view; > this->mptr_vNodes = &view;
But this is incorrect, for the same reason as with c4_Storage: mptr_vNodes is valied as long as view exists, but once the routine exists, it points into thin air and your code will crash. The problem is that C++ compilers will not warn you about this, AFAIK.
Instead of using a pointer for a c4_View, why not use a c4_View itself? So, in NodeView, define:
class NodeView { ... c4_View m_vNodes; ... };
And similarly for the other objects. Then, your commit becomes:
Nodeview::Commit() { m_pLabel (m_row) = m_label; m_vNodes.Add(row); mptr_storage->Store("nodes", m_vNodes); mptr_storage->Commit(); }
The basic idea is simple, write assignments just like if the data were a plain "int" value, and avoid pointers and references when possible. Unfortunately, the c4_Storage object *has* to be a pointer, since it does nto exist at the time your Model object is created.
There are a lot of such issues in C++, and I'm afraid there's no other way than investing a lot of time figuring all this out. If you don't, you'll end up fighting packages like Metakit on every line of code you write... The developer release of Metakit contains more sample code and about a hundred little tests in a program called "regress", which can serve as illustration of how to do all sorts of things in C++ with Metakit.
If it's an option, you might also want to consider using the Tcl scripting language - which has a very simple and powerful interface to Metakit - see the following page:
https://www.equi4.com/metakit/tcl.html
-jcw