This section covers some basic aspects of KeyValue's design. The material is kept at the minimum just enough to give the reader all she needs to develop her application using KeyValue.
All KeyValue classes, functions, templates, etc. belong to namespace
::keyvalue
.
The five so called basic types are:
bool
;
double
;
;ptime
.string; and
unsigned int
.
Additionally, KeyValue introduces
to represent
empty data.value::Nothing
To maximize portability, KeyValue uses
and
::std::string
for strings and
times, resp. These types are exported to namespace ::boost::posix_time
::keyvalue
where they are called
and
string
resp.ptime
The single-value and multi-type container for basic types
(excluding unsigned int
) is
.value::Variant
The value assigned to a key is not necessarily a single
. It may be a
container of value::Variant
s as well.
KeyValue provides three such containers:value::Variant
Actually, bridge and core library developers do not need to care
about these containers. Indeed, they are used exclusively inside
KeyValue and, at some point, are converted to more standard types. More
precisely, a
is converted into
an appropriate basic type value::Single
T
while a
becomes a
value::Vector
and a ::std::vector
<T>
is transformed
into a
value::Matrix
.::std::vector
<::std::vector
<T>>
Class
is a single-value
and multi-type container for value::Value
,
value::Single
or
value::Vector
.value::Matrix
Only
s are returned
from KeyValue to front-ends. Hence, a series of conversions must be
performed when one wants to return a more basic type. For instance,
suppose that a double value
value::Value
must be returned. In that case the
sequence of conversions would be:x
returnvalue::Value
(value::Single
(value::Variant
(x
)));
Statements like the one above would be needed often and this is very annoying. Fortunately, KeyValue implements a hierarchy tree of types that allow for multi-level implicitly conversions. Therefore, in the example above, the compiler automatically replaces the simpler statement
return x
;
the one previously shown.
The hierarchy of types constitutes a tree where each node is
defined by a specialization of template struct
.Parent
Initially a key is just a text labeling a value. However, there is
more inside a key that just a
can model. Consider the key Dates in the
introductory example again. Its associated value is expected to verify
certain conditions:string
The corresponding
is made of
value::Value
s rather than, say,
ptime
double
s.
Given the plural in Dates, one can
expect more than one
and
then, ptime
's content
might be a value::Value
(of
value::Vector
s).ptime
Since each date corresponds to a stock price, these dates cannot be in the future.
Additionally, one can expect the dates to be in increasing order.
This kind of information is encapsulated by a certain class. In
KeyValue terminology, these classes are called real keys and belong to namespace ::keyvalue::key
.
The class
is the base of all real
keys. More precisely, real keys derive from key::Key
which, in turn,
derives from key::Traits
.key::Key
Actually,
is a template class
depending on a few parameters:key::Traits
ElementType
Type parameter which defines the type of elements in the
output container. It can be bool
,
double
,
,
ptime
, classes defined by
the core library, etc.string
ConverterType
This template template parameter[3] defines the class responsible to convert the input value container into a more appropriate type for core library's use. (See Section 12.3.1.)
MapType
This template template parameter[3] tells how each
object in
the input container must be mapped into an
value::Variant
ElementType
value. (See Section 12.3.2.)
Conversions between KeyValue containers
,
value::Single
and
value::Vector
to more standard
types are responsibility of container
converter classes.value::Matrix
KeyValue provides three such templates (described below) depending on a parameter ElementType.
key::StdSingle
<ElementType>
Converts from
to
value::Single
ElementType
.
key::StdVector
<ElementType>
Converts from
to
value::Vector
.::boost::shared_ptr
<::std::vector
<ElementType>>
key::StdMatrix
<ElementType>
Converts from
to
value::Matrix
.::boost::shared_ptr
<::std::vector
<::std::vector
<ElementType>>>
If the core library uses non-standard containers, then bridge developers have two choices. They can either use the converters above as a first step and then convert again to desired types; or they can implement new container converters that produce the desired types directly from KeyValue containers. The second option is clearly more efficient.
To learn how to implement new container converters, the reading of the reference documentation of three container converters above it strongly advised. Moreover, their implementations can serve as samples for implementing new ones.
Similarly to lexical conversions but depending on the key,
sometimes, each element of the input container must be mapped to a
special value. For instance, for a key Month, it
may be convenient to map string
s
"Jan
", "Fev
", ..., "Dec
" into
numbers 1, 2, ...,
12. This is an example of
.key::FlagMap
Mappings are performed by classes which implement a method to
convert from a
into other
types. Actually, they are template classes depending on a parameter
named value::Variant
OutputType
which defines (but not
necessarily matches) the actual output type. The actual output type
might be recovered through the member type
OutputType_
.
The map template classes are the following:
key::NoMap
<OutputType>
Through this map, a
holding
a value x is mapped into an object of
type value::Variant
OutputType
which has the same
lexical value as x. Only front-end
enabled lexical conversions are considered. For instance, a
holding
either the double 10.1 or
the value::Variant
string
"10.1
" is
mapped into the double
(OutputType
in this case)
10.1.
key::FlagMap
<OutputType>
Some
values
are accepted others not. The accepted ones are mapped into
particular values of type
string
OutputType
. In the example of key
Month above,
OutputType
can be
double
, unsigned
int
or an enum
type.
key::PartialMap
<OutputType>
Half way between
and
key::NoMap
. First,
similarly to key::FlagMap
and
considering front-end enabled lexical conversions, it tries to
map a key::NoMap
value
into an object of type value::Variant
OutputType
which has the same lexical value as x. If
it fails, then, like
, it tries
to map a key::FlagMap
into a
corresponding value of type
string
OutputType
. For instance, the value
for NumberOfEdges (of a regular polygon)
must be an unsigned int
greater than
2. For some special values (but not all)
there correspond a polygon name (e.g.
"Triangle
" for 3 or
"Square
" for 4). There is no
special name for a regular polygon with
1111 edges.
key::ObjectMap
<OutputType>
This is a map where a
identifier is
mapped into a pointer to an object of type
string
OutputType
. Notice that this is the
only map where OutputType
and
OutputType_
differ.
Some properties are shared by several types of keys. For instance, Price, Weight, Size, etc., accept only positive values. Although one can write one class for each of them, this would imply unecessary code duplication. To avoid this, KeyValue implements a few generic keys and then, only very specific and application-dependent keys need to be written as new real keys.
All generic keys set their label at construction time. They are:
key::Single
<OutputType>
Key for a single object of type
ElementType
. No constraints on the
value are set.
Example: A key labeled Number
which accepts
any double value is defined by
key::Single
<double>key
("Number");
key::Vector
<ElementType>
Key for a vector of objects of type
ElementType
with no constraints on
them. A restriction on the size of the vector might be set on
construction.
Example: A key labeled Names
which expects
a vector of 5 string
s is defined
by
key::Vector
<string
>key
("Names",5
);
key::Matrix
<ElementType>
Key for a matrix of objects of type
ElementType
with no constraints. A
restriction on the matrix dimension can be set at
construction.
Example: A key labeled Transformation
which
accepts a 2x3 matrix is defined
by
key::Matrix
<double>key
("Transformation",2
,3
);
key::Positive
Key for a positive number.
Example: A key labeled Price
is defined
by
key::Positive
key
("Price");
key::StrictlyPositive
Key for a strictly positive number.
Example: If the key in the previous example could not accept the value 0, then it would be defined by
key::StrictlyPositive
key
("Price");
key::Bounded
<ElementType,
Bound1, Bound2>
Key for a single bounded value of type
ElementType
. Template template
parameters[3]
Bound1
and
Bound2
define the bound types and
can be either
,
key::NoBound
,
key::Greater
(greater than
or equal to), key::Geq
or
key::Less
(less than or
equal to).key::Leq
Example: A key labeled Probability
accepting any double
value from and
including 0 up to and including
1 is defined by
key::Bounded
<double,key::Geq
,key::Leq
>key
("Probability",0.0
,1.0
);
key::MonotoneBoundedVector
<ElementType,
Monotone, Bound1,
Bound2>
Key for vectors whose elements are monotonic and/or
bounded. Template template parameter[3]
Monotone
defines the type of
monotonicity and can be either
,
key::NonMonotone
,
key::Increasing
,
key::StrictlyIncreasing
or
key::Decreasing
.
key::StrictlyDecreasing
Bound1
and
Bound2
are as in
.
Additionally, a constraint on the vector size can be set at
construction.key::Bounded
Example: A key labeled Probabilities
accepting 10 strictly increasing numbers
from and excluding 0 up to and including
1 is defined by
key::Bounded
<double,key::StrictlyIncreasing
,key::Greater
,key::Leq
>key
("Probabilities",0.0
,1.0
,10
);
Key-value pairs are stored in
s. This class implements
methods DataSet
and
getValue
()
to retrieve values
assigned to keys. Both methods receive a real key and processes all the
information about the expected value encapsulated by the key. For
instance, suppose the variable find
()
holds the current date and consider a key
BirthDates which corresponds to a vector of
increasing dates, supposedly, in the past or today.today
An appropriate real key is then:
key::MonotoneBoundedVector
<ptime,key::Increasing
,key::Leq
>births
("BrithDates",today
);
Therefore, if key BirthDates belongs to a
DataSet
data
,
the result of
data
.getValue
(births
);
is
a
such that the elements of the pointed vector are in increasing order and
before (less than or equal to) ::boost::shared_ptr
<::std::vector
<ptime
>>
. If
the input does not verify this constraint, then an exception is thrown
to inform the user about the failure. Therefore, the caller does not
need to check the constraints.today
Since the type returned by
depends on the real
key it receives, this method is a template function. The same is true
for getValue
()
.find
()
The difference between
and
getValue()
concerns what happens
when the key is not resolved. The former method throws an exception to
indicate the failure whereas the latter returns a null pointer. In
practice, find
()
is used for
compulsory keys and getValue()
for optional ones. A
typical use of find
()
follows:find
()
boolfoo
(false
); if (bool*ptr
=data
.find
(key::Single
<bool>("Foo")))foo
= *ptr
;
In the
code above
is foo
false
unless key Foo is found in
, in which case,
data
gets the given value.foo
All builders and calculators derive from class
. This class declares
two pure virtual methods: Processor
and
getName
()
. The former method
returns the name under which the processor is recognized by key
Processor. The second gets the result of processing
a getResult
()
.DataSet
Actually, builders and calculators are specializations of template
classes
and
Builder
, resp. They depend on
a parameter type named Calculator
Tag
whose primary role
is distinguishing different specializations.
and
Builder
specializations may
implement different features which affects their declarations and
implementations. Therefore, different specializations of
Calculator
s and
Builder
s might derive from
different base classes and implement different methods. Rather than
declaring the specializations from scratch, providing all their base
classes and declaring all necessary methods, helper files
Calculator
keyvalue/mngt/DeclareBuilder.h
and
keyvalue/mngt/DeclareCalculator.h
should be used.
(See
and
Builder
for details.)Calculator
A
able to process an
empty Processor
might be declared a
DataSet
. More precisely, if
the Command
is
Processor
, then its
Builder
<Tag>
method might do its job ignoring
getObject
(const DataSet
&
data
)data
(e.g. ListOfDataSets
and NumberOfDataSets).
Another possibility is when the method does look up values in
but, failing to find any, can
still do its job considering default values for the keys, with or
without intervention of Default data
set.data
In the cases above,
might be
declared a Builder
<Tag>
by deriving from this
class. Similar arguments hold for Command
.Calculator
<Tag>
When a
is a
Processor
, front-ends might take
advantage of this fact and provide shortcuts or menu entries to call
the Command
without asking for
additional input.Processor
In general, the input data required by
s are so rich that must
be stored in a Builder
. Nevertheless, in some
cases, a single DataSet
might be
enough. For instance, consider a builder that creates a function given
a few points on its graph. Normally, this value::Variant
needs the set of
points, an interpolator and an extrapolator. A Builder
is necessary to hold
all this information. However, when the function is known to be
constant, then a single number ― the constant ― is enough to build the
function. Rather than creating a DataSet
to store a single
DataSet
double
value, it would be more convenient if
the
could accept just this
value (or more generally, a Builder
). This is,
indeed, the case.value::Variant
Any
specialization able to
build from a Builder
must derive
from template class value::Variant
. This template
class depends on BuilderFrom
ObjectType
― the type build
by the builder ― and on InputType
― the
basic type that the
can build from. (In
the previous example, Builder
InputType
would be
double
.)
is the abstract class
that defines the interface for all types of messages sent to loggers.
Message
is a template class
which implements MessageImpl
's pure virtual methods.
There are six different specializations of Message
with corresponding
typedefs:MessageImpl
They define
to append
formated data to themselves. A typical use follows:operator &
()
Info
info
(1
); // Create a level-1 Info message. size_ti
;::std::vector
<double>x
; //...info
& "x[" &i
& "] = " &x
[i
] & '\n';
Similarly,
is an
abstract class whose pure virtual methods are implemented by template
class exception::Exception
. This
template class has a member which is an instantiation of
exception::ExceptionImpl
. The exact
instantiation is provided as a template parameter of
MessageImpl
. There
are two specializations of exception::ExceptionImpl
with
corresponding typedefs:exception::ExceptionImpl
(having an
RuntimeError
member); andError
(having a
LogicError
member).Logic
indicates errors
that can be detected only at runtime depending on user-provided data.
RuntimeError
indicates errors that
should be detected at development time. In other terms, a
LogicError
means a bug and is
thrown when a program invariant fails. It is mainly used indirectly
through macro LogicError
as inKV_ASSERT
KV_ASSERT
(i
<getSize
(), "Out of bound!");
To keep compatibility with exception handlers catching standard
exceptions,
derives from
RuntimeError
while
::std::runtime_error
derives from
LogicError
.::std::logic_error
Method
provides the
same functionality of exception::ExceptionImpl
::operator &
()
.
Example:MessageImpl
::operator &
()
if (price
<=0.0
) throwRuntimeError
() & "Invalid price. Expecting a positive number. Got " &price
;
Other more specific exception classes are implemented to indicate
errors that need special treatment. They all derive from either
or
RuntimeError
.LogicError
[3] Template template parameters are one of the least
known features of C++ and deserve a quick note here. Most
template parameters are types. Nevertheless, sometimes a
template parameter can be a template, in which case it is
referred as a template template
parameter. For instance, a template
depending on only
one template template parameter might be instantiated with
Foo
but not with
Foo
<::std::vector
>
.
Recall that
Foo
<::std::vector
<int>>
is a
template class while
::std::vector
is a class.::std::vector
<int>