[Ns-developers] ns-3-names comments
Gustavo Carneiro
gjcarneiro at gmail.com
Sun Feb 8 04:42:26 PST 2009
2009/2/8 <craigdo at ee.washington.edu>
> More long email. But there are reasons that I did what I did and I'm going
> to explain and justify the design decisions I made ...
>
> > I was asked to review this ns-3-names code, so here are some thoughts.
> >
> > static std::string FindShortName (Ptr<Object> object);
> > static std::string FindFullName (Ptr<Object> object);
> >
> > I think this was already discussed but I'm too lazy to search
> > the archives.
> >
> > I think I would prefer Get instead of Find here because I
> > associate a "find"
> > operation as something returning an iterator for the location
> > of something,
> > and that iterator can be null if the object is not found.
>
> We've gone over this a number of times, and the interpretation I think we
> generally agreed on is that Get refers to one item. It does not return an
> error. Find looks for an item among some number of things and it can fail.
> Find doesn't necessarily have anything to do with an iterator, just
> selecting one of n items based on some criterion. Unfortunately, as quite
> recently discussed elsewhere, this convention is not generally observed,
> and
> we often just use Get irrespective of what is happening.
>
> I think Find* is the correct use based on our often discussed and debated
> convention. If we want to just bail out on using the verb Find, based on
> the many, many instances of the verb Get and the several instances of Find,
> I could be convinced. But I think this is how we agreed to do it.
>
> Oh, and all of our requirements authors wanted to call the functions Add
> and
> Find.
>
> > These methods,
> > however, return always a string (by the way, the documentation should
> > indicate that an empty string is returned if the object isn't
> > found). But
> > the scenario of returning an empty string is uncommon (I
> > think...), in which
> > case the Get becomes a stronger alternative to Find.
>
> I would argue that Find becomes a stronger alternative to Get because it is
> returning an empty string that indicates that there is no name to Find
> among
> the possibly many alternatives.
>
> > I would argue that
> > returning an empty string is uncommon because I don't find it
> > normal for the
> > programmer to ask for the name of an object if he did not
> > previously had
> > assigned human readable names to objects of interest.
>
> I'm not sure I understand what you mean. Sure, a user writing correct
> programs will only ask for the name of an object that has a name. Asking
> for the name of an object you haven't named will hopefully be an uncommon
> thing to do for a script author, but we should indicate this somehow, no?
We could do one of two things:
1. Return a dynamically generated string, like "ns3::Node @ 0x0ac81234";
2. Assert if the name is not set for that object (this reminds me of how
much ObjectBase::TraceConnect* returning a boolean instead of asserting
irritate me).
And possibly keep a Find variant that signals the lack of name somehow.
>
>
> It may not be an uncommon thing to happen, however, if we change the pcap
> code to look for a name. It would do that so it could create a file name
> like, "server-eth0.pcap". In this case, there might be dozens of devices
> with no name, but only one with a name. In that case it would not be
> uncommon to return an empty string (or indicate an error some other way).
> I'm really not sure what you're getting at ...
You describe a scenario that is very specific. In that scenario I agree
with you, but it might not be that common.
>
>
> > Another aspect is regarding terminology. ShortName vs
> > FullName. Perhaps it
> > would be simpler to call these just Name and Path?
>
> Well, in the Config code, Objects have Attributes and Attributes that have
> names. A peath in a config namespace means that there are some number of
> corresponding Objects that are actually linked together using Attributes.
> There are rules for what is legal in a path and what correspondences there
> must be with Attributes. Path means something that is apparently hard for
> users to understand.
But do you expect "full name" to be easier to explain than "path"? Paths
are very intuitive IMHO because we already have encountered in several
places: file system paths, xml paths (xpath), even ns-3 attribute/config
paths.
>
>
> In the name code, there are no Attributes involved at all to have names.
> There are shortnames which aren't connected with Attributes in any way.
> Objects do not have to be linked together in any way to make a path
> reachable from some root namespace Object. There are no wildcards allowed
> in a fullname, and no automatic index parsing. A fullname is actually
> semantically different than a path so I called it something different.
>
> Understanding the structure of a config path has been difficult for
> beginning users. I've seen lots of questions about how it works. It
> seemed
> to me that introducing another kind of "path" that is superficially
> similar,
> but subtly different and has different rules for regular expressions and
> how
> it is added to the system would just add to confusion.
>
> The names system is different in this respect, so I refer to the pieces by
> different names. To me it makes more sense.
OK, but I don't think of the confusion with config paths. 'Path' is already
a very overloaded term, so much that you have to say contextualize, say
'config path' instead of just 'path', for instance. We could have 'config
path' and 'name path'.
But I also get your concern and don't completely disagree with it.
>
>
> > My ideal
> > naming for
> > these methods would then be something like this:
> >
> > static std::string GetObjectName (Ptr<Object> object);
> > static std::string GetObjectPath (Ptr<Object> object);
> >
> > Idem for FindObjectFromFullName => GetObjectFromPath.
>
> I'm not sure about GetObjectXxx. See below.
>
> > This is just silly:
> >
> > * @comment This method is identical to
> > FindObjectFromFullName, but has a
> > * short signature since it is a common use and we want it
> > to be easy to
> > * type.
> > */
> > template <typename T>
> > static Ptr<T> Find (std::string name);
>
> I don't think it's silly at all, of course. Depending on what level you
> are
> using the system, you may have different ideas about how to think about the
> name service and the pieces that make it up. From a high-level
> perspective,
> you will probably just be wanting to look up Object names and use them in
> config paths or log prints. You probably will never care about the fact
> that there are Object contexts and probably won't ever think about breaking
> these strings up and matching them with Config path segments. You won't
> worry about whether or not you provide a context, or not, and what the
> remaining name string has got to look like (single end segment or fully
> qualified name).
>
> If you are mixing and matching calls, especially in a place where the term
> context is meaningful, I think it is helpful to associate the fact that a
> shortname goes with a call requiring an object context. It's a shortname
> --
> it needs a context.
>
> FindObjectFromShortName (Ptr<Object> context, std::string name);
>
> However, if you are providing a fully qualified name, you don't need the
> context, because the act of providing the qualification provides the
> context. It's a longname -- it doesn't need a context because the name
> provides the context.
>
> Names::FindObjectFromFullName (std::string name);
>
> I found the verbosity and symmetry helpful in keeping track of what call
> did
> what; and what was expected in the name parameter in which situation.
>
> As I said, users operating in a script will probably not need to concern
> themselves at all about Object contexts, shortnames or fully qualified
> names. They probably just want to get an Object Ptr by providing a string.
> All of this other stuff will just confuse the situation. So this user
> wants
> a simple, straightforward, short uncomplicated function. I gave it to them
>
> Find (std::string name);
>
> This makes perfect sense to me and the overhead is once subroutine call
> which seems quite worth it.
>
> > Make up your mind and choose one method name.
>
> I'm not confused or unable to make up my mind. I deliberately chose to use
> two method names for what could have been one API method in order to help
> out two kinds of people operating in very different environments.
But why not just pick Find and use C++ overloading here?
Find (context, name)
Find (fullName)
It makes sense IMHO. And because of the different arity there is no way C++
overloading rules are going to screw this up.
>
> > My pick would
> > be to have
> > just one GetObject (std::string path) method.
>
> Wow. It almost makes my brain explode trying to conceive of explaining the
> subtleties of this to someone working at a scripting level who doesn't even
> understand the basics of aggregation. Think about,
>
> Ptr<AggregatedType> at = otherObject->GetObject<AggregatedType> ();
> Ptr<Object> o = node->GetObject<Object> (TypeId::LookupByName
> ("ns3::Node"));
> Ptr<Node> n = Names::GetObject<Node> ("/Names/client/eth0");
>
> and answering questions about why this or that doesn't work. e.g., 'I
> tried
> to do node->GetObject<NetDevice> ("eth0") but it didn't work and suggested
> I
> provide a TypeId. But TypeIds seems to have something to do with classes,
> not object names. GetObject is an object method. Someone told me I need
> to
> use a static function because this isn't really Object aggregation. I
> don't
> get it. All I want to do is get the eth0 device.'
>
> It has a certain attractiveness, though, but it's one of those things were
> it subtly leads you into thinking that one thing is going on but in reality
> something entirely different is happening. In this case, I think it would
> astonish people that node->GetObject<NetDevice> ("eth0") wouldn't work
> because they have been subtly misled into thinking that Names::GetObject is
> something else than it is.
>
> I think in order to be not entirely confusing, naming would have to be
> integrated down at the GetObject level in class Object so the previous
> thing
> would just work. At the other end, I'm a bit worried about having to
> present GetObject at a very high (scripting) level. That said, eventually,
> though, someone is going to figure out that you can do,
>
> Ptr<Node> n = CreateObject<Node> ();
> Ptr<Mobility> mob = CreateObject<Mobility> ();
> n->AggregateObject (mob);
> Names::Add ("node", n);
> Ptr<Mobility> m = Names::Find<Mobility> ("\Names\node");
>
> and find your aggregated mobility object using the name service to look up
> the node since there really is a GetObject under the sheets. Eventually it
> may be seen in some scripts, but at least you don't have to introduce
> GetObject at the beginner script level.
>
> So, I'm a bit conflicted on this one. I had not considered integrating
> names down to that level in the system for this go-around. Myself, being
> comfortable with GetObject, I don't have a problem with integrating names
> down at this level. I'm really not so sure about newbie users, though --
> getting them to think of GetObject as a name lookup and then throwing
> object
> aggregation into the mix.
>
> I think we need to think carefully before just jumping in on this one ...
>
> > template <typename T>
> > static Ptr<T> FindObjectFromShortName (Ptr<Object> context,
> > std::string
> > name);
> >
> > I'd also rename this to: GetObject (Ptr<Object> context,
> > std::string name);
> > C++ overloading will take care of this nicely, and I think
> > this is one case
> > where overloading does make complete sense.
>
> I think this makes the situation _very_ confusing -- this clearly isn't
> like
> GetObject since it involves another object, but it's called the same thing:
>
> Ptr<NetDevice> nd = Names::GetObject<NetDevice> (n, "eth0");
>
> Sorry, I think this would be horribly confusing. If you're going to make
> it
> work like GetObject, make it work like GetObject.
>
> Ptr<NetDevice> nd = n->GetObject<NetDevice> ("eth0");
OK, forget about GetObject then :-)
I still can't get over the fact that Object::QueryInterface should never
have been renamed to Object::GetObject :-/
>
> > And instead of 'context'
> > perhaps just calling the parameter 'parent', or
> > 'parentObject', makes it
> > easier to understand.
>
> This is something that comes from the config code. I actually thought that
> the term context was confusing there when I first saw it as well (in
> config), but since that is what we call it, it made sense to me to re-use
> the term. People will have to figure it out in the Config "context" so
> lets
> just borrow the term since it really does look the same down there.
>
> In the Config (trace) code
>
> /NodeList/0/DeviceList/1/ Rx
> ^^^^^^^^^^^^^^^^^^^^^^^^ ^^
> This is the context This is the attribute
Actually you are mistaken. The context that you receive in the trace
callback (Config::Connect) is the whole path, "/NodeList/0/DeviceList/1/
Rx".
>
>
> In the Names code, providing a string context in an add
>
> /Names/client/ eth0
> ^^^^^^^^^^^^^^ ^^^^
> This is the context This is the name
>
> When using Names and Config together in a Config path for a trace hook
>
> /Names/client/eth0 Rx
> ^^^^^^^^^^^^^^^^^^ ^^
> This is the context This is the attribute
>
> So the context in Names and Config are essentially the same thing from the
> tracing point of view; so I used the same term. It makes more sense when
> you look at the string versions of the methods and relate them to the
> Config
> alternatives. It doesn't make as much sense if you just focus on the
> object
> version and what it does under the sheets.
>
> It can get a bit confused too at the boundary cases
>
> /NodeList/0/eth0 Rx
> ^^^^^^^^^^^^^^^^ ^^
> This is the context This is the attribute
> of a trace.
>
> This can be used as
> a mixed path in Config
> calls.
>
> This is not a strictly
> legal config path.
>
> This is not a legal
> context in the Names
> system.
>
> I think it all depends on how you are looking at the system -- from which
> vantage point -- whether one term or the other is marginally clearer.
> Should it be context, path or parent? Since the place where we are
> currently using these low-level functions is actually in the config code
> where the term context is used, it makes good sense to call these things
> contexts. It ties it all together nicely in the place where the name and
> config code is tied together. It makes perfect sense in all but the
> degenerate cases. If you look at it from outside that perspective, you
> could argue differently; but the place where the thing is used is where the
> term context makes sense.
>
> So, calling it context made the most sense to me, even though I originally
> had a problem with the word context, too.
>
> > Documentation somewhere says:
> >
> > * Given a fullname string, look to see if there's an
> > object in the system
> > * with a that associated with it. If there is, do a
> > QueryObject on the
> > * resulting object to convert it to the requested typename.
> >
> > Of course QueryObject does not exist (sigh), and you mean GetObject.
>
> Sigh indeed. I still catch myself talking about QueryInterface
> periodically. Good catch.
>
>
All that said, OK, now that I have given feedback, and you obviously have
read it, whichever way you decide is fine by me. We just need someone to
make a fully informed decision, not to have another bikeshed discussion. :-)
Take care.
--
Gustavo J. A. M. Carneiro
INESC Porto, Telecommunications and Multimedia Unit
"The universe is always one step beyond logic." -- Frank Herbert
More information about the Ns-developers
mailing list