Most Sh types support storing metadata with their instances. This is data that is not necessary for the object itself to function, but provides potentially useful information relating to the object that might be used by other parts of the system or by the user. Sh automatically generates some metadata (such as the type), but other kinds of metadata are specified by the user.
There are two major reasons to have metadata in Sh. The first is documentation. By providing information such as a name, a title, and a description of a tuple, a user interface containing widgets to manipulate uniform parameters and attributes can be constructed automatically, and can operate on arbitrary programs by querying the metadata. This sort of information can also be useful for debugging purposes.
The second major reason to have metadata is to provide information to the backends. Because the Sh frontend is designed to be as separate from its backends as possible, we do not wish to expose interfaces that rely on particular backend properties at the API level. To provide a clean separation, we allow backends to define and access arbitrary string-based metadata that can be attached to objects with special-purpose meanings. Backend can then be updated or changed without having to change the Sh API and hence without breaking backwards compatibility. This comes at the cost of having to establish conventions for naming this metadata. We expect backends to document the metadata they can make use of appropriately, and to use common conventions whenever possible.
/ All types containing metadata inherit from the ShMeta class. This class provides the following member functions:
Note that for “thin wrapper” classes, such as ShVariable and ShProgram, the above member functions are defined to forward any queries to the actual reference-counted node object to which they refer. / This is done by inheriting from the ShMetaForwarder class.
Variables in C++ are usually named. It would be nice if Sh could return these variable names by default when name() is called. Unfortunately C++ has no standard way to access these names at run-time.
It would be theoretically possible to open up a program’s executable file at runtime and search through the symbol table to find the name corresponding to a particular variable address. However, such code would be extremely unportable, and there are many cases where this would not work (e.g. if debugging information were not present in the executable, and possibly in the case of shared libraries).
Another alternative would be to preprocess the source somehow and add in code to mark variable declarations with appropriate calls to name(). This is non-trivial though and adds another level of indirection to the compilation process. We specifically try to avoid having to add external dependencies when using Sh, as one of its prime advantages is that it is merely a C++ library that can easily be added to a project.
Therefore, for portability reasons, we currently recommend that the name() property be set explicitly. Names are used for several introspection purposes besides documentation, and these names unfortunately cannot be set automatically in a reliable fashion.1 Sh provides several macros that make assigning names to objects more convenient. These macros are:
Each of these take some variable and name it using the name as it appears in the source program. SH_NAME is intended to be used at some point after declaring a variable. It simply sets the given variable’s name to its C++ name. Writing SH_NAME(foo) is somewhat nicer than having to write foo.name("foo"), and avoids mistakes like foo.name("bar").
The SH_DECL and SH_NAMEDECL macros allow a variable to be named at the same time as it is declared in a function (but not, unfortunately, in a class). The SH_NAMEDECL allows the metadata name to differ from the actual C++ variable name.
Here’s an example of the different ways in which variables can be named:
We recommend that you get into the habit of using SH_DECL wherever possible. This will save you from having to later go back and add the appropriate calls.
One limitation of these macros is that they cannot be used for class member declarations. You have to set the name in the constructor, since methods cannot be called at the same time as declarations in classes.
Note: This manual is available as a bound book from AK Peters, including better formatting, in-depth examples, and about 200 pages not available on-line.