help-bison
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: C++ / multiple instances / iostreams


From: Hans Aberg
Subject: Re: C++ / multiple instances / iostreams
Date: Wed, 21 Jul 2004 20:05:44 +0200

At 00:01 +0200 2004/07/21, Laurence Finston wrote:
>> 4) The acronym POD stands for "plain old data."
>
>Thanks again.  You certainly know your standard.

Nope, just some parts of it. This is one thing one eventually encounters.

>> The idea of C++ is to take those running time expenses in favor of
>> programming convenience.

>I don't agree that this is the idea of C++.  I believe C++ is designed to
>support a variety of different programming styles.

I believe that the design criterion that BS expressed was that C++ should
admit efficient implementations, but by no means as optimized as in C. Sed
the book DEC++ ("Design and Evolution of C++").

>  One of the important
>goals in the design of C++ was also not to lose the ability to program in a
>low-level style efficiently.

Right: C++ is a multiparadigm language. But the high level cannot be as
efficient as optimized low-level programming.

>  I don't quite understand what you mean by
>automatic cleanup, since C++ doesn't do garbage collection.  I assume you mean
>something having to do with destructors performing deallocation when a count
>reaches 0, or something similar.

Right: If one uses a C++ stack as semantic stack, when the Bison parser
unwinds it, the C++ code will invoke class destructors, which can be used
for cleanup then. Under C, one does not have that possibility: It must
somehow be made by hand.

The reference count is just a way to provide a primitive GC: Saving some
memory and recopying. If time and space are not programming concerns, one
does not need to use it. But I have found that it is very convenient in a
parser, as it typically builds some objects, which then are conveniently
fit together.

>> >I've understood him correctly) will probably work well for a lot of
>> >applications.  In my application it wouldn't, but I've found that
>> casting the
>> >objects to the appropriate types as needed isn't any trouble.
>>
>> It will probably work in your case as well, if you were willing to take the
>> overhead it generates relative your method.
>
>I was wrong to say it wouldn't work.  I believe it would work, I just
>don't think it would be good design for my application.

Right.

>  For example, many of
>the types in 3DLDF are derived from the abstract base class `Shape', e.g.,
>`Point', `Path', `Circle', etc.  Others are not, e.g., `Transform', `Pen', and
>`Color'.  If it would make sense to define virtual functions that might be
>called on objects of any of these types in the parser actions, then I think it
>would make sense to define an abstract base class for all of them.  Since this
>is not the case, I don't see any advantage in doing so over using a `void*'.

Actually, I have a similar situation in my theorem prover: I have a class
object, which maintains a reference-counted pointer to a base class
object_root.
All polymorphic objects are derived from object_root. For example:
  class formula_root;
  class proposition_root;
  class variable_root;
But as it becomes difficult to keep track of the semantics while
programming, I have added a few more object classes:
  class proposition;
  class formula;
  class variable;
These need not having any derivation order, but are only there to simplify
my programming. It is easy to convert data of any of them to class object
data. Thus,
 class semantic_type {
 public:
   long number_;
   std::string text_;
   my::object object_;

   semantic_type() : number_(0) {}
 };
 #define YYSTYPE semantic_type
will suffice as semantic type. One converts data using either
dynamic_cast<T*>, in which case null pointer must be checked, or
dynamic_cast<T&>, in which case null pointers case an exception to be
thrown.

>Overhead is also a significant factor.  Currently, 3DLDF produces only
>MetaPost code, but ultimately I'd like it to perform its own scan converting
>and rendering and produce output in graphics formats such as PNG and MNG.
>Someday, I'd also like it to be able to produce graphics in real-time.  So I
>do have to try to program efficiently in way that's not necessary for a lot of
>applications in these days of fast processors and cheap memory.  I'm actually
>glad of the excuse to program in a somewhat low-level style.  Sadly, it's not
>what most employers seem to be looking for these days.

I can't tell you what is right for you here. :-)

Here is my reference-count setup:

class object_root;

class object;

class object_root {
  mutable unsigned count_;
public:
  object_root() : count_(1) {}
  virtual ~object_root() {}

  object_root* copy() const { ++count_; return
const_cast<object_root*>(this); }
  void shed() { if (--count_ == 0)  delete this; }

  virtual object_root* clone() const = 0;

  virtual void write(std::ostream&, write_style) const = 0;
};

class object {
protected:
  object_root* data_;

public:
  object_root* copy() const { return (data_ == 0)? 0 : data_->copy(); }
  void shed() { if (data_ != 0)  data_->shed(); }

  object() : data_(0) {}
  ~object() { shed(); }

  object(const object& x) : data_(x.copy()) {}
  object& operator=(const object& x) {
    if (data_ != x.data_) { shed(); data_ = x.copy(); }
    return *this;
  }

  object(object_root* rp) : data_(rp) {}

  object_root* data() { return data_; }
  const object_root* data() const { return data_; }

  template<class T>
  struct cast {
    T item;
    cast(object& x) : item(dynamic_cast<T>(x.data())) {}
    operator T() { return item; }
  };

  void write(std::ostream& os, write_style ws) const {
    if (data_ == 0) { if (trace_null)  os << "ø"; }
    else data_->write(os, ws);
  }
};

The object::cast struct is just an experimental attempt to autmoate
dynamic_casts It is probably simöler to do it by hand.

  Hans Aberg






reply via email to

[Prev in Thread] Current Thread [Next in Thread]