Table of Contents

Language plugins : Implementation

Attributes

Background

Sometimes it is desired to attach some meta-data, an “attribute” to some object. The name and the use of such attributes is determined externally by some other application. It has meaning there, but likely not to us. We just need to store it, to pass it on as we get it, unchanged.

Some possible applications that might use this could be schematic capture or layout. The attributes would contain the graphic info that determines how it is rendered.

These attributes could be attached to any object, including sub-objects like port names and parameters.

In terms of performance, both time and space, it is most important that there is no cost when it is not used. Something so simple as a pointer, that would be NULL if there is nothing there, is too high.

Implementation

The file “u_attrib.h” defines them. A partial listing, just an outline, is shown here.

class INTERFACE ATTRIB_LIST {
private:
  std::string _s;
  int _ref_count;
  ATTRIB_LIST* _up;
  const void* _owner;
 
  ATTRIB_LIST(const ATTRIB_LIST&) = delete;
  ATTRIB_LIST() = delete;
public:
  ATTRIB_LIST(const std::string& S, ATTRIB_LIST* UP, const void* Owner);
  ~ATTRIB_LIST();
  int   inc_ref_count() {return ++_ref_count;}
  int   dec_ref_count() {assert(_ref_count>0); return --_ref_count;}
  //int   ref_count()const {untested(); return _ref_count;}
 
  const std::string string(const void* Owner)const;
  const std::string operator[] (const std::string& Key)const {itested();
};

An ATTRIB_LIST is just a string, internally, with links for reuse, lookup and incremental construction.

To construct, it just reads a string, which is expected to be names, or name=value pairs, separated by commas. This is not checked on read-in. Whatever comes in is just stored.

The “Owner” is a unique tag that identifies the object that owns it. Usually, it would be a traditional pointer to that object, hence the type void *, but it could be any unique tag. It is never dereferenced.

When more comes in, the old one is pushed upstairs (*UP).

A reference count keeps track of how many places it is used, so it can be deleted when it is no longer needed.

The ATTRIB_LIST is accessed through ATTRIB_LIST_p, which behaves as a smart pointer, that manages creation and deletion of ATTRIB_LIST objects.

class INTERFACE ATTRIB_LIST_p {
private:
  ATTRIB_LIST* _p;
public:
  ATTRIB_LIST_p() :_p(NULL) {}
  ATTRIB_LIST_p(const ATTRIB_LIST_p& P);
  operator bool()const {return _p;}
  const std::string string(const void* Owner)const {return _p->string(Owner);}
  const std::string operator[] (const std::string& Key)const {itested();return ((_p) ? (*_p)[Key] : "0");}
  ATTRIB_LIST_p& add_to(const std::string& String, const void* Owner);
};

Since this would still require allocation of a pointer, an indirect storage scheme eliminates the need for the pointer when the attribute does not exist.

This is defined in the file “l_indirect.h”.

template <class T>
class INDIRECT {
 private:
  std::map<const void*, T> _map;
  INDIRECT(const INDIRECT&) = delete;
 public:
  INDIRECT() : _map() {_map[NULL];}
  ~INDIRECT() {}
  size_t count(const void* x)const {return _map.count(x);}
  T& operator[](const void* x);    // subscript operator, to add new attributes, by owner
  const T& at(const void* x)const; // at operator, to look up existing attributes by owner
  size_t erase(void* b, void* e);  // erase operator, to dispose of attribs no longer needed
                                   // usually this happens when the owner is deleted.
};

Usage examples

The following code snippets show its use in lang_verilog.

// possibly global, here in e_base.
protected:
  static INDIRECT<ATTRIB_LIST_p> _attribs;
public:
  ATTRIB_LIST_p& attributes(const void* x) {return _attribs[x];}
  const ATTRIB_LIST_p& attributes(const void* x)const {return _attribs.at(x);}
 
// where used, here in lang_verilog
/*--------------------------------------------------------------------------*/
void LANG_VERILOG::parse_attributes(CS& cmd, const void* x)
{
  assert(x);
  while (cmd >> "(*") {
    std::string attrib_string;
    while(cmd.ns_more() && !(cmd >> "*)")) {
      attrib_string += cmd.ctoc();
    }
    attributes(x).add_to(attrib_string, x);
  }
}
/*--------------------------------------------------------------------------*/
void LANG_VERILOG::print_attributes(OMSTREAM& o, const void* x)
{
  assert(x);
  if (attributes(x)) {
    o << "(* " << attributes(x).string(NULL) << " *) ";
  }else{
  }
}
/*--------------------------------------------------------------------------*/