Contents:
General
The most involved and least frequently required interface to the
XParam library is the registration interface. In contrast to the
user's interface, that is required whenever a program using
XParam is invoked, and the programmer's interface, that should be
used whenever you write a new program with XParam parameter
handling, registration is needed only when you want XParam to
learn how to use new classes, or when the interface to an already
registered class has changed.
In principle, to handle user inputs such as a=7, where a is, for example, an instance of class Duck, XParam should find the best path from 7 to a Duck variable. If Duck has a non-explicit constructor from int, that can, of course, be used. If, instead, it only has a non-explicit constructor from double, then the 7 integer should be converted to a double, and from there to Duck. If, on the other hand, the only non-explicit constructor Duck has is from char, then the 7 should not be taken as an integer value at all, but as an abbreviation of '7', using XParam's relaxed type matching. This was only a very simple example, but arbitrarily complex implicit conversion paths can also be generated by the user. Consider, for example, the user's command mentioned in the introduction:
~/bin>a.out 'my_shape=[ Circle(Point(50,50),50), Circle(Point(25,75),10), Circle(Point(75,75),10), Arc(Point(25,50), Point(50,25), Point(75,50)) ]'
To parse this line correctly, XParam needs considerable knowledge of the existing classes and the inter-connections between them.
All the information required by XParam exists as part of the classes' interface. Unfortunately, because C++ has no introspection capability, in the registration process the registrator has to make this information explicitly available for XParam's usage. As will shortly be demonstrated, the registration process itself is entirely non-intrusive, enabling third-party registration. The only restriction XParam-registered classes have is that if they are concrete, they should support a public copy constructor and be assignable. Because of this, classes can be developed by one person, entirely unaware of XParam, then registered by a second person and finally used by a third person. For this to be possible, the class interface, developed by the first person, should be available for the registering person, and code developed by the registrator and the programmer should be linked together (though it can be compiled separately) into the running program.
To emphasize this point, consider that in order to run the example quoted above, classes Circle, Arc and Point weren't necessarily even programmed at the time the main program was compiled and even executed. Trying to parse the command-line, XParam can dynamically load both the registration code and the class implementations at run-time.
The Basic Structure
In the next sections, the exact contents of registration files will
be elaborated, but the basic structure of all registration files is
exactly the same. First, note that XParam's registration files, as
was hinted at in the first section, are C++ files. They should be
compiled as any C++ file and linked with the rest of your program.
This can be confusing, because registration files don't look like
they are C++-compilable files. What they do look like, is this:
#include <xparam_extend.h> #include "point.h" #include "point_output.h" using namespace xParam; PARAM_BEGIN_REG PARAM_CLASS(Point); param_ctor<Point>(ByVal<int>("x"),ByVal<int>("y")); param_output<Point,Point_output_functor>(); param_vector<Point>(); PARAM_END_REG
To understand this format, let's go over it line by line:
#include <xparam_extend.h>
This file contains the declarations needed for XParam registration.
#include "point.h"
The interface of the registered class should be visible to the registration code.
#include "point_output.h"
Consult the section about Creators to learn about other header files you may want to #include to your registration code.
using namespace xParam;
You don't need this line, of course, but if you don't use it, you will have to add the xParam:: prefix to virtually everything in the registration code, so we highly recommend it. All our examples of registration code will assume the inclusion of this line.
Finally, we reach the registration code itself. It is denoted by a PARAM_BEGIN_REG/PARAM_END_REG block. The purpose of this block is to create an environment in which commands are executed before main() is entered. The two macros set up an anonymous namespace and define a class within that namespace, and a static instance of the class. The commands placed inside a PARAM_BEGIN_REG/PARAM_END_REG block compose the class's constructor, and are therefore executed. All XParam's registration commands are simple function calls and constructors for temporary objects. The PARAM_BEGIN_REG/PARAM_END_REG block allows these function calls to be executed and the registration to take effect before main() is reached.
You can, of course, run the registration commands by other means, but it is our opinion that this is the simplest and most straightforward way.
One PARAM_BEGIN_REG/PARAM_END_REG pair is good to put all your registration commands in, but if you want you can separate your registration commands into as many blocks as you want. This can be effective if you want to put ordinary C++ code in your registration files. The Point_output_functor, for example, could have been written directly in the Point registration file, and splitting the file into several registration blocks can help you place the output functor near the registration command relevant to it.
An XParam program can link in as many registration files as it wishes, and as many classes as you wish can be registered in the same registration block.
Ordinary C++ code should not be placed inside a registration
block, unless you want it to run prior to your main().
This is rare, but not unheard of. Here's a challenge for an
advanced XParam user: Program "param_map
In the next section we will go over all the registration
commands supplied by XParam for use in registration blocks.
PARAM_CLASS(my_class);
PARAM_CLASS is a macro. If you rather not use this
macro, you can always opt for the longer form:
param_class<my_class>("my_class");
which is what the previous line expands to. This format is
usually not used, unless you want your class to be given a
different name in its XParam user interface than it does in
C++. Though this may seem like a strange idea at first, take
into account that fully qualified class names are used much
more frequently in XParam than they are in C++ code, so it
is convenient to use a shorter form here. Here is one
example of such a usage, in XParam's own cpp files:
param_class<std::string>("string");
The STL string, known inside C++ programs as std::string, and
sometimes even as std::basic_string<char, std::char_traits<char>,
std::allocator<char> > would have been cumbersome to
use under that name. Therefore, the XParam name it has been
given is simply string. XParam would have been able to
handle this fully qualified name. However, no XParam class name can
include a modifier, such as "unsigned", "long", "const", "static" or
"volatile".
One other reason not to use the macro is when your class-name has
a comma in it, such as "pair
Because XParam tries to instantiate its classes, abstract classes can
not be registered in this way. They use
PARAM_ABSTRACT_CLASS(my_abstract_class);
which is a macro that expands to
param_abstract_class<my_abstract_class>("my_abstract_class");
Registering abstract classes is useful for using polymorphism, where
inheritance relationships are necessary.
The following types have been pre-registered by XParam. The name in
parentheses indicates the XParam-name of the class, if it differs from
the C++ name.
Note that it is impossible, in XParam, to register a template, such
as std::vector directly. However, it is possible to register
template specializations, such as std::vector<int>.
For vectors, specifically, XParam even supports special registration
commands, to make your life easier. They are elaborated in the
Vectors section. Though you can register any
template specialization you want, not every template specialization
is a legal XParam name, meaning you may not be able to use the
class registration macros. Currently, XParam can only
handle template specializations which expect class names as their
specialization parameters. That is: Matrix<int> is
allowed, but Matrix<3,4> isn't.
There are two exceptions to the "no modifiers are allowed in class names"
rule: when registering a template specialization, classes that are
part of the specialization description can include the modifier "const"
and a "*" if they are pointer types,
but no other modifier. For example: vector<const string*>
is a legal XParam class name, and one can therefore register it
directly using the registration macro:
PARAM_CLASS(vector<const string*>);
Technical Note: Class declarations are the only registration commands in
XParam that get registered immediately. For all other commands
we use a mechanism called "delayed registration". If, for example,
you want to register that B is a derived class from A,
XParam will wait with this registration command, and only enter it
after both class A and class B have been registered.
For this reason, it is important to check that all your classes are
registered properly, or else all your other registration commands
may simply be ignored.
param_inheritance(DerivedTag<my_derived_class>(),
BaseTag<my_base_class>());
The syntax includes DerivedTag and BaseTag so
as to minimize the possibility of confusion in the registration
order.
Just as in C++, if A derives from B and B derives from C, then
A derives from C. You do not need to register the A-from-C
relationship explicitly.
Note: The entire registration process is meant to register class
interfaces. If your class has private or protected inheritance from
a base class, this is naturally not a part of the class interface,
and you should therefore not register it. Another very important
detail to note about registering inheritances is that all classes
involved in an XParam inheritance relationship must have at least
one virtual method (even if it is the destructor). This makes C++
create a virtual method pointer table for the class, enables
real-time type information for the class, allows use of
dynamic_casts, and, in general, allows XParam to make
proper use of the inheritance information.
param_const(name,value);
where name is a string, and value can be a C++
variable of any type which has been registered into XParam.
XParam does not insist that your variable will be defined as
'const'. However, the value that this variable had at
registration time is the value that XParam will use.
The constant will be of the same type as that of the C++ variable.
In XParam initializations, the constant will be recognized by the
name given in name.
Usually, the name you will want to give your constant is the same
as the name you reference it in your C++ programs. To accomplish
this, XParam defines an easy interface using the PARAM_CONST
macro.
PARAM_CONST(variable);
expands to
param_const("variable",variable);
Note: It is not recommended to use dynamically loaded constants.
Though XParam does support loading constants dynamically, using
a constant in a parameter initialization will not trigger dynamic
loading. XParam will only recognize the constant if it had
already been loaded due to a missing class that had to be
dynamically loaded. For this reason, it is safest to have all your
constants statically linked to your program.
PARAM_ENUM(enum_type);
or, alternatively, by an explicit call to the registration function
it expands to:
param_enum<enum_type>("enum_type_name_in_XParam");
or, for an ISO-challanged compiler:
param_enum(TypeTag<enum_type>(), "enum_type_name_in_XParam");
The macro naturally assumes that you want to set the name of the enum_type
in XParam to be exactly what it is in C++.
To define a certain instance of your enum, all you have to do is use the
macro
PARAM_ENUM_VAL(value);
or the function it expands to:
param_enum_val("value_name_in_XParam", value);
where the value's name in C++ and the value's name in XParam are assumed
to be the same.
If this sounds complex, here is an example that will make it clearer:
That's all there is to it! The enumerator is now ready to be read from the
input as if it were a regular constant. Note, however, that there is a
difference between constants and enums in the way they are output: constants
are output by their values, so if the programmer was to write
"cout << Val(M_PI)", she can expect to get "3.1415" on the output line.
Enums, on the other hand, are output by name. Writing "cout << Wednesday",
will result in "Wednesday" being output, so that re-reading
the enum elsewhere will still give the value of Wednesday, even if the enum
is defined by a different integer there.
An exception to this rule are enums that were not declared by the registrator.
If, in the registration process, you registered "PARAM_ENUM(DOW);", but did
not continue to register its instances -- something we discourage you from
wholeheartedly -- then anyone who tries writing "cout << Wednesday" will get
"DOW(2)" in the output. Though this still works, and can even be read back
using XParam (assuming Wednesday is still represented by the number 2 in the DOW
list of the reading program) it is much less clear to a human reader, and
may be less portable. For this reason, it is always advisable to register
all your enum values.
param_ctor<Point>(ByVal<int>("x"),ByVal<int>("y"));
This particular line means: class Point has a
constructor from two integers, x and y, both passed by value.
As you can see, a major part of the description is composed
of argument passers: ByVal<int>("x") is the
XParam way of saying "an argument called x, of type
int, which is passed by value". In this section
we will go over the various argument passers supplied by
XParam and how to use them.
In XParam, arguments can be passed by value, by
constant reference or by pointer. If it is
passed by pointer, an argument can be constant or
non-constant, and the responsibility to delete it after
the end of the construction call can lie either with the
caller, or with the called constructor. This gives four
different types of pointers. In addition to this, there
are two more argument passers: AsConvertedVal and
AsCString. These will be explained later on.
An XParam argument, with the exception of AsConvertedVal and
AsCString, takes the following form:
passmode<type>("argname")
for example:
ByVal<int>("mode")
This means: the argument's name is mode,
its type is int and it should be passed
by value. The argument's name is only used for
error reporting and getting help.
Instead of ByVal, you can put ConstRef
to indicate an argument passed by constant reference,
ClassPtr and CallerPtr to indicate a
pointer that is owned by the method it was passed to
or one that is owned by the caller and should be deleted by
XParam, respectively, and ClassConstPtr and
CallerConstPtr, which are the constant
pointer equivalents of ClassPtr and CallerPtr.
XParam pointers are all allocated by new, so
ClassPtr and ClassConstPtr should be
deallocated by using delete in the called
method.
One special case is a constructor expecting a C-string
(i.e. a null-terminated array of chars).
XParam does not, natively, work with C-strings. It uses
std::string variables whether you asked for them
explicitly or whether you used constant string literals.
If you have a constructor that requires a C-string, you
will want XParam to
convert its relevant std::string to a C-string
for the constructor. This is done by AsCString, and is
denoted by
AsCString("argname")
Note that even though XParam does not
distinguish between native strings and STL strings, it
does distinguish between strings and pointers to characters,
so if your constructor expects a pointer to a character, and
not a C-string, one of the pointer argument passers is the
one you want.
Finally, if you need, for some reason, to allow an
implicit conversion at pass-time, and your compiler
warns you about this, you can use
AsConvertedVal<sourcetype,destinationtype>("argname");
This should hardly ever be needed. However, XParam's
pre-registered types use this, because implicit
conversions between almost all built-in C types are
allowed by the language.
param_ctor<registered_type>( .. list of
argument passers ... );
The list of argument passers can contain between
zero and thirteen arguments (XParam does not support
methods with more than thirteen arguments, by default.
This can be changed. See the
installation section for details).
The arguments should have the format described in
the previous section. Arguments in the list should
be separated by commas.
For example:
param_ctor<Complex>(); is the default Complex constructor.
Unfortunately, though this is ISO-C++, many compilers
will still balk at this syntax. For this reason,
XParam is also willing to accept the following format
for registering constructors:
param_ctor(TypeTag<registered_type>(), ... arglist ... );
If your compiler is ISO-challenged, it may give you some
trouble when you try to compile XParam itself. In such a case,
try compiling with "-DNO_EXPLICIT_TEMPLATE_FUNC_ARGS". This
definition tells the compiler not to try and compile the more
sophisticated registration flavor.
In XParam, as in C++, constructors can also be declared
"explicit", to prevent their usage in implicit conversion
paths. To register this, use the single-argument
constructor, in either flavor, switching param_ctor
with param_explicit_ctor. So, if we do not
wish the Complex-from-double constructor to be allowed
implicitly, all we need is to register it as
param_explicit_ctor<Complex>(ByVal<double>("real"));
Because XParam requires that all concrete classes have copy
constructors, this constructor is registered automatically
when you use "param_class" to register a concrete class.
How can that be? you ask. Well, many classes don't allow
themselves to be set up completely at construction-time.
You build them up in a certain way, and then need a little
more tweaking to get it exactly right. Consider, for example,
the std::vector. If I wanted a vector of integers to be
filled with the numbers one through five, in C++ it would
have looked like this:
std::vector<int> v;
In XParam, however, you want to do everything on a single
construction line. The person who wrote the C++ interface
could have omitted the possibility for complete construction
on a single line because she didn't think it too important,
or because (as in the case of std::vector) the appropriate
constructor isn't possible to program in C++. Either way,
XParam may have a solution for you.
What you need to do is to program a creator. A creator is
a functor class supporting the following interface:
where arglist is the list of arguments you want
to send to the creator functor. It is composed of normal
C++ arguments, not XParam argument passers.
Here's an example: a friend of mine programmed the following struct:
In C++, using this struct is no problem: you take a "Point" variable
and assign whatever value you want to it. However, in XParam you want
the initialization to be done right on the object definition line.
You therefore want to register the following creation functor:
Make sure that in your creator the pointer to your created type is
allocated using new.
Once the functor is set up, all you need to do is to register it:
param_creator<Point,Point_creator>
(ByVal<int>("_x"),ByVal<int>("_y"));
or, for an ISO-challenged compiler:
param_creator(TypeTag<Point>(),TypeTag<Point_creator>(),
ByVal<int>("_x"),ByVal<int>("_y"));
This example was perhaps a little contrived, but was given here
for simplicity. Here is a real life usage:
We want to register a creator
that would allow the creation of an
std::map<std::string,int>
from an explicit listing of its contents in the form of an
std::vector<std::pair<std::string,int> >,
what we need to do is to program the following class:
with the method implementation being, for example, this:
Armed with this, all you need is the following registration
command (assuming the rest of map, vector and pair have already
been registered):
using namespace std;
Or, for an ISO challenged compiler:
using namespace std;
As constructors, XParam creators, too, can be declared to be
"explicit". This is done by switching param_creator
with param_explicit_creator
In general, the syntax for creator registration is the following:
param_creator<my_class,my_class_creator>(...
arglist ...);
The arg and arglist, of course, are the XParam
argument-passers list that matches the argument list
given in the functor.
When registering a creator, an output function, or any other
functional object, in XParam, the registered class, or at least
its interface, must be visible to the registration code.
For this reason, if you separate the functors from the registration
code, you will want to #include their header files in
the registration file.
In the Programmer Interface
section, we mentioned the fact that, unlike in C++, XParam
does not allow you to register two constructors for ClassA,
one from a pointer to ClassB, the other from a constant
reference to ClassB. This is because XParam uses the
same syntax to signify both. If you encounter this problem,
or any other reason why XParam does not allow you to register
the constructors that you want to use, you may want to use
creators as a workaround. Consider, for example, this
creator functional object:
If you register it, you will be able to use the
Class-A-from-ClassB-pointer constructor by invoking this
creator, which receives an extra "Dummy" parameter.
If you already have the ClassA-from-const-ClassB-reference constructor
and class "Dummy" registered, this workaround allows you to
use the user syntax a=ClassB(...) to signify the
construction of ClassA from a ClassB const reference (assuming
a is a paramter of type ClassA), and to use
a=ClassA(ClassB(...),Dummy()) to signify a
ClassA-from-ClassB-pointer construction. Such workarounds
are hardly ever needed, but it is useful to know they can
be used in the unlikely case you're going to need them.
Note: there is a more elegant way of working around this problem,
and it doesn't utilize creators at all. Programming in C++,
you may sometimes find yourself adding a dummy class to your
program in order to solve an ambiguity. For example, you may
want to add the class "Length" to your program, simply to
differentiate between Vector(7) and Vector(Length(7)).
The same solution works in XParam: program and register class
WorkaroundPtr that has an explicit constructor from a
"const ClassB* const" and a conversion operator to a "ClassA".
Using it, you'll be able to differentiate between
a=ClassA(ClassB(...))
indicating a construction from a "ClassB" constant reference, and
a=ClassA(WorkaroundPtr(ClassB(...))
indicating a construction from a "ClassB*" pointer.
param_conversion_operator(SourceTag<source_type>(),
TargetTag<target_type>());
Since the standard vector is a highly useful template,
we created a single registration command that executes
all the registration commands needed to register a new
vector specialization. The command has the following form:
param_vector<my_class>();
This is all you need to do, after you've registered
my_class, to register
std::vector<my_class>. For your
convenience, XParam registers this class under the name
vector<my_class>, omitting the
std:: prefix.
Similarly, XParam also provides the following registration
commands:
param_ptr_vector<my_class>();
for registering std::vector<my_class*>
and std::vector<const my_class*>,
respectively. There's no reason not to add all three
of these immediately, whenever you register a class,
because they are very useful.
Note: One possibility to construct a vector in XParam is by
explicitly listing its elements in list form. (see
The User Interface for details.)
Any vector registered by one of the param_vector
variants will support this construction method, but
you don't have any equivalent registration command at your
disposal for your own classes. However, taking advantage
of the fact that vector supports this construction
method should be just as good for you: all you need to do
is to register a constructor or creator that expects an
std::vector.
Entering a list instead of a vector will cause an
implicit conversion, and is just as effective. For example,
the registration of a creator for a map given in the
Creators section allows you to use
my_map=[ pair("goodbye",2), pair("hello",1) ]
to initialize an std::map on a single line. The
value-list is converted into an std::vector, and
this converted to an std::map.
The XParam output registration command is very simple:
param_output<my_class, my_class_output_functor>();
It is very similar to the registration of creators, but
differs in the fact that there is no argument list, and
that there is no need for a variation with TypeTags.
Any modern C++ compiler should be able to handle this
form. (This is because it doesn't use explicit template
function arguments. It is simply a constructor call.)
This command registers an output functor for your class.
However, you still need to supply the functor itself. To
understand what this functor is and what it does better,
recall that XParam outputs its variables in such a way
that they are readable by XParam, in case you want to
read them again from a different program or in a different
time. So, XParam must be able to output a Triangle,
for example, in the following format:
Triangle(Point(5,6),Point(7,8),Point(10,1))
Naturally, this calls for a recursive approach. The output
functor of class Triangle should tell XParam which
three points should be output in order to describe this
triangle, at which point XParam will recursively have to
find out how to output a Point and finally how to output
an integer. Neither of the latter two should be supplied
by the Triangle class. It only needs to tell
XParam which three Point objects compose it.
Here's how this is done:
The interface of the output functor is always the same:
it must support a public static method called sub_objects
that receives a constant reference to the output variable
and returns a variable of type ValueList. The
way to fill this ValueList with the correct
information is to construct it with a default constructor,
and then to use
ValueList& operator<<(ValueList&,const Handle<ValueSource>&);
to append to it the sub-objects that compose the object to
be printed.
After you register an output functor, it's a good idea to
go back and check that you really do have a constructor
that matches the output function, so that variables that have
been serialized and then deserialized will return exactly to
their original state. It can be very confusing if they
don't.
We recommend supplying output capability to all classes you
register. This will make the debugging process much more
painless for both you and the programmer, and will make class
usage much more convenient to the user (because class output
is used in much of XParam's help-giving and error-handling
mechanisms). If you don't, any
attempt to serialize the class and output it will result in
classname(NO OUTPUT FUNCTION)
Which is not readable as input to another program using XParam
parameter handling.
The only real difference, as far as XParam is concerned,
between native types and non-native types, is that conversion
between native types is considered more "attractive" than
user-defined conversions. Consider, for example, the following
situation:
Variable b can either be constructed from class
A or from a double. C++ will choose the latter
option, because converting an int to a double
is more attractive than converting it to an A.
If you want to register types that will make use of this
difference in conversion weights, here's how you do it.
XParam, like C++, knows two conversion weights other than
user-defined conversions. These are "standard conversion"
and "promotion". For details regarding these weights and
their behavior in C++, consult the ISO-C++ standard near
you. To register constructors and creators that make
use of these weights in XParam, use param_weighted_ctor
and param_weighted_creator instead of
param_ctor and param_creator, respectively,
and add, as a third and last parameter to the registration
command, either STANDARD_CONVERSION or
PROMOTION. You can also add a weight to the
registration of a param_conversion_operator as a
third and last parameter. For conversion operators, no
other change is needed.
As was implied above, conversion weights can only be used
in conversions, that is, in conversion constructors,
conversion creators and conversion operators. If your
constructor or creator has more than one argument, weights
can not be used.
In order to do this, XParam must have the information of
what to load when a certain class is needed. This information
should be supplied in the form of an XPN file. An
XPN file is a file with an xpn extension
which contains one or more
[class1, class2] => [file1, file2]
sequences. The particular line in the example states that
if either class1 or class2 is needed,
file1 and file2 should both be dynamically
loaded. Any amount of white-space can be added in the
XPN files, except in the middle of a class name, a
file name, or the => symbol. If a list of
classes contains only one class, the brackets around it may
be omitted. Likewise if the file list contains only a
single file, the brackets around its name may be omitted.
XPN files may also contain single line comments,
these being lines beginning with the character '#'.
You may have as many XPN files as you want, and
place them in any directory you want (as long as it is
accessible to XParam). However, the full list of all
directories containing XPN files should be available
in the environment variable XPARAM_CLASSPATH.
In the Usage Examples section, a program
using dynamic loading is demonstrated.
Note: XParam currently does not support dynamic loading under Windows.
You must link in your classes and their registration commands statically,
instead.
Next: Installing XParamClass Declarations
The usual way to declare a class for XParam's use is
All the registration commands needed to register these classes are
part of the registration interface, and you are welcome to register
other types, such as short or std::map<std::string,int>.
Inheritance
Since we already mentioned inheritance, here's the syntax to
register it:
Constants
Defining a constant, so that it will be recognized in XParam
initializations, is very simple. The registration command is:
Enumerators
Enumerators are handled in a very similar way to constants.
First, you need to register your enum type. This is done by the
macro
enum DOW { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
PARAM_BEGIN_REG
PARAM_ENUM(DOW);
PARAM_ENUM_VAL(Monday);
PARAM_ENUM_VAL(Tuesday);
PARAM_ENUM_VAL(Wednesday);
PARAM_ENUM_VAL(Thursday);
PARAM_ENUM_VAL(Friday);
PARAM_ENUM_VAL(Saturday);
PARAM_ENUM_VAL(Sunday);
PARAM_END_REG
Argument Passers
Most of the registration code is composed of registration of
constructors. This, in general, looks something like this:
Constructors
Now that we've gone over argument passing, here's
the syntax to register constructors:
param_ctor<Complex>(ByVal<double>("real"));
is the double-to-Complex implicit conversion constructor.
param_ctor<Complex>(ByVal<double>("real"),
ByVal<double>("imaginary")); is the
Complex-from-two-doubles constructor.
or
param_explicit_ctor(TypeTag<Complex>(),
ByVal<double>("real"));
Creators
Not always is everything handed to us on a silver platter.
One of the many surprises life can have in store for you
is that the class you wish to register does not support
the interface you need.
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
class creator_name {
public:
static created_class_name* create( ... arglist ... );
};
struct Point {
int x,y;
};
class Point_creator {
public:
static Point* create(const int _x, const int _y) {
Point* rc=new Point;
rc->x=_x;
rc->y=_y;
return rc;
}
};
class map_creator {
public:
static std::map<std::string,int>*
create(const std::vector<std::pair<std::string,int> >& v);
};
using namespace std;
static map<string,int>*
map_creator::create(const vector<pair<string,int> >& v) {
typedef map<string,int> maptype;
typedef vector<pair<string,int> > vectype;
maptype* rc=new maptype();
for(vectype::const_iterator i=v.begin();i!=v.end();++i) {
(*rc)[i->first]=i->second;
}
return rc;
}
param_creator<map<string,int>,
map_creator>(ConstRef<vector<pair<string,int>
> >("v"));
param_creator(TypeTag<map<string,int> >(),
TypeTag<map_creator>(),
ConstRef<vector<pair<string,int> > >("v"));
or
param_creator(TypeTag<my_class>(),
TypeTag<my_class_creator>(),... arglist ...);
or
param_explicit_creator<my_class,
my_class_creator>(arg);
or
param_explicit_creator(TypeTag<my_class>(),
TypeTag<my_class_creator>(),arg);
class workaround {
public:
static ClassA* create(const ClassB* const b, const Dummy&) {
return new ClassA(b);
}
};
Conversions
In C++, one can define a conversion either in the form of a
conversion constructor, or in the form of a conversion
operator. Both because these have slightly different
behaviors, and because we wanted to keep the conceptual
difference that the C++ language makes, XParam also
allows the registration of conversion operators. These
have the following format:
Vectors
As was mentioned before, templates can not be registered
in XParam, but template specializations can. So, for
example, one can not register the template std::vector,
but one can register any of its specializations, such as
std::vector<int> and std::vector<Duck>.
and
param_const_ptr_vector<my_class>();
Output
All the sections so far, have dealt with input, i.e.
with the question of how user-input, whether from the
command-line, from file or piped-in from another program,
should be parsed and made into real, live, working
objects. This section is different, because here you
specify how you want XParam classes to serialize
themselves back to a streamable output form, so that
you can save them in a file, print them or e-mail
them to your congress member.
class Triangle_output_functor {
public:
static ValueList sub_objects(const Triangle& t) {
ValueList vl;
return vl << Val(t.p1) << Val(t.p2) << Val(t.p3);
}
};
Weights
The registration syntax described up until now is all the
syntax you're ever going to need for registering classes in
XParam. However, if you ever find yourself trying to
register a basic C type, a non-class, non-struct type, you might
need a bit more (though not a whole lot more).
class A {
public:
A(int);
};
class B {
public:
B(double);
B(const A&);
};
void main(void) {
B b(7);
}
Dynamic Loading
We have already mentioned that XParam can load classes,
including their registration information, dynamically.
Previous: The Programmer Interface
Up: Table of Contents