[Ns-developers] Different Approaches to Default Values (was Re: Default Values)
craigdo@ee.washington.edu
craigdo at ee.washington.edu
Sat Jan 19 12:29:28 PST 2008
[ ... ]
> > I did not read Craig's proposal as an extension to the Parameter
> > proposal. I read it as suggesting to set up a parallel
> configuration
> > registry that reuses the tracing namespace.
>
> Hrm. Well, I have a hard time seeing how you could do this without
> integrating the default values into the object itself, which
> is exactly
> what the Parameter stuff is about.
I think you're both right. From a high-level perspective, I was talking
about a new configuration mechanism that closely resembles the tracing
subsystem and reuses the tracing namespace. From a low-level perspective,
since part of this is the *local* configuration mechanism, it needs to be
integrated into the object itself. As Mathieu observes, this is pretty much
what he is proposing with the parameter additions to TypeInfo.
> > Can you describe a use case where the above definition:
> "Default values
> > should be only used to initialize objects and should not be
> referenced
> > throughout its lifetime" will cause problems? A use case
> that does not
>
> I do not see any problem with this definition.
>
> > involve a user abusing or ignoring the above guidance?
> >
> > Another helpful example would be to describe a case where
> you have a
> > default value that needs to be overridden during some
> portion of the
> > topology phase, and this cannot be accomplished by manipulating the
> > value of the DefaultValue. That is what I understand to be
> a motivation
> > for your Parameter proposal (to send a configuration object
> through the
> > topology API, instead of manipulating a default value).
>
> If you look a the hierarchical mobility model, you will see that to
> build a hierarchical mobility model, you need to specify a
> 'parent' and
> a 'child' model. If you use a ClassId for the parent and a separate
> ClassId for the child, you can easily control the _type_ of the object
> to create for each of them when the HierarchicalMobilityModel is
> created. Everything works really well provided the parent and
> the child
> models are different types of models because they are going
> to get their
> parameters from different sets of DefaultValues. However, if
> you want to
> create a child which is the same kind of model as the parent,
> then, both
> the parent and the child will read their initial values from the same
> set of default values, hence making it impossible to create a
> child with
> a different set of parameters than the parent unless, of
> course, you do
> not delegate their creation to the ClassId used by the
> HierarchicalMobilityModel constructor.
>
> If, instead, you can associated to a TypeId a specific set of
> parameter
> values to use when creating an object, then, you can make the
> TypeId of
> the child have a different set of parameter values than the parent. To
> achieve this, you need the Parameter stuff.
Let's be clear. If you need another set of parameter values to pass to the
constructor, what you need is another set of parameter values to pass to the
constructor. There are several ways to do this, one of which is the
"Parameter stuff."
> I would like to point out that this is really just an example we have
> today: the fundamental problem is that since you are using a single
> global variable to hold all the initial values, you cannot instantiate
> in a single function two objects with different arguments. It
> works only
> if you, as a user, are given a chance to change the global
> value between
> the two object creations.
Yes. There is a global configuration space that works in simple cases. I
think we should keep that. What we need is what I've been calling a local
configuration space. There are different ways to do that.
It is relatively easy to consider a global space that is available when the
object has not yet been instantiated; and a local space that is available
with the object fully instantiated. These are the two Set() examples I've
been using worded to make the distinction clearer (I hope). The first is
the global case, the second is the local case:
Set ("/global/SomeClass/configuration/item")
Set ("/nodes/1/SomeClassObject/configuration/item")
The hard case we are really talking about lives in-between these two
boundary cases. What happens when you want to use some configuration item
that is not in the global space while the object has not yet been
constructed. There must be a "third way" to allow the user to specify which
class to create and which parameters to use. There are a number of ways to
do this. Pretty much all ways will get more and more complicated as the
structure you want to create gets more complicated.
I think the easiest to visualize the general problem is try and create a
general hierarchy of various types of some ClassObject. Consider a node
onto which you have an abstract need to hang a hierarchy of different
objects you want to be user configurable.
[ASCII art warning ...]
+-> SomeClass2Object -> SomeClass4Object
|
node -> SomeClass1Object -> + +-> SomeClass5Object
| |
+-> SomeClass3Object -> +
|
+-> SomeClass6Object
Just to be cruel, assume that each of the SomeClass{*} constructors can take
a different set of constructors. I think Mathieu is asking, "how the #~(&
do you configure a situation like this generically.
One way is to just enter the whole structure into the global configuration
space since it's available and waiting for such a situation:
Set ("/global/TheConfig/class", "SomeClass1");
Set ("/global/TheConfig/class/nChildren", "2");
Set ("/global/TheConfig/childType/0/class", "SomeClass2");
Set ("/global/TheConfig/Object/childType/0/nChildren", "1");
Set ("/global/TheConfig/childType/0/0/class", "SomeClass4");
Set ("/global/TheConfig/Object/childType/0/0/nChildren", "0");
Set ("/global/TheConfig/childType/1/class", "SomeClass3");
Set ("/global/TheConfig/Object/childType/1/nChildren", "2");
Set ("/global/TheConfig/childType/1/0/class", "SomeClass5");
Set ("/global/TheConfig/Object/childType/1/0/nChildren", "0");
Set ("/global/TheConfig/childType/1/0/class", "SomeClass6");
Set ("/global/TheConfig/Object/childType/1/0/nChildren", "0");
You can imagine inserting various construction parameters into this
structure. The contructors of all the objects need to know what level they
live on and parse and use this stuff. Welcome to the Windows Registry. I
won't comment on how wonderful this looks.
Another way to do this is to package this information up into blobs
(Parameters) and pass them down into the system. You will still have to
pass the same amount of information, it'll just be in a different form:
Parameter p1;
p1.Set("class", "SomeClass1");
p1.Set("nChildren", "2");
Now I instantly see the need for a vector of Parameter in a Parameter, so
I'll just assume one exists and work backward (making them all vector
parameters for generality):
Ptr<VectorParameter> p4 = Create<VectorParameter>
p4->Set("class", "SomeClass4");
p4->Set("nChildren", "0");
Ptr<VectorParameter> p5 = Create<VectorParameter>
p4->Set("class", "SomeClass5");
p4->Set("nChildren", "0");
Ptr<VectorParameter> p6 = Create<VectorParameter>
p4->Set("class", "SomeClass6");
p4->Set("nChildren", "0");
Ptr<VectorParameter> p2 = Create<VectorParameter>
p2->Set("class", "SomeClass4");
p2->Set("nChildren", "1");
p2->SetVector(0, p4)
Ptr<VectorParameter> p3 = Create<VectorParameter>
p2->Set("class", "SomeClass3");
p2->Set("nChildren", "2");
p2->SetVector(0, p5)
p2->SetVector(0, p6)
Ptr<VectorParameter> p1 = Create<VectorParameter>
p2->Set("class", "SomeClass1");
p2->Set("nChildren", "2");
p2->SetVector(0, p2)
p2->SetVector(0, p3)
So this puts together a blob of parameters that can be passed down to the
node doing the construction and disassembled by each SomeClass to discover
construction parameters. At each level, the constructor strips off the
lower level parameters and passes them on down the system. The parameters
still need to be "parsed" somehow.
Another way is to use a framework approach. In this case, the Build method
of the highest level SomeClass would be refined by the user into MySomeClass
that would create the hierarchy explicitly. If parameters were required,
they could be provided here directly.
Build ()
{
Ptr<SomeClassObject4> p4 = Create<SomeClassObject4> (0); // no child
Ptr<SomeClassObject5> p5 = Create<SomeClassObject5> (0); // no child
Ptr<SomeClassObject5> p6 = Create<SomeClassObject6> (0); // no child
Ptr<SomeClassObject2> p2 = Create<SomeClassObject2> (1); // one child
p2->SetChild(0, p4);
Ptr<SomeClassObject3> p3 = Create<SomeClassObject3> (2); // two child
p2->SetChild(0, p5);
p2->SetChild(1, p6);
Ptr<SomeClassObject1> p1 = Create<SomeClassObject1> (2); // two child
p2->SetChild(0, p2);
p2->SetChild(1, p3);
}
I think this is quite nice, myself.
> So far, it sort of works in simple cases but
> as soon as you get into arranging more complex object structures, you
> will get in cases like this where multiple objects are created by a
> single call to a single function from the point of view of
> the user. You
> can try to add what craig has been championing to solve this problem,
> that is, an inversion of control flow but I doubt very much that the
> result is going to be much easier to deal with.
Unless I am missing something, it is much easier to deal with. But I am
perfectly willing to hear that I have missed something completely.
[ ... ]
-- Craig
More information about the Ns-developers
mailing list