meta.tex   [plain text]


% file: .../doc/meta.tex

% $Header: /cvs/Darwin/Security/SecuritySNACCRuntime/doc/meta.tex,v 1.1.1.1 2001/05/18 23:14:10 mb Exp $
% $Log: meta.tex,v $
% Revision 1.1.1.1  2001/05/18 23:14:10  mb
% Move from private repository to open source repository
%
% Revision 1.1.1.1  1999/03/16 18:05:53  aram
% Originals from SMIME Free Library.
%
% Revision 1.1  1997/01/01 22:47:38  rj
% first check-in
%

\chapter{\label{meta-chapter}The Metacode}

%-----------------------------------------------------------------------------------------------------------------------------------
\section{\label{intro-meta-section}Introduction}

When you call snacc, during its compilation, the text in the {\ufn .asn1} files gets turned into e.g. C++ classes.
Names become identifiers, and after the C++ compilation, the user program has no more access to the original module and type names, only to pointers and the bits and bytes of the classes' contents.

The metacode remedies this.
Using it, a program can access the modules, their types, their subtypes and the named values via strings.
Generic programs do not have to know any of the modules' or types' names---all the information can be traversed starting at a single well-known place.

The metacode has to map strings (``component {\C "name"} in type {\C "Person"} in ASN.1 module {\C "Mail"}'') into the in-core address of the indicated object.
Moreover, using the metacode, a generic program has to be able to traverse all modules, types and their components to learn about their names and types.
Given the name of a type, the metacode must be able to return a newly allocated object instance.

The metacode is an extension to the Snacc compiler's C++ backend and the C++ runtime library.

Since the metacode relies heavyly on the virtual function call mechanism, it is only implemented for the C++ backend.

A number of functions has been added to the C++ runtime library.
All code extensions have been wrapped into preprocessor conditionals.
Currently, only one or two libraries are made in {\ufn \dots/c++-lib/}, one with neither metacode nor Tcl interface, and an optional additional one with both extensions.
If you cannot or do not want to (as stated in {\ufn \dots/policy.h}) use the metacode or the Tcl interface, Snacc will be compiled without it, and only the normal library will get made.

If you want to use the metacode but not the Tcl interface, you have got to change the makefile to compile another set of {\ufn .o} files in a directory you may want to name {\ufn \dots/c++-lib/meta/} and archive the resulting files in {\ufn libasn1meta.a}.
Compile with {\ufn -DMETA=1 -DTCL=0}.

%-----------------------------------------------------------------------------------------------------------------------------------
\section{Implementation}

%leftover:----------------
With the metacode, the strings and the ASN.1 type components' type information are stored in arrays, one per structured ASN.1 type.
The type descriptions are listed in another array, one per ASN.1 module.

In the C++ code generated by snacc, every ASN.1 type is represented by a C++ class.
These C++ classes for ASN.1 simple types are implemented in the runtime library, and for structured types they are generated by the backend.
The metacode is an extension to all of those C++ classes.
The metacode put into every C++ class is very similar:
\begin{itemize}
  \item a static {\C \_desc} member is always present.
  \item simple types with names (ENUMERATED, INTEGER, BIT STRING) get an additional static {\C \_nmdescs[]} member.
    The array is exclusively referenced from {\C \_desc}.
    The array provides the bidirectional mapping of symbolic and numeric values.
  \item structured types with members (SET, SEQUENCE, CHOICE) get an additional static {\C \_mdescs[]} member.
    The array is exclusively referenced from {\C \_desc}.
    The array references the components' type descriptions (their {\C \_desc} data members).
  \item every class gets a virtual {\C \_getref()} function.
    Its only purpose is to return the address of {\C \_desc}\footnote{
      This sounds as if virtual data members were a nice idea, and in fact they are.
      The C++ standards committees are currently discussing this.
    %  Of course, there are not a lot of compilers already implementing this feature.
    }.
  \item structured types with members (SET, SEQUENCE, CHOICE) get an additional virtual {\C \_getref()} function.
    This function provides the member name to member address mapping.
\end{itemize}
The data members are {\C static}, and therefore get instantiated exactly once per executable, not once per class object instance.

To get an impression, let us have a look at an example:
The two ASN.1 types
\begin{ASNcode}
File ::= SET\\
\{\+\\
  name [0] PrintableString,\\
  contents [1] OCTET STRING,\\
  checksum [2] INTEGER OPTIONAL,\\
  read-only [3] BOOLEAN DEFAULT FALSE\-\\
\}\\
\\
Directory ::= SET\\
\{\+\\
  name PrintableString,\\
  files SET OF File\-\\
\}
\end{ASNcode}

get turned into two individual C++ classes:

\begin{Ccode}
class File: public AsnType\\
\{\\
public:\+\\
  PrintableString               \>\>name;\\
  AsnOcts               \>\>contents;\\
  AsnInt                \>\>*checksum;\\
  AsnBool               \>\>*read\_only;\\
\\
\<\#if META\\
  static const AsnSetTypeDesc   \>\>\_desc;\\
  static const AsnSetMemberDesc \>\>\_mdescs[];\\
  const AsnTypeDesc             \>\>*\_getdesc() const;\\
  AsnType                       \>\>*\_getref (const char *membername, bool create = false);\\
\<\#endif // META\\
\\
  // \dots other functions omitted\dots\-\\
\};\\
\\
class Directory: public AsnType\\
\{\\
public:\+\\
  PrintableString               \>\>name;\\
  DirectorySetOf                \>\>files;\\
\\
\<\#if META\\
  static const AsnSetTypeDesc   \>\>\_desc;\\
  static const AsnSetMemberDesc \>\>\_mdescs[];\\
  const AsnTypeDesc             \>\>*\_getdesc() const;\\
  AsnType                       \>\>*\_getref (const char *membername, bool create = false);\\
\<\#endif // META\\
\\
  // \dots other functions omitted\dots\\
\<\};
\end{Ccode}

The above definitions stem from the {\ufn .h} file, the following code is taken from the {\ufn .C} file.
Only the code for the {\ASN Directory} type is shown, because the code for the {\ASN File} type looks very similar.

\begin{Ccode}
\#if META\\
\\
static AsnType *createDirectory()\\
\{\\
  \>return new Directory;\\
\}\\
\\
const AsnSetMemberDesc Directory::\_mdescs[] =\\
\{\+\\
  AsnSetMemberDesc ("name", \&PrintableString::\_desc, false), // `name'\\
  AsnSetMemberDesc ("files", \&DirectorySetOf::\_desc, false), // `files'\\
  AsnSetMemberDesc()\-\\
\};\\
\\
const AsnSetTypeDesc Directory::\_desc\\
(\+\\
  \&EdEx\_StructuredModuleDesc,\\
  "Directory", // `Directory'\\
  true,\\
  AsnTypeDesc::SET,\\
  createDirectory,\\
  \_mdescs\-\\
);\\
\\
const AsnTypeDesc *Directory::\_getdesc() const\\
\{\\
  \>return \&\_desc;\\
\}\\
\\
AsnType *Directory::\_getref (const char *membername, bool create)\\
\{\+\\
  if (!strcmp (membername, "name"))\\
    \>return \&name;\\
  if (!strcmp (membername, "files"))\\
    \>return \&files;\\
  return NULL;\-\\
\}\\
\\
\#endif // META
\end{Ccode}

The two ASN.1 types get turned into two individual C++ classes, but their {\C \_desc} members point to two different instances of the same type.
The C++ backend generates an individual C++ class for every structured ASN.1 type.
The metacode is different: 
The types of the descriptions data members ({\C \_desc}, {\C \_nmdesc} and {\C \_mdesc}) despend on the \emph{general} ASN.1 type (i.e. e.g. {\ASN SET}, not {\ASN File}).







%leftover:----------------
Every {\ufn .asn1} file contains an ASN.1 module and gets translated into a {\ufn .C} file that contains an array that lists all the module's type descriptions.

Snacc generates an additional file {\ufn modules.C} that contains an array that lists all the module descriptions.
This single top level array provides the well known entry point for the whole module and type hierarchy.
%leftover:----------------





The type of the {\C \_desc} member differs depending on the ASN.1 type it describes.
The different {\C \_desc} types mirror the {\C AsnType} class hierarchy.
For example, the ASN.1 BOOLEAN type is mapped into a C++ class called {\C AsnBool} and is described by a C++ class {\C AsnBoolTypeDesc}.
The root of the {\C \_desc} class hierarchy is called {\C AsnTypeDesc} and looks as follows
(taken from {\ufn \dots/c++-lib/inc/meta.h}):
\begin{Ccode}
struct AsnTypeDesc\\
\{\+\\
  const AsnModuleDesc		\>\>*module;\\
  const char			\>\>*const name;	// NULL for basic types\\
  const bool			\>\>pdu;\\
  const enum Type \qquad // NOTE: keep this enum in sync with the typenames[]\\
  \{\+\\
    VOID,\\
    ALIAS,\\
\\
    INTEGER,\\
    REAL,\\
    NUL\_, // sic! (can't fight the ubiquitous NULL \#define)\\
    BOOLEAN,\\
    ENUMERATED,\\
    BIT\_STRING,\\
    OCTET\_STRING,\\
    OBJECT\_IDENTIFIER,\\
\\
    SET,\\
    SEQUENCE,\\
    SET\_OF,\\
    SEQUENCE\_OF,\\
    CHOICE,\\
    ANY,\-\\
  \}				\>\>type;\\
\\
  static const char		\>\>*const typenames[];\\
\\
				\>\>AsnTypeDesc (const AsnModuleDesc *, const char *,\\
					\`bool ispdu, AsnType *(*create)(), Type);\\
\\
  AsnType			\>\>*(*create)();\\
\\
  virtual const AsnModuleDesc	\>\>*getmodule() const;\\
  virtual const char		\>\>*getname() const;\\
  virtual bool			\>\>ispdu() const;\\
  virtual Type			\>\>gettype() const;\\
  virtual const AsnNameDesc	\>\>*getnames() const;\\
  %//virtual const AsnMemberDesc	\>\>*getmembers() const;\\
\\
\<\#if TCL\\
  virtual int			\>\>TclGetDesc (Tcl\_DString *) const;\\
  virtual int			\>\>TclGetDesc2 (Tcl\_DString *) const;\-\\
\#endif\\
\};\\
\\
typedef AsnTypeDesc		\>\>\>AsnRealTypeDesc;\\
typedef AsnTypeDesc		\>\>\>AsnNullTypeDesc;\\
typedef AsnTypeDesc		\>\>\>AsnBoolTypeDesc;\\
\\
typedef AsnTypeDesc		\>\>\>AsnOctsTypeDesc;\\
typedef AsnTypeDesc		\>\>\>AsnOidTypeDesc;
\end{Ccode}

{\C AsnTypeDesc}'s data members provide the following information:
\begin{description}
  \item[\textnormal{The} {\C module}] data member points to the module description described at the end of this chapter in section~\ref{meta-modules-sect}.

  \item[\textnormal{The} {\C name}] is either the type's name as used by the backend code (default, or if snacc was called with the {\ufn -mC} switch) or the type's given names as defined in the {\ufn .asn1} file (if snacc has been called with the {\ufn -mA} command line option).
    Section~\ref{naming-C++-section} on page~\pageref{naming-C++-section} explains the differences.
    (For example, the backend code sometimes has an additional number tacked to the name---you can see the effect in figure~\ref{snacced-example} on page~\pageref{snacced-example}.)
    The generated source code contains the respective counterpart printed in a comment.

  \item[\textnormal{The} {\C pdu}] flag is set to {\C true} iff the type was listed after snacc's {\ufn -meta} or {\ufn -tcl} switch.

  \item[\textnormal{The} {\C type}] member is used as an index into the {\C typenames} array---the virtual function call mechanism obliviates the use for any {\C switch} statements.

  \item[\textnormal{The} {\C create}] data member points to a global function that returns a pointer to a newly allocated object of the description type's mirror type, that is, gives you an instance for the generic description.
    It is the counterpart to the {\C AsnType}'s {\C \_getdesc} function which goes in the opposite direction, from the object instance to its generic description.
    The {\C AsnType}'s {\C Clone} function serves a similar purpose as the {\C AsnTypeDesc}'s {\C create} function.
\end{description}

The {\C AsnTypeDesc} class is the only class in the hierarchy that has got the {\C module}, {\C name}, {\C pdu} and {\C type} data members, and {\C AsnNamesTypeDesc} the only class to implement a {\C names} data member.
Therefore, unlike {\C \_getdesc()} mentioned above, the five virtual functions {\C getmodule}, {\C getname}, {\C ispdu}, {\C gettype} and {\C getnames} are not meant to implement some kind of virtual data members, but help to implement the alias type description functionality described in section~\ref{meta-aliases}.

As you can see looking at the last five code lines with the {\C typedef}s, the five ASN.1 simple types REAL, NULL, BOOLEAN, OCTET STRING and OBJECT IDENTIFIER are directly described by instances of this class.
The other types, having either named values or components, are more demanding and have their own classes derived from {\C AsnTypeDesc}.

%-----------------------------------------------------------------------------------------------------------------------------------
\subsection{Named Values}

Some basic ASN.1 types allow values to be named, namely INTEGER, ENUMERATED and BIT STRING.
The accompanying description types contain arrays listing the names and values.
The virtual function {\C getnames()} returns this array.
The respective C++ classes in the runtime library contain an {\C AsnNameDesc \_nmdescs[]} array, the address of which is given to the type descriptions constructor as last argument.

\begin{Ccode}
struct AsnNameDesc\\
\{\+\\
  const char			\>\>*const name;\\
  const long int		\>\>value;\-\\
\};\\
\\
struct AsnNamesTypeDesc: AsnTypeDesc\\
\{\+\\
  const AsnNameDesc		\>\>*const names;\\
\\
				\>\>AsnNamesTypeDesc (const AsnModuleDesc *, const char *,\\
					\`bool ispdu, AsnType *(*create)(), Type, const AsnNameDesc *);\\
\\
  const AsnNameDesc		\>\>*getnames() const;\\
\\
\<\#if TCL\\
  int				\>\>TclGetDesc (Tcl\_DString *) const;\\
  // for BIT STRING and INTEGER, ENUMERATED has its own:\\
  int				\>\>TclGetDesc2 (Tcl\_DString *) const;\-\\
\#endif\\
\};\\
\\
struct AsnEnumTypeDesc: AsnNamesTypeDesc\\
\{\\
  \>				\>\>AsnEnumTypeDesc (const AsnModuleDesc *, const char *,\\
					\`bool ispdu, Type, AsnType *(*create)(), const AsnNameDesc *);\\
\\
\#if TCL\\
  \>int				\>\>TclGetDesc2 (Tcl\_DString *) const;\\
\#endif\\
\};\\
\\
typedef AsnNamesTypeDesc	\>\>\>AsnIntTypeDesc;\\
typedef AsnNamesTypeDesc	\>\>\>AsnBitsTypeDesc;
\end{Ccode}

The ENUMERATED type gets its own description class because the Tcl interface for ENUMERATED types behaves differently than for the INTEGER and BIT STRING types.

As for {\C AsnTypeDesc::name} above, the content of {\C AsnNameDesc::name} is either the value's name as used by the backend code (default, or if snacc was called with the {\ufn -mC} switch) or the value's name as given in the {\ufn .asn1} file (if snacc has been called with the {\ufn -mA} command line option).
The generated source code contains the respective counterpart printed in a comment.

\subsection{Types with Members}

The ASN.1 types CHOICE, SET and SEQUENCE are defined in terms of other types, their so-called components.
The ASN.1 components map into C++ data members.

The three ASN.1 structured types get mapped into C++ classes that contain an {\C Asn\dots{}MemberDesc \_mdescs[]} array (with the `{\C \dots}' replaced by `{\C Choice}' `{\C Set}' or `{\C Sequence}').
The address of this array is given to the description type's constructor as last argument.
The elements of this array point to the descriptions of the data classes data members.
This is similar to the named values above, only the integral value has been replaced by a pointer to a type description.

\begin{Ccode}
struct AsnMemberDesc // description of CHOICE member; base class for AsnSe\_MemberDesc\\
\{\+\\
  const char			\>\>*const name;\\
  const AsnTypeDesc		\>\>*const desc;\\
\\
				\>\>AsnMemberDesc (const char *, const AsnTypeDesc *);\\
				\>\>AsnMemberDesc();\\
\\
\<\#if TCL\\
  virtual int			\>\>TclGetDesc (Tcl\_DString *) const;\\
  virtual int			\>\>TclGetDesc2 (Tcl\_DString *) const;\-\\
\#endif\\
\};\\
\\
struct AsnSe\_MemberDesc: AsnMemberDesc\qquad// \_ == t/quence; description of SET or SEQUENCE member\\
\{\+\\
  bool				\>\>optional;\\
\\
				\>\>AsnSe\_MemberDesc (const char *, const AsnTypeDesc *, bool);\\
				\>\>AsnSe\_MemberDesc();\\
\-\\
\#if TCL\\
  \>int				\>\>TclGetDesc2 (Tcl\_DString *) const;\\
\#endif\\
\};\\
\\
typedef AsnMemberDesc		\>\>\>AsnChoiceMemberDesc;\\
typedef AsnSe\_MemberDesc	\>\>\>AsnSetMemberDesc;\\
typedef AsnSe\_MemberDesc	\>\>\>AsnSequenceMemberDesc;\\
\\
struct AsnMembersTypeDesc: AsnTypeDesc\\
\{\+\\
				\>\>AsnMembersTypeDesc (const AsnModuleDesc *, const char *,\\
					\`bool ispdu, AsnType *(*create)(), Type);\\
\\
\<\#if TCL\\
  int				\>\>TclGetDesc (Tcl\_DString *) const;\-\\
\#endif\\
\};\\
\\
struct AsnChoiceTypeDesc: AsnMembersTypeDesc\\
\{\+\\
  const AsnChoiceMemberDesc	\>\>*const members;\\
\\
				\>\>AsnChoiceTypeDesc (const AsnModuleDesc *, const char *,\\
					\`bool ispdu, AsnType *(*create)(), Type, const AsnChoiceMemberDesc *);\\
\\
  int				\>\>choicebyname (const char *name) const;\\
  const char			\>\>*choicebyvalue (int value) const;\\
\\
\<\#if TCL\\
  int				\>\>TclGetDesc2 (Tcl\_DString *) const;\-\\
\#endif\\
\};\\
\\
struct AsnSe\_TypeDesc: AsnMembersTypeDesc\qquad// \_ == t/quence\\
\{\+\\
  const AsnSe\_MemberDesc	\>\>*const members;\\
\\
				\>\>AsnSe\_TypeDesc (const AsnModuleDesc *, const char *,\\
					\`bool ispdu, AsnType *(*create)(), Type, const AsnSe\_MemberDesc *);\\
\\
\<\#if TCL\\
  int				\>\>TclGetDesc2 (Tcl\_DString *) const;\-\\
\#endif\\
\};\\
\\
typedef AsnSe\_TypeDesc		\>\>\>AsnSetTypeDesc;\\
typedef AsnSe\_TypeDesc		\>\>\>AsnSequenceTypeDesc;
\end{Ccode}

As for {\C AsnTypeDesc::name} above, the content of {\C AsnMemberDesc::name} is either the member's name as used by the backend code (default, or if snacc was called with the {\ufn -mC} switch) or the component's name as defined in the {\ufn .asn1} file (if snacc has been called with the {\ufn -mA} command line option).
The generated source code contains the respective counterpart printed in a comment.
In case the ASN.1 component was not given a name, the backend's member name is used instead.

The data classes have a member function called {\C \_getref}, that allows the C++ class members to be accessed by their name.
{\C \_getref()} is the second metacode function and it is present in all C++ classes representing composed ASN.1 types.

A class for a SET contains the following code fragment:
\begin{Ccode}
class FooSet: public AsnType\+\\
  AsnInt	\>\>bar; // an example data member\\
  \dots // lots of member functions\\
\<\#if META\\
  static const AsnSetTypeDesc   \>\>\_desc;\\
  static const AsnSetMemberDesc \>\>mdescs[];\\
  const AsnTypeDesc             \>\>*\_getdesc() const;\\
  AsnType                       \>\>*\_getref (const char *membername, bool create = false);\\
\<\#if TCL\\
  int                \>\>TclGetDesc (Tcl\_DString *) const;\\
  int                \>\>TclGetVal (Tcl\_Interp *) const;\\
  int                \>\>TclSetVal (Tcl\_Interp *, const char *valstr);\\
  int                \>\>TclUnsetVal (Tcl\_Interp *, const char *membername);\-\\
\#endif // TCL \\
\#endif // META\\
\};
\end{Ccode}

{\C \_getref()}'s {\C bool} parameter {\C create} determines whether a non-existing member should be returned as a {\C NULL} pointer or whether it should instead be allocated and its address be returned.
This parameter is used by value reading and writing routines to implement their different member access semantics.

The following four assignments are equivalent:
\begin{Ccode}
FooSet foo;\\
foo.bar = 1;\\
*(AsnInt *)foo.\_getref ("bar") = 1;\\
foo.bar.TclSetVal (interp, "1");\\
foo.\_getref ("bar")-->TclSetVal (interp, "1");
\end{Ccode}
{\C TclSetVal()} is a virtual member function and therefore no cast from {\C AsnType~*} to {\C AsnInt~*} is required.
The Tcl interface will be described in chapter~\ref{tcl-if-chapter}.

The C++ classes that represent CHOICE types contain an {\C enum ChoiceIdEnum} that allows {\C \_getref()} to be written using a {\C switch} statement.
The functions {\C choicebyname()} and {\C choicebyvalue()} turn the component's name into its enumeration value and vice versa.
(The enum has not been introduced with the metacode, it is used by Snacc's encoding and printing functions as well.)

\subsection{\label{meta-list}SET OF and SEQUENCE OF}

The list description behaves like an ASN.1 simple type's---the description type is derived directly from the type descriptions' base class and does not redefine any of the metacode functions:

\begin{Ccode}
struct AsnListTypeDesc: AsnTypeDesc\\
\{\+\\
  const AsnTypeDesc		\>\>*const base;\\
\\
  				\>\>AsnListTypeDesc (const AsnModuleDesc *, const char *,\\
					\`bool ispdu, Type, AsnType *(*create)(), const AsnTypeDesc *);\-\\
\\
\#if TCL\\
  \>int				\>\>TclGetDesc (Tcl\_DString *) const;\\
\#endif\\
\};
\end{Ccode}

The {\C TclGetDesc} function merely adds the base type's standard type description (module and type name, pdu flag and type) after its own, so that a programmer may take the base type's name and ask the metacode once again for the base type's full description.

A list type's data class on the other hand has got a {\C \_getref()} function that gives access to the list's elements and it can be used to insert new elements at any desired position.

\subsection{\label{meta-aliases}Aliases}

For ASN.1 types being defined as a direct copy of another type, snacc in normal operation uses a C++ {\C typedef} to define the C++ type.
Since this {\C typedef} makes the two types totally equivalent, the metacode has no chance to preserve the two types' different names and thus, this contruct cannot be used.
A new C++ class has got to be defined instead.

Example: the following ASN.1 code snippet\dots
\begin{ASNcode}
Int1 ::= INTEGER \{ foo(42) \}\\
Int2 ::= Int1
\end{ASNcode}
\dots maps into the following C++ definitions:
\begin{Ccode}
class Int1: public AsnInt\\
\{\\
public:\+\\
                        \>\>Int1(): AsnInt() \{\}\\
                        \>\>Int1 (int i): AsnInt (i) \{\}\\
  enum\\
  \{\\
    \>foo = 42\\
  \};\\
\\
\<\#if META\\
  static const AsnNameDesc      \>\>\_nmdescs[];\\
  static const AsnIntTypeDesc   \>\>\_desc;\\
  const AsnTypeDesc     \>\>*\_getdesc() const;\-\\
\#endif // META\\
\};\\
\\
\#if META\\
struct Int2: public Int1\\
\{\+\\
                        \>\>Int2(): Int1() \{\}\\
                        \>\>Int2 (int i): Int1 (i) \{\}\\
  AsnType               \>\>*Clone() const;\\
\\
  static const AsnAliasTypeDesc \>\>\_desc;\\
  const AsnTypeDesc     \>\>*\_getdesc() const;\-\\
\};\\
\\
\#else // META\\
\\
typedef Int1 \>\>\>Int2;\\
\\
\#endif // META
\end{Ccode}

The descriptor type's definition points to the reference type:

\begin{Ccode}
struct AsnAliasTypeDesc: AsnTypeDesc\\
\{\+\\
  const AsnTypeDesc		\>\>*const alias;\\
\\
				\>\>AsnAliasTypeDesc (const AsnModuleDesc *, const char *,\\
					\`bool ispdu, AsnType *(*create)(), Type, const AsnTypeDesc *);\\
\\
  const AsnModuleDesc		\>\>*getmodule() const;\\
  const char			\>\>*getname() const;\\
  bool				\>\>ispdu() const;\\
  Type				\>\>gettype() const;\\
  const AsnNameDesc		\>\>*getnames() const;\\
  %//const AsnMemberDesc		\>\>*getmembers() const;\\
\\
\<\#if TCL\\
  int				\>\>TclGetDesc (Tcl\_DString *) const;\-\\
\#endif\\
\};
\end{Ccode}

The {\C AsnAliasTypeDesc} is the reason for the five virtual functions from {\C getmodule} to {\C getnames} defined in both {\C AsnTypeDesc} and {\C AsnNamesTypeDesc} on the one hand and {\C AsnAliasTypeDesc} on the other hand.
While the alias type belongs to a different module or has another type name, and it may have another {\C pdu} flag value, its type and names array values are those of its reference type.
Therefore, {\C AsnAliasTypeDesc}'s first three functions of return the description's own values, and the latter two call their reference type's functions.

The {\C getnames} function has to be defined in the hierarchy's base class because the aliases may be defined for any type of type, not only for types with named values.

\subsection{\label{meta-any}ANY (DEFINED BY)}

ANY DEFINED BY is quite problematic.
The ASN.1 Book \cite{ASN.1Book} calls it ``a rather half-baked attempt at solution''.
Since snacc has problems with it---the user has to modify the snacc generated code---and none of our applications requires this construct, no effort has been made to implement it.

ANY itself on the other hand would be quite simple to implement---the virtual function call mechanism that is used to implement the ANY type is the basis for the metacode as well.
But again, since we have no need for the ANY type, it is as far unimplemented.
Besides that, according to the ASN.1 book, the ``use of ANY without the DEFINED BY construct is ``deprecated'' (frowned upon) by the standard''.
The next ASN.1 standard will probably not have the ANY type any more.
In the 1993 draft standard \cite{asn1:1993}, ANY and ANY DEFINED BY can be found in ``Annex I: Superseded features'', Section 3: ``The any type''.
% Macros can be found in this annex as well.

\subsection{\label{meta-modules-sect}Modules}

Every {\ufn .C} file (that corresponds to an {\ufn .asn1} file, or, an ASN.1 module), gets an array that lists all the module's types.
This array contains pointers to all the {\C \_desc} members of all classes of a module.

\begin{Ccode}
struct AsnModuleDesc\\
\{\+\\
  const char			\>\>*const name;\\
  const AsnTypeDesc		\>\>**const types;\-\\
\};\\
\\
extern const AsnModuleDesc	\>\>\>*asnModuleDescs[];
\end{Ccode}

The modules themselves are listed in yet another array, the declaration of which is shown in the preceeding line.
This array has got its own source file named {\ufn modules.C}.
This array allows all modules to be found, and every type that is defined for these modules.

%-----------------------------------------------------------------------------------------------------------------------------------
\section{Efficiency}

The metacode is designed with efficiency in mind.
The metacode is intended for interpreted interfaces and therefore does not need to be highly optimized.
On the other hand, the same object code should be useable for normal (non-metacode) tasks without loss of performance.

\subsection{Normal Operation}

The metacode does not significantly affect the normal mode of operation.
The static data members {\C \_mdescs} and {\C \_desc} do not increase the class instances' size.
The virtual function tables, which have already been present (they are used for the ANY type), get a little longer, but since these tables exist only once for every class, this difference is neglible.
The class instances reference their virtual function table with a pointer, and so the metacode does not introduce any change here.
Except for alias types, the C++ classes generated are exactly the same.
The metacode introduces a new class for alias types, but since no new data members are introduced their size stays the same; only the virtual function table pointer is different.

All normal member functions (constructor, destructor, assignment operator, encode, decode and print functions) are identical---with only one exception: if the metacode is compiled to be usable by the Tcl interface, the constructors initialize their mandatory members.

To sum it up, both code and data grow, but except for a longer loading time from disk and an increased probability for cache misses, the code will run as fast as it does without the metacode.

\subsection{Metacode}

The metacode routines are kept quite simple.
Intended to be used in conjunction with a Tcl interface, speed was not the most important concern.
Consequently, the code is optimized more towards memory usage than run time efficiency.
As an example, name to member resolution uses a linear lookup strategy instead of more elaborated algorithms like binary search or hash tables.
I think for data types that typically have up to a dozen components, more sophisticated algorithms would have been overkill.

A typical object file gets almost 20\% larger due to the metacode (the Tcl interface adds another 25\%).

%-----------------------------------------------------------------------------------------------------------------------------------
\section{\label{meta-ttab-comparison}Metacode Vs. Type Tables}

Here's a list of both the type tables' (see chapter~\ref{ttab-chapter}) and the metacode's (dis)advantages:
\begin{itemize}

  \item source code language:
  \begin{itemize}
    \item[$-$] The type tables are implemented for C only.
    \item[$-$] The metacode works only for C++.
  \end{itemize}

  \item speed:
  \begin{itemize}
    \item[$-$] Encoding and decoding using the type tables is said to be about 4 times slower than using the C routines.
    \item[$+$] The metacode does not (significantly) harm performance.
  \end{itemize}

  \item code size:
  \begin{itemize}
    \item[$+$] The tables are a lot smaller than the compiled routines.
    \item[$-$] The metacode makes the compiled code even larger.
  \end{itemize}

  \item value constants:
  \begin{itemize}
    \item[$-$] The type tables lack the values defined in the {\ufn .asn1} files.
    \item[$+$] The metacode interacts fine with these values.
  \end{itemize}

  \item named values:
  \begin{itemize}
    \item[$-$] The type tables lack the named values defined ENUMERATED, INTEGER and BIT STRING types.
    \item[$+$] The metacode interacts fine with these names.
  \end{itemize}

  \item compatibility to normal snacc code:
  \begin{itemize}
    \item[$-$] The C structures defined by mkchdr and used by the type table encoding and decoding routines and the C structures defined by snacc's C backend are quite different.
    \item[$\pm$] Where the backend's structures generated for SEQUENCE contain madatory members by value, the type table's structures contain only pointer members!
  \end{itemize}

\end{itemize}

%-----------------------------------------------------------------------------------------------------------------------------------
\section{\label{metacode-setup}Setup for the Metacode Generator}

To compile Snacc with the metacode generator, the following condition must be met:
\begin{itemize}
  \item either the configure script must be able to find {\ufn tclsh} and the Tcl/Tk libraries or you have to insert a {\C \#define META 1} into {\ufn \dots/policy.h}
  \item the {\C NO\_META} preprocessor macro in {\ufn \dots/policy.h} must not be set
\end{itemize}