This document is an informal tutorial. For an in-depth description of the ClassAd language, see the reference manual. If there is a discrepancy between this tutorial and the reference manual, then the reference manual is correct. If there is a discrepancy between the code and the reference manual, the reference manual is correct and there is a bug in the code.
For detailed documentation on the C++ ClassAd API, see the API documentation.
[ id0 = expr0 ; id1 = expr1 ; ... ; idn = exprn ]where each id is a valid identifier and each expr is an expression. Identifiers begin with an alphabetic character or an underscore, and are followed with any number of alphabetic, numeric, or underscore characters. Identifiers are case-insensitive, but case-preserving. That is, if you refer to "Rank" or "rANK", it refers to the same attribute, but it will print with the same case that it was created with.
Since classads are themselves expressions, classads can be hierarchically nested, as illustrated in the following example.
[ a = 7 ; b = "foo" ; c = [ a = -1 ; b = "bar" ] ]The outermost classad is referred to the root scope. Every other classad is contained in its parent scope, which is the unique classad that immediately contains the classad in question.
Classads are self-evaluating structures: a classad "expression" evaluates to a classad "value."
The following important points must be noted:
|
|
{ e1, e2 ... , en } where
each 'e' is an expression. Components of lists may be accessed via the
subscript operator as in:{ 10, "foo", -3.14 }[0] which evaluates
to the integer value 10.
The syntax of a function call is
name( arg0, arg1, ... , argn )As with operators, most functions are strict with respect to undefined and error on all arguments. However, some functions are non-strict. The name of the function is case-insensitive. The supported set of functions are:
| Type predicates (Non-Strict) | |
| isUndefined(V) | True if and only if V is the undefined value. |
| isError(V) | True if and only if V is the error value. |
| isString(V) | True if and only if V is a string value. |
| isList(V) | True if and only if V is a list value. |
| isClassad(V) | True if and only if V is a classad value. |
| isBoolean(V) | True if and only if V is a boolean value. |
| isAbsTime(V) | True if and only if V is an absolute time value. |
| isRelTime(V) | True if and only if V is a relative time value. |
| List Membership | |
| member(V,L) | True if and only if scalar value V is a member of the list L |
| identicalMember(V,L) | Like Member, but uses is for comparison instead of ==. Not strict on first argument. |
| Time | |
| time() | Returns the current Coordinated Universal Time, in seconds since midnight January 1, 1970. |
| currentTime() | Get current time as an absolute time |
| timeZoneOffset() | Get time zone offset as a relative time |
| dayTime() | Get current time as relative time since midnight. |
| splitTime(T) | Creates a ClassAd with each component of the time (absolute or relative) as an element of the ClassAd |
| formatTime(T, S) | Formats an absolute time according the the strftime-style format. See the reference manual for details. |
| String Functions | |
| strcat(V1, ... , Vn) | Concatenates string representations of values V1 through Vn |
| toUpper(S) | Converts the string S to uppercase |
| toLower(S) | Converts the string S to lowercase |
| substr(S,offset [,len]) | Returns substring of S. Negative offsets and lengths count from the end of the string. |
| regexp(P,S) | Checks if S matches pattern P (both args must be strings). For details on the options other than "i" for case-insensitive matching, see the reference manual. |
| Type Conversion Functions | |
| int(V) | Converts V to an integer. Time values are converted to number of seconds, strings are parsed, bools are mapped to 0 or 1. Other values result in error. |
| real(V) | Similar to int(V), but to a real value. |
| string(V) | Converts V to its string representation |
| bool(V) | Converts V to a boolean value. Empty strings, and zero values converted to false; non-empty strings and non-zero values converted to true. |
| absTime(V) | Converts V to an absolute time. Numeric values treated as seconds past UNIX epoch, strings parsed as necessary. See the reference manual for details. |
| relTime(V) | Converts V to an relative time. Numeric values treated as number of seconds, strings parsed as necessary. See the reference manual for details. |
| Mathematical Functions | |
| floor(N) | Floor of numeric value N |
| ceiling(N) | Ceiling of numeric value N |
| round(N) | Rounded value of numeric value N |
| random(N)> | If N is an integer, the result is an integer random number R in the range 0 <= R < N. If N is a real number, the result is a real random number in the same range.If N is anything else, the result is an error. |
[
a = 17;
b = "foo";
c = { "x", "y", 3*a };
d = [
a = 23;
b = 15;
c = [];
d = .a;
];
e = '00:15:00';
]
We now provide a few example expressions, and their resulting values when
evaluated in the context of the above classad.
| Expression | Result |
| a | 17 |
| strcat(b, "bar", a) | "foobar17" |
| x | undefined |
| x+10 | undefined |
| x || true | true |
| d.a | 23 |
| d.b | 15 |
| d.self | [ a = 23 ; b = 15 ; c = [] ; d = .a ] |
| d.c | [] |
| d.c.parent | [ a = 23 ; b = 15 ; c = [] ; d = .a ] |
| d.c.a | 23 |
| d.parent.c | { "x", "y", 3*a } |
| d.parent.c[2] | 51 |
| d.d | 17 |
| e*4 | '01:00:00' |
ClassAds are really a special case of expressions, so the next section will explain how to create ClassAds. However, since ClassAds are often the primary concern of programmers that use the ClassAd library, we will explain them separately here.
The most common method to create ClassAds is with the ClassAd parser. The parser can take text input (a string, a file, or a stream) and convert it to a ClassAd. The parser is encapsulated in the ClassAdParser class, which can be found in source.h. Normally you won't need to include source.h directly, since it is included by classad_distribution.h.
The ClassAdParser has two prototypical ways of creating a ClassAd, though there are several variants with each method. For this tutorial, we'll create ClassAds from strings: the variants merely let you construct the ClassAds from different sources.
Method One: Return a string
string classad_string = "[a = 1; b = \"Cardini\"]"; ClassAd *classad; ClassAdParser parser; classad = parser.ParseClassAd(classad_string, true);In this example, true is passed to ParseClassAd to indicate that no extra text follows the ClassAd. If it does, it will return an error. If you legitimately may have extra text after a ClassAd, pass false instead. If the parsing fails, NULL will be returned. You own the ClassAd, so it is up to you to delete it when you are finished with it.
Method Two: Fill in a ClassAd
string classad_string = "[a = 1; b = \"Cardini\"]"; ClassAd classad; ClassAdParser parser; bool success. success = parser.ParseClassAd(classad_string, classad, true);In this example, the classad is filled in directly. If the ClassAd already contains attributes, they are cleared. You could also allocated the ClassAd with new and pass it as *classad, if you prefer.
If you prefer, you can create and empty ClassAd, then fill it up with expressions that you insert on the fly.. Here is an example:
ClassAd *classad;
classad = new ClassAd;
// Insert a = 1
classad->InsertAttr("a", 1);
// Insert b = "Cardini"
classad->InsertAttr("b", "Cardini");
However, users rarely directly invoke factory methods since expressions may usually be constructed through more convenient means. Expressions can be created in three ways.
ExprTree *now = Literal::MakeAbsTime(); // current time value
ExprTree *past = Literal::MakeAbsTime("1999-01-13T09:00:00-0600");
ExprTree *tenMinutes = Literal::MakeRelTime(600); // in seconds
The Literal::MakeRelTime() factory also has a variant which accepts two
time_t values and constructs the interval between their values. If -1
is passed for either argument, the current time is used in that place.
ClassAdParser parser;
ExprTree *tree;
ClassAd *ad;
if( !( tree = parser.ParseExpression( "10 * 17 / foo.bar" ) ) ||
!( ad = parser.ParseClassAd( "[ a=3 ; b=5 ]" ) ) ) {
// error
...
} else {
ad->Insert("c", tree);
}
ClassAd ad;
if( !ad.InsertAttr( "Memory", 128, Value::M_FACTOR )|| // Memory=128M
!ad.InsertAttr( "Name", "foo.cs.wisc.edu" ) || // Name="foo.cs.wisc.edu"
!ad.InsertAttr( "IsBlue", false ) ) { // IsBlue=false
// error
...
}
}
ExprTree *now;
ClassAd ad;
if( !( now = Literal::MakeAbsTime() ) ||
!(ad.Insert( "CurrentTime", now ) ) ) {
// error
...
}
If a similarly named expression (case-insensitive) already exists in
the ad, the old expression is deleted, and the new expression takes
its place. Once inserted, the storage associated with the expression
is adopted by the classad---do not deallocate the storage of the
expression yourself. If you will need direct access to the
expression after you insert it, use the Copy() method first to make a
private copy as follows:
ExprTree *original_expression, *private_copy; ... // Assume original_expression has been created private_copy = original_expression->Copy( ); // Now we insert the original_copy ad.Insert( "foo", original_copy); // At this point, we shouldn't delete the original_copy // because it is owned by the ClassAd, but we can manipulate // the private_copy as much as we like.
ClassAd ad;
if( !ad.InsertAttr( "foo", 10 ) || !ad.InsertAttr( "bar", 20 ) ) {
// error
...
}
if( !ad.Delete( "foo" ) ) {
// attr not found in ad
}
ExprTree *tree = ad.Remove( "bar" );
// "tree" is now yours (NULL if ad had no "bar")
...
delete tree;
Once expressions have been inserted into classads they may be looked up with the Lookup() method. The method returns the expression (or NULL if no such attribute exists in the classad), but the expression still belongs to the classad. Thus, the expression must not be deallocated, or its parent scope reset.
ClassAd ad; ExprTree *tree; ad.InsertAttr( "foo", 10 ); tree = ad.Lookup( "foo" );If the classad being used is nested in another classad, the LookupInScope() method may be used to test if the expression exists in the classad, or any of its parent scopes. Often you will prefer to evaluate expressions instead of looking them up. See below for more details.
Expressions in the classad may also be enumerated by means of an iterator as illustrated below.
ClassAd ad;
...
// assume the ad is filled with attributes
ClassAdUnParser unp;
string buffer;
ClassAdIterator itr;
string attrName;
const ExprTree *attrExpr;
itr.Initialize( ad );
while( !itr.IsAfterLast( ) ) {
itr.CurrentAttribute( attrName, attrExpr );
buffer += attrName + " = ";
unp.Unparse( buffer, attrExpr );
buffer += " ";
itr.NextAttribute( attrName, attrExpr );
}
You can also use STL-style iterators to iterate through a ClassAd.
ClassAd ad;
...
// assume the ad is filled with attributes
ClassAd::iterator iter;
iter = ad->begin();
while (iter != ad->end()) {
string name = iter->first;
ExprTree *tree = iter->second;
...
iter++;
}
Expressions already in the classad may be evaluated via the EvaluateAttr() method.
ClassAd ad;
Value val;
int fooVal;
if( !ad.InsertAttr( "foo" ,10 ) || !ad.EvaluateAttr( "foo", val ) ) {
// error
...
}
if( !val.IsIntegerValue( fooVal ) ) {
// enh???
}
In the above example, the result of the evaluation was already expected to be
an integer. Thus, the following shortcut may be used.
if( !ad.EvaluateAttrInt( "foo", fooVal ) ) {
// eval error, or not an int
...
}
Similar convenience methods exist for all the other types.
Expressions which have not been inserted into the classad may be evaluated in the context of the classad via the EvaluateExpr() method. However, the expression must be notified of the scope that it is being evaluated in, as illustrated in the following example. (Return values are not checked to keep the example brief.)
ClassAd ad; ClassAdParser parser; ExprTree *tree; Value val; ad.InsertAttr( "foo", 10 ); ad.InsertAttr( "bar", 20 ); tree = parser.ParseExpression( "foo - bar" ); tree->SetParentScope( &ad ); // to be evaluated in the context of ad ad.EvaluateExpr( tree, val ); // now evaluate the expression
The MatchClassAd object extends a classad by providing two additional functionalities:
MatchClassAd mad;
ClassAd *mach, *job;
...
// assume mach and job are two valid classads
// the following is not necessary if no ads were inserted
mad.RemoveLeftAd( );
mad.RemoveRightAd( );
// insert the two ads into the context
mad.ReplaceLeftAd( mach );
mad.ReplaceRightAd( job );
// test
bool match
if( !mad.EvaluateAttrBool( "symmetricMatch" match ) || !match ) {
// they don't like each other
...
} else {
// they like each other
...
}
For your information, the MatchClassAd sets itself up as follows:
[
symmetricMatch = leftMatchesRight && rightMatchesLeft;
leftMatchesRight = adcr.ad.requirements;
rightMatchesLeft = adcl.ad.requirements;
leftRankValue = adcl.ad.rank;
rightRankValue = adcr.ad.rank;
adcl =
[
other = .adcr.ad;
my = ad; // for condor backwards compatibility
target = other; // for condor backwards compatibility
ad =
[
// the ``left'' match candidate goes here
]
];
adcr =
[
other = .adcl.ad;
my = ad; // for condor backwards compatibility
target = other; // for condor backwards compatibility
ad =
[
// the ``right'' match candidate goes here
]
];
]
ClassAds, expressions and values are displayed by first "unparsing" them into STL strings and then printing them to the screen. The same mechanism is used to transport classads over the network: they are first unparsed into a string which is transported over the network, and then parsed back by the receiver.
Unparsing is performed by the ClassAdUnParser object. The interface is straightforward.
ClassAdUnParser unp; ExprTree *tree; ClassAd ad; Value val; ... // assume tree, ad, val need to be unparsed string buffer1, buffer2, buffer3; unp.Unparse( buffer1, tree ); unp.Unparse( buffer2, &ad ); unp.Unparse( buffer3, val );A more sophisticated unparser called PrettyPrint is also available. The pretty printer can indent classads and lists, and display parenthesized expressions with the minimal number of required parentheses.
PrettyPrint pp; ExprTree *tree; ClassAd ad; Value val; ... // assume tree, ad, val need to be unparsed string buffer1, buffer2, buffer3; pp.Unparse( buffer1, tree ); pp.Unparse( buffer2, &ad ); pp.Unparse( buffer3, val );
ClassAds can be displayed and parsed in XML as well as the native format used above. For example, a ClassAd in XML format might look like this:
<?xml version="1.0"?> <c> <a n=\"A\"><s>Alain Aslag Roy</s></a> <a n=\"B\"><i>3</i></a> </c>
They are parsed and printed similarly to the native format described above. To parse an XML ClassAd you use the ClassAdXMLParser class:
ClassAdXMLParser parser; ClassAd *classad; string xml = ... // The ClassAd classad = parser.ParseClassAd(xml);
To print out a ClassAd in XML format you use the ClassAdXMLUnParser class:
ClassAd *classad; ClassAdXMLUnParser unparser; string printed_classad; // Assume classad is already created unparser.SetCompactSpacing(false); unparser.Unparse(printed_classad, classad);