Boost logo

Boost Users :

From: Robbie Morrison (robbie_at_[hidden])
Date: 2007-10-17 14:10:37


Hi all

The following kind of code is probably quite common.
It involves:

  * using individual Register objects containing
    construction parameters to help factory-create new
    Entity objects

  * and, in the process, "tying" key object state
    variables so that each associated Registry object
    can watch these

And yes, I know this breaks encapsulation!

I chose C++ references and my code seems quite
complicated for what should be a relatively
straightforward task. Pointers were rejected because
the dereferencing syntax can lead to really obscure
numerical expressions (particularly for non-C/C++
programmers).

However, boost::ref() seemed to offer a simpler
solution and I experimented with this for quite some
time to no avail. I also considered using a
boost::ptr_vector and 1-tuples.

In addition, adding cout statements to ctors and dtors
led to more activity than I would have expected.
Moreover, there were more dtor calls than ctor calls.

Any comments or suggestions gratefully received!

Also an explanation of the differences between a "type
conversion" operator and an "address-of" operator. Is
this simply related to role or is there a distinction?

More information follows the program listing.

many thanks in advance
Robbie

---
Robbie Morrison
PhD student -- policy-oriented energy system simulation
Institute for Energy Engineering (IET)
Technical University of Berlin (TU-Berlin), Germany
[from IMAP client]
// ---------------------------------------------------------
//  Copyright: (c) 2007 Robbie Morrison. All rights reserved.
//  License: Unrestricted reuse in any form, for any purpose.
//  Version: $Id: frag-xeona-registers-9.cc,v 1.3 2007/10/17 15:23:43 robbie Exp $
//  Tested using g++ 4.1.2 (-Wall -Weffc++ -pedantic), boost
//  1.34.1 (source build), and valgrind (memory checker) on
//  Ubuntu 6.10 (7.04 is current) using Linux 2.6.17-12-generic.
#include <iostream>           // standard io
#include <string>             // C++ strings
#include <vector>             // STL sequence container
#include <iomanip>            // setw() and family
#include <tr1/memory>         // TR1 smart pointers
using std::tr1::shared_ptr;   // for convenience
// ---------------------------------------------------------
template <typename T>
class Value                         // just for type conversion operator T&()
{
public:
  explicit Value(T v) : _value(new T(v)) { }
  operator T&() { return *_value; } // return aliasable value
private:
  shared_ptr<T>    _value;          // pointer usage is essential
};
// ---------------------------------------------------------
class Register                      // initializes and watches selected
{                                   // ..  Entity variables
public:
  explicit Register() : _vec() { }  // explicit for testing
  template <typename T>             // would normally support int,double,bool
  Value<T>
  tieRef(const std::string& tag)    // would normally search on tag!
  {
    T temp = 0.45;                  // would normally obtain value from user
    shared_ptr<Value<T> > val(new Value<T>(temp)); // actual object is shared
    _vec.push_back(val);            // crude "last on" indexing for testing
    report();
    return *_vec.back();            // given aforementioned "last on" indexing
  }
  void report()                     // just for doubles (could be templated)
  {
    double temp = *_vec.back();     // this line is interesting!
    std::cout << "Register : value : " << temp << std::endl;
  }
private:
  std::vector<shared_ptr<Value<double> > > _vec;  // data mirror
};
// ---------------------------------------------------------
class Entity                        // represents simulation 'things'
{
public:
  explicit                          // explicit for testing
  Entity(Register& r) :             // pass-by-reference necessary
    _register(r),                   // for testing purposes
    _coeff(r.tieRef<double>("coeff")),  // intuitive syntax
    _notInteresting()               // rely on default construction
  {
    report();
  }
  void modify(double d)
  {
    _coeff = d;                     // normal syntax (no pointer derefs)
    report();                       // display this modification
    _register.report();             // confirm the modification propagated
  }
  void report()
  {
    std::cout << "Entity   : value : " << _coeff << std::endl;
  }
private:
  Register&    _register;
  double&      _coeff;              // watched variable
  int          _notInteresting;     // ignored variable
};
// ---------------------------------------------------------
int
main(int argc, char* argv[])
{
  std::cout << std::fixed << std::setprecision(2);
  std::cout << " creating a register"  << std::endl;
  Register reg;
  std::cout << " creating an entity"   << std::endl;
  Entity ent(reg);
  std::cout << " modifying the entity" << std::endl;
  ent.modify(0.01);
  ent.modify(2.0);
}
// ---- end-of-program -------------------------------------
//  OUTPUT
//   creating a register
//   creating an entity
//  Register : value : 0.45
//  Entity   : value : 0.45
//   modifying the entity
//  Entity   : value : 0.01
//  Register : value : 0.01
//  Entity   : value : 2.00
//  Register : value : 2.00
//  Development environment
//
//    boost    : 1.34.1 (via ./configure; make all; make install)
//    gcc      : 4.1.2 20060928 (prerelease) (Ubuntu 4.1.1-13ubuntu5)
//    os       : Ubuntu 6.10, Linux 2.6.17-12-generic (7.04 is current)
//    hardware : Toshiba Tecra A2 330 laptop (purchased 27-Aug-2004)
//    specs    : 1.4GHz Intel Celeron M (32-bit) / 512MiB RAM / 40GB HDD
//
//  Problem
//
//      The problem involves sharing data between Entity objects,
//      used to represent various 'things' in my simulation --
//      and Register objects ('register' in the sense of a book
//      recording important information), used to help construct
//      and then watch the state of Entity objects.  A concrete
//      ObjectFactory object (not relevant to this discussion)
//      will be used to create the Entity objects (Alexandrescu
//      2001 pp179-217).
//
//      I would also like a relatively clear syntax for
//      programming the Entity's as there are lots of these and
//      they contain numerical expressions -- for instance,
//      dereferencing (smart) pointers would yield lines like:
//
//        *_perf = 2.1 * local * *_coeff;
//
//  Solution (provisional)
//
//      The chosen strategy is to declare all to-be-observed
//      Entity object data members as references and then bind
//      these to the relevant Register object element upon
//      construction.
//
//      An alternative approach could be to utilize the Memento
//      pattern (which relies on friendship) in conjunction with
//      the Iterator pattern (for traversing a population of
//      objects) (Gamma etal 1995 pp283-291 and pp257-271
//      respectively).
//
//  Design notes
//
//      In relation to C++, a reference data member must be
//      initialized in the constructor's initializer list
//      (Lischner 2003 p36).  And a reference may not be bound to
//      a class member (data member or member function) (p35).
//
//      Class Value<T> is instead used to avoid an "invalid
//      initialization .. from a temporary" error when
//      initializing Entity::_coeff using the Register::tie<T>
//      member function.
//
//      Class Value supplies a "type conversion operator"
//      (Lischner 2003 p112), in this case, Value::operator T&,
//      which binds to the non-const reference data member in
//      Entity.  The code for Value derives from Lischner (2003
//      p119 example 5-21).
//
//  References
//
//      Alexandrescu, Andrei.  2001.  Modern C++ design :
//        generic programming and design patterns applied.
//        Addison-Wesley, Boston, USA.  ISBN 0-201-70431-5.
//
//      Gamma, Erich, Richard Helm, Ralph Johnson, and John
//        Vlissides.  1995.  Design patterns : elements of
//        reusable object-oriented software.  Addison-Wesley,
//        Boston, USA.  ISBN 0-201-63361-2.
//
//      Lischner, Ray.  2003.  C++ in a nutshell : a language
//        and library reference, O'Reilly and Associates,
//        Sebastopol, California, USA.  ISBN 0-596-00298-X.

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