Export symbolfoo should immediately follow its functionvariable




















But in Lisp, there are no operators: instead, there are only functions. Other functions have a fixed number of arguments, plus an optional argument at the end.

For example, subseq takes a string followed by one or two numbers. If only one number i is provided, then subseq returns the substring starting a position i in the string and ending at the end of the string. If two numbers i and j are provided, then subseq returns the substring starting a position i in the string and ending at position j. Lisp has a special name for functions which return "true" usually t or "false" nil. These functions are called predicates.

Traditionally, many Lisp predicate names end with a p. Here are some predicates. When an expression is evaluated which generates an error, Lisp breaks and returns to the command prompt with a break sequence, just like what happens when you press Control-C. Errors can also occur if there is no function associated with a given symbol in a list. When a list contains another list among its expressions, the evaluation procedure is recursive.

The Lisp system returns Here are some more examples. Now you see how easy it is to get lost in the parentheses! One particularly useful function is print , which takes the form print expression-to-print. This function evaluates its argument, then prints it, then returns the argument. As can be seen at right, if you just use print all by itself, the screen will appear to print the element twice.

Why is that? It's because print printed its argument, then returned it, and Lisp always prints [again] the final return value of the expression. One nice use of print is to stick it in the middle of an expression, where it will print elements without effecting the final return value of the whole expression.

There are some evaluatable lists which are not functions because they do not obey the function rule "evaluate each argument exactly one time each". These lists are known as macros or special forms.

For now we will not distinguish between these two terms, though there is a massive difference underneath. Macros and special forms are mostly used as control structures. For example, the control structure if is a special form.

If this returns true, then if evaluates and returns then-expression , else it evaluates and returns optional-else-expression or if optional-else-expression is missing, returns nil.

Because if is an expression, unlike most languages it's quite common to see it embedded inside other expressions like the last expression at right. This is roughly equivalent to C's i? Why can't if be a function? Because it may not necessarily evaluate the then-expression , or if it does, it will not evaluate the optional-else-expression. Thus it violates the function rule. What if you want to do three things in the then-expression?

You need to make a block a group of expressions executed one-by-one. Blocks are made with the special form progn , which takes the form: progn expr1 expr2 expr Except when they're at the head of a list, symbols are also expressions. When it's not the head of a list, a symbol represents a variable.

When evaluated, a symbol will return the value of a variable. The value of a symbol's variable has nothing to do with the function, special form, or macro associated with the symbol. You can thus have variables called print , if , etc. Variables are set with the macro setf. For now, as far as you're concerned, this macro looks like this: setf variable-symbol expression setf is a macro and not a function because it does not evaluate variable-symbol.

Instead, it just evaluates expression , and stores its value in the variable associated with variable-symbol. Then it returns the value of expression. If a symbol is evaluated before anything has been stored in its variable, it will generate an error. Be careful with setf. Lisp doesn't need to declare variables before they are used. Therefore, unless variables are declared to be local discussed later , setf will make global variables. And setf is the first operation we've seen with side effects -- so the order of operations will matter!

See the example at right. So x is evaluated -- returning 6; then setf x 3 is evaluated, which sets x to 3 and returns 3; then x is evaluated -- and now it returns 3. Because special forms and macros don't obey the function rule, they can take whatever syntax they like.

Here is let , a special form which declares local variables: let declaration1 declaration Then it evaluates the expressions in order as a block. These expressions are evaluated in the context of these local variables the expressions can see them. Thus the local variables are only declared within the scope of the let expression. A declaration takes one of two forms: var. A list consisting of the variable symbol followed by an expression. The expression is evaluated and the variable is initialized to that value.

Another reason a list might be a special form or macro is because it repeatedly evaluates its arguments. One example is dotimes. This macro is an iterator a looping control structure. Like most iterators in Lisp, dotimes requires a variable. Here's the format: dotimes var high-val optional-return-val expr1 expr Here, dotimes first evaluates the expression high-val , which should return a positive integer. Then it sets the variable var which is a symbol, and is not evaluated to 0. Then it evaluates the zero or more expressions one by one.

Then it increments var by 1 and reevaluates the expressions one by one. It does this until var reaches high-val. At this time, optional-return-val is evaluated and returned, or nil is returned if optional-return-val is missing.

You don't need to declare the dotimes variable in an enclosing let -- dotimes declares the variable locally for you. The dotimes variable is local only to the dotimes scope -- when dotimes exits, the variable's value resumes its previous setting or none at all.

In Lisp, functions are created by calling a function-making macro. This macro is called defun. A simple version of defun takes the following general form: defun function-name-symbol param1 param2 param The name of the function is the function-name-symbol.

At right is a really simple example: a function of no arguments which simply returns the string "Hello, World! Here are some examples with one, two, and three arguments but just one expression. Here are some examples with several expressions in the function Remember, the function returns the value of the last expression. Lisp functions can have local variables, control structures, whatnot.

Try to use local variables rather than global variables! Declare local variables with let. Actually, it is surprisingly rare in Lisp to have more than one expression in a function. Instead, expressions tend to get nested together.

Lisp functions tend to take on functional form rather than declarative form. In Lisp you don't -- you nest functions. Like Java, Lisp is pass-by-value. The parameters of a function are considered to be local variables to that function, and can be set with setf. This does not change the values of things passed in. You can also make recursive functions.

Lisp style often makes heavy use of recursion. You'll find that functional style and recursion together result in a need for very few local variables. Here's the factorial function again, only done recursively. If the optional parameter isn't provided when the function is called, then the parameter is set to nil.

Alternatively you can provide the default value to set the parameter to when it's not provided when the function is called. Lisp can also have keyword parameters. These are parameters which can appear or not appear, or be in any order, because they're given names.

Similarly to optional arguments, each keyword parameter is either a parameter name whose value defaults to nil if not passed in when the function is called or is a list of the form param-name default-value Keyword parameters may appear only at the end of the parameter list. You pass a keyword parameter whose name is foo into a function by using the term :foo followed by the value to set foo to. Keyword parameters can be passed in in any order, but must appear at the end of the parameter list.

Though it's possible to have both keyword parameters and optional parameters in the same function, don't do it. Gets confusing. Many built-in Lisp functions use lots of keyword parameters to "extend" them!

Lists are normally evaluated as function or macro calls. Symbols are normally evaluated as variable references. But they don't have to be. Lists and symbols are data as well! The special form quote can be used to bypass the evaluation of its argument.

What is a symbol when used in data form? It's just itself. The symbol foo is just a thing that looks like foo case insensitive of course. It is a data type like any other. You can set variables to it. What is a list when used in data form? A list is a singly-linked list. There are a great many functions which operate on lists as well.

The old name of first is car. It does not damage the original list. The old name of rest is cdr. Lists as data can of course contain sublists. In data form, the first item of a list can be anything -- it's not restricted to be just a symbol. Lists have a common control structure, dolist , which iterates over a list. The format of dolist is very similar to dotimes : dolist var list-to-iterate-over optional-return-val expr1 expr Lists and strings share a common supertype, sequences. There are a great many sequence functions.

All sequence functions work on any kind of sequence including strings and lists. Here are two sequence functions we've seen so far. Lisp is both an interpreter and a compiler. If you type in code at the command line, it is on most Lisp systems interpreted. You can compile a function by passing its symbol name quoted! You can time the speed of any expression, and its garbage collection, with the time function.

Run time: 1. Run time: 0. Space: 0 Bytes You don't have to type all your code in on the command line. Instead, put it in a file named "myfile.

Then load it with the load command. By default, load is fairly silent -- it doesn't print out all the return values to the screen like you'd get if you typed the code in at the command line.

If you'd like to see these return values printed out, you can add the :print t keyword parameter. You can load and reload files to your heart's content. You can also compile a whole file with the compile-file function. When a file is compiled, the object file created has a. Depends on the Lisp compiler. You load object files with the load function as well.

You can omit the extension ". Some systems load the most recent version either the source or the. In general, to be safe, always load the full name of the file including the extension.

When the compiler compiles the file, one common thing it will complain of is special variables. There is presently no support for substituting a different underlying random number generator e. Patches are welcome. In ns-3, these are assigned sequentially starting from the first stream as new RandomVariableStream instances make their first call to GetValue. As a result of how these RandomVariableStream objects are assigned to underlying streams, the assignment is sensitive to perturbations of the simulation configuration.

The consequence is that if any aspect of the simulation configuration is changed, the mapping of RandomVariables to streams may or may not change. As a concrete example, a user running a comparative study between routing protocols may find that the act of changing one routing protocol for another will notice that the underlying mobility pattern also changed. Starting with ns This is the Stream attribute, part of the base class RandomVariableStream.

The assignment of streams to a fixed stream number is optional; instances of RandomVariableStream that do not have a stream value assigned will be assigned the next one from the pool of automatic streams. To fix a RandomVariableStream to a particular underlying stream, assign its Stream attribute to a non-negative integer the default value of -1 means that a value will be automatically allocated. When you publish simulation results, a key piece of configuration information that you should always state is how you used the random number generator.

It is incumbent on the researcher publishing results to include enough information to allow others to reproduce his or her results. It is also incumbent on the researcher to convince oneself that the random numbers used were statistically valid, and to state in the paper why such confidence is assumed.

In the simplest usage, the hash function returns the bit or bit hash of a data buffer or string. The default underlying hash function is murmur3 , chosen because it has good hash function properties and offers a bit version. The venerable FNV1a hash is also available.

There is a straight-forward mechanism to add or provide at run time alternative hash function implementations. For example, you might want the hash of a packet stream, but not want to assemble a single buffer with the combined contents of all the packets. By default Hasher preserves internal state to enable incremental hashing. The default hash function is murmur3. FNV1a is also available. To specify the hash function explicitly, use this constructor:. To add the hash function foo , follow the hash-murmur3.

If your hash function is a single function, e. For this to compile, your hashf has to match one of the function pointer signatures:.

Conceptually, the simulator keeps track of a number of events that are scheduled to execute at a specified simulation time. The job of the simulator is to execute the events in sequential time order. Once the completion of an event occurs, the simulator will move to the next event or will exit if there are no more events in the event queue.

This chapter of the manual describes these fundamental objects simulator, scheduler, time, event and how they are used. The Simulator class is the public entry point to access event scheduling facilities.

Once a couple of events have been scheduled to start the simulation, the user can start to execute them by entering the simulator main loop call Simulator::Run. Once the main loop starts running, it will sequentially execute all scheduled events in order from oldest to most recent until there are either no more events left in the event queue or Simulator::Stop has been called. The Simulator API was designed to make it really simple to schedule most events.

It provides three variants to do so ordered from most commonly used to least commonly used :. There are two basic ways to schedule events, with and without context. What does this mean? Readers who invest time and effort in developing or using a non-trivial simulation model will know the value of the ns-3 logging framework to debug simple and complex simulations alike.

The node id of the currently executing network node is in fact tracked by the Simulator class. To associate a context to each event, the Schedule, and ScheduleNow methods automatically reuse the context of the currently-executing event as the context of the event scheduled for execution later.

In some cases, most notably when simulating the transmission of a packet from a node to another, this behavior is undesirable since the expected context of the reception event is that of the receiving node, not the sending node. To avoid this problem, the Simulator class provides a specific schedule method: ScheduleWithContext which allows one to provide explicitly the node id of the receiving node associated with the receive event.

In some very rare cases, developers might need to modify or understand how the context node id of the first event is set to that of its associated node. The DoInitialize method overridden in some of these objects most notably in the Application base class will schedule some events most notably Application::StartApplication which will in turn scheduling traffic generation events which will in turn schedule network-level events.

In ns-3, the network models interpret the context as the node id of the node which generated an event. This is why it is important to call ScheduleWithContext in nsChannel subclasses because we are generating an event from node i to node j and we want to make sure that the event which will run on node j has the right context.

Some new users to ns-3 are unfamiliar with an extensively used programming idiom used throughout the code: the ns-3 callback. This chapter provides some motivation on the callback, guidance on how to use it, and details on its implementation.

Consider that you have two simulation models A and B, and you wish to have them pass information between them during the simulation. One way that you can do that is that you can make A and B each explicitly knowledgeable about the other, so that they can invoke methods on each other:.

It is easy to see that this is a brute force mechanism of communication that can lead to programming cruft in the models. This is not to say that objects should not know about one another if there is a hard dependency between them, but that often the model can be made more flexible if its interactions are less constrained at compile time. This is not an abstract problem for network simulation research, but rather it has been a source of problems in previous simulators, when researchers want to extend or modify the system to do different things as they are apt to do in research.

If the simulator has made assumptions, and hard coded into the code, that IP always talks to a transport protocol above, the user may be forced to hack the system to get the desired interconnections. This is clearly not an optimal way to design a generic simulator. The basic mechanism that allows one to address the problem above is known as a callback.

This ultimately means you need some kind of indirection — you treat the address of the called function as a variable. This variable is called a pointer-to-function variable. The relationship between function and pointer-to-function pointer is really no different that that of object and pointer-to-object.

In C the canonical example of a pointer-to-function is a pointer-to-function-returning-integer PFI. For a PFI taking one int parameter, this could be declared like,:. What you get from this is a variable named simply pfi that is initialized to the value 0. If you want to initialize this pointer to something meaningful, you have to have a function with a matching signature. In this case:. This is suggestive since it looks like you are dereferencing the function pointer just like you would dereference any pointer.

Typically, however, people take advantage of the fact that the compiler knows what is going on and will just use a shorter form:. Notice that the function pointer obeys value semantics, so you can pass it around like any other value. Typically, when you use an asynchronous interface you will pass some entity like this to a function which will perform an action and call back to let you know it completed. It calls back by following the indirection and executing the provided function. This declares a variable named pmi just as the previous example declared a variable named pfi.

Since the will be to call a method of an instance of a particular class, one must declare that method in a class:. This assigns the address of the code implementing the method to the variable, completing the indirection. In order to call a method, the code needs a this pointer. This, in turn, means there must be an object of MyClass to refer to. A simplistic example of this is just calling a method indirectly think virtual function :.

Just like in the C example, you can use this in an asynchronous call to another module which will call back using a method and an object pointer. The straightforward extension one might consider is to pass a pointer to the object and the PMI variable. The module would just do:. The called module will have to understand the concrete type of the calling object in order to properly make the callback.

This is precisely the problem described above. What is needed is a way to decouple the calling function from the called class completely. This requirement led to the development of the Functor. A functor is the outgrowth of something invented in the s called a closure. It is basically just a packaged-up function call, possibly with some state. A functor has two parts, a specific part and a generic part, related through inheritance.

The calling code the code that executes the callback will execute a generic overloaded operator of a generic functor to cause the callback to be called. The called code the code that wants to be called back will have to provide a specialized implementation of the operator that performs the class-specific work that caused the close-coupling problem above.

With the specific functor and its overloaded operator created, the called code then gives the specialized code to the module that will execute the callback the calling code. The calling code will take a generic functor as a parameter, so an implicit cast is done in the function call to convert the specific functor to a generic functor. This means that the calling module just needs to understand the generic functor type. It is decoupled from the calling code completely.

The information one needs to make a specific functor is the object pointer and the pointer-to-method address. The caller defines a specific part of the functor that really is just there to implement the specific operator method:. The previous code is not real ns-3 code. It is simplistic example code used only to illustrate the concepts involved and to help you understand the system more. Do not expect to find this code anywhere in the ns-3 tree.

Notice that there are two variables defined in the class above. The code that will talk to the model would build a specific functor and pass it to LibraryFunction :. When LibraryFunction is done, it executes the callback using the operator on the generic functor it was passed, and in this particular case, provides the integer argument:.

Notice that LibraryFunction is completely decoupled from the specific type of the client. The connection is made through the Functor polymorphism. The Callback API in ns-3 implements object-oriented callbacks using the functor mechanism. It is therefore more type-safe to use than traditional function pointers, but the syntax may look imposing at first.

This section is designed to walk you through the Callback system so that you can be comfortable using it in ns This is an example of a C-style callback — one which does not include or need a this pointer. The function template Callback is essentially the declaration of the variable containing the pointer-to-function. In the example above, we explicitly showed a pointer to a function that returned an integer and took a single integer as a parameter, The Callback template function is a generic version of that — it is used to declare the type of a callback.

The Callback template requires one mandatory argument the return type of the function to be assigned to this callback and up to five optional arguments, which each specify the type of the arguments if your particular callback function has more than five arguments, then this can be handled by extending the callback implementation.

The signature of the function that it will hold must return double and must support two double arguments. If one tries to pass a function whose signature does not match the declared callback, a compilation error will occur. Now, we need to tie together this callback instance and the actual target function CbOne. Notice above that CbOne has the same function signature types as the callback— this is important. We can pass in any such properly-typed function to this callback. You can only bind a function to a callback if they have the matching signature.

The first template argument is the return type, and the additional template arguments are the types of the arguments of the function signature. This call to MakeCallback is, in essence, creating one of the specialized functors mentioned above. The variable declared using the Callback template function is going to be playing the part of the generic functor. The check for IsNull ensures that the callback is not null — that there is a function to call behind this callback.

Then, one executes the generic operator which is really overloaded with a specific implementation of operator and returns the same result as if CbOne had been called directly. Generally, you will not be calling static functions but instead public member functions of an object.

In this case, an extra argument is needed to the MakeCallback function, to tell the system on which object the function should be invoked. Consider this example, also from main-callback. Recall from the background section above that Operator will use the pointer to member syntax when it executes on an object:.

The line:. In this case, when two is invoked:. It is possible for callbacks to be null; hence it may be wise to check before using them.

Invoking a null callback is just like invoking a null function pointer: it will crash at runtime. A very useful extension to the functor concept is that of a Bound Callback. Previously it was mentioned that closures were originally function calls packaged up for later execution. Notice that in all of the Callback descriptions above, there is no way to package up any parameters for use later — when the Callback is called via operator.

All of the parameters are provided by the calling function. What if it is desired to allow the client function the one that provides the callback to provide some of the parameters? One of the parameters of operator has been bound fixed by the client. Some of our pcap tracing code provides a nice example of this. There is a function that needs to be called whenever a packet is received. This function calls an object that actually writes the packet to disk in the pcap file format.

The signature of one of these functions will be:. The static keyword means this is a static function which does not need a this pointer, so it will be using C-style callbacks.

What we want in the calling code is just a call that looks like:. We provide the MakeBoundCallback template function for that purpose. It takes the same parameters as the MakeCallback template function but also takes the parameters to be bound. In the case of the example above:. Conceptually, it extends the specific functor described above with one or more bound arguments:.

When the operator is invoked with the single parameter, as in:. Similarly for three arguments one would have function with a signature:. This kind of binding can be used for exchanging information between objects in simulation; specifically, bound callbacks can be used as traced callbacks, which will be described in the next section. Where are callbacks frequently used in ns-3? Here are some of the more visible ones to typical users:.

The code snippets above are simplistic and only designed to illustrate the mechanism itself. The actual Callback code is quite complicated and very template-intense and a deep understanding of the code is not required. If interested, expert users may find the following useful. This code most notably departs from the Alexandrescu implementation in that it does not use type lists to specify and pass around the types of the callback arguments.

Of course, it also does not use copy-destruction semantics and relies on a reference list rather than autoPtr to hold the pointer. This manual chapter is intended to introduce the reader to the ns-3 object model. In brief, several design patterns in use include classic object-oriented design polymorphic interfaces and implementations , separation of interface and implementation, the non-virtual public interface design pattern, an object aggregation facility, and reference counting for memory management.

Those familiar with component models such as COM or Bonobo will recognize elements of the design in the ns-3 object aggregation model, although the ns-3 design is not strictly in accordance with either.

There are three special base classes used in ns Classes that inherit from these base classes can instantiate objects with special properties. These base classes are:. It is not required that ns-3 objects inherit from these class, but those that do get special properties. Classes deriving from class Object get the following properties.

Classes that derive from class ObjectBase get the first two properties above, but do not get smart pointers. Classes that derive from class SimpleRefCount : get only the smart-pointer reference counting system. In practice, class Object is the variant of the three above that the ns-3 developer will most commonly encounter.

We have settled on a reference counting design described as follows. All objects using reference counting maintain an internal reference count to determine when an object can safely delete itself.

It is the obligation of the user of the pointer to explicitly Unref the pointer when done. When the reference count falls to zero, the object is deleted. The burden for calling Unref is somewhat relieved by the use of the reference counting smart pointer class described below. Users using a low-level API who wish to explicitly allocate non-reference-counted objects on the heap, using operator new, are responsible for deleting such objects. This smart-pointer class assumes that the underlying type provides a pair of Ref and Unref methods that are expected to increment and decrement the internal refcount of the object instance.

This implementation allows you to manipulate the smart pointer as if it was a normal pointer: you can compare it with zero, compare it against other pointers, assign zero to it, etc. It is possible to extract the raw pointer from this smart pointer with the GetPointer and PeekPointer methods. If you want to store a newed object into a smart pointer, we recommend you to use the CreateObject template functions to create the object and store it in a smart pointer to avoid memory leaks.

These functions are really small convenience functions and their goal is just to save you a small bit of typing. This holds true for ns-3 also, but some objects in the system have some additional frameworks available. Specifically, reference counted objects are usually allocated using a templated Create or CreateObject method, as follows.

For objects deriving from class Object :. Please do not create such objects using operator new ; create them using CreateObject instead. For objects deriving from class SimpleRefCount , or other objects that support usage of the smart pointer class, a templated helper function is available and recommended to be used:.

This is simply a wrapper around operator new that correctly handles the reference counting system. The ns-3 object aggregation system is motivated in strong part by a recognition that a common use case for ns-2 has been the use of inheritance and polymorphism to extend protocol models. Weak base class refers to the problems that arise when a class cannot be effectively reused derived from because it lacks necessary functionality, leading the developer to have to modify the base class and causing proliferation of base class API calls, some of which may not be semantically correct for all subclasses.

This design is based on elements of the Component Object Model and GNOME Bonobo although full binary-level compatibility of replaceable components is not supported and we have tried to simplify the syntax and impact on model developers.

Node is a good example of the use of aggregation in ns Note that there are not derived classes of Nodes in ns-3 such as class InternetNode. Instead, components protocols are aggregated to a node. Note that the Ipv4 protocols are created using CreateObject.

Then, they are aggregated to the node. In this manner, the Node base class does not need to be edited to allow users with a base class Node pointer to access the Ipv4 interface; users may ask the node for a pointer to its Ipv4 interface at runtime. How the user asks the node is described in the next subsection. Note that it is a programming error to aggregate more than one object of the same type to an nsObject. So, for instance, aggregation is not an option for storing all of the active sockets of a node.

GetObject is a type-safe way to achieve a safe downcasting and to allow interfaces to be found on an object. The client code wishes to configure a default route. To do so, it must access an object within the node that has an interface to the IP forwarding configuration. It performs the following:. If the node in fact does not have an Ipv4 object aggregated to it, then the method will return null. Therefore, it is good practice to check the return value from such a function call.

If successful, the user can now use the Ptr to the Ipv4 object that was previously aggregated to the node. Another example of how one might use aggregation is to add optional models to objects. However, other nodes need not know anything about energy models. We hope that this mode of programming will require much less need for developers to modify the base classes.

A common use case is to create lots of similarly configured objects. One can repeatedly call CreateObject but there is also a factory design pattern in use in the ns-3 system. Class ObjectFactory can be used to instantiate objects and to configure the attributes on those objects:. The first method allows one to use the ns-3 TypeId system to specify the type of objects created.

The second allows one to set attributes on the objects to be created, and the third allows one to create the objects themselves.

The answer to this is that in many situations, both techniques will work. DynamicCast works when the programmer has a base type pointer and is testing against a subclass pointer.

GetObject works when looking for different objects aggregated, but also works with subclasses, in the same way as DynamicCast. If unsure, the programmer should use GetObject, as it works in all cases. If the programmer knows the class hierarchy of the object under consideration, it is more direct to just use DynamicCast.

This chapter focuses on the second item above: how the many values in use in ns-3 are organized, documented, and modifiable by ns-3 users. The ns-3 attribute system is also the underpinning of how traces and statistics are gathered in the simulator.

In the course of this chapter we will discuss the various ways to set or modify the values used by ns-3 model objects. In increasing order of specificity, these are:. Before delving into details of the attribute value system, it will help to review some basic properties of class Object. Many ns-3 objects inherit from the Object base class. These objects have some additional properties that we exploit for organizing the system and improving the memory management of our objects:.

Most ns-3 objects we will discuss derive from Object , but a few that are outside the smart pointer memory management framework derive from ObjectBase.

As introduced in the ns-3 tutorial, ns-3 objects are memory managed by a reference counting smart pointer implementation , class Ptr. Smart pointers are used extensively in the ns-3 APIs, to avoid passing references to heap-allocated objects that may cause memory leaks.

For most basic usage syntax , treat a smart pointer like a regular pointer:. As we discussed above in Memory management and class Ptr , at the lowest-level API, objects of type Object are not instantiated using operator new as usual but instead by a templated function called CreateObject. Objects that derive from Object must be allocated on the heap using CreateObject.

Those deriving from ObjectBase , such as ns-3 helper functions and packet headers and trailers, can be allocated on the stack. In some scripts, you may not see a lot of CreateObject calls in the code; this is because there are some helper objects in effect that are doing the CreateObject calls for you. The public header file node. This is defined in the node. It also enables subclasses to inherit the Attributes of their parent class. Outside a module, only the public symbols are accessible when the module is imported in another module.

Normally import is not "transitive", i. To work around this, instead of import you can also use an include declaration which causes all imports and includes of the included module to be "reexported". For instance, let us consider the case that module foo includes module bar :. Then by importing module foo you also gain access to the public symbols of module bar and, recursively, to the modules included by bar , etc. This provides a means to group different modules together in one larger "umbrella" module.

For instance, the standard prelude script cf. The Standard Library simply includes most of the other standard library modules. In the Q language, each module has its own separate namespace. This means that two different modules, say foo1 and foo2 , may both provide their own public function symbol named foo.

If both foo1 and foo2 are imported in the same script, you can distinguish between the two by using a qualified identifier , using the module name as a prefix, e. Writing simply foo in this case will produce an error message because the reference is ambiguous.

Import and include declarations can occur anywhere in a script and will be active from this point on up to the end of the script file , but it is common and recommended practice to put them near the beginning of the script. As in most other programming languages, module dependencies must always be acyclic.

If the compiler finds a cyclic chain of import and include declarations, such as a module importing itself or a module foo importing a module bar which in turn imports foo again, it produces an error message.

Another caveat: When using unqualified import clauses, it is much too easy to accidentally "reuse" an imported symbol because of a missing local symbol declaration. External C functions, variables, and types can be referred to within a Chapel program.

The section Working with C below describes the basic ideas of how Chapel and C interoperate. There are two supported strategies for providing the Chapel compiler with information about the C declarations that should be usable from Chapel code. The explicit strategy is to use extern declarations, as described in the section Support for Extern Declarations below.

When using the explicit strategy, one creates a Chapel declarations for each C function, variable, or type that one wants to use from Chapel code.

This explicit strategy allows for a great deal of manual control over how the Chapel compiler views a C function or type. This strategy is typically used within the Chapel standard modules for portability reasons. The c2chapel tool can be used to automatically generate extern declarations from a valid C99 file.

The extern block feature provides an implicit strategy, as described in the section Support for Extern Blocks below. This strategy makes use of the clang parser and so requires a Chapel compiler built with LLVM support. The main advantage of the extern block strategy is that it is not necessary to provide Chapel declarations for every desired C function or type.

Instead, such declarations are added automatically. In addition, the extern block can contain C function definitions in addition to declarations and so provides a way in which programs that need to be written in both C and Chapel can be implemented in a single source file. Whether using extern declarations or extern blocks, it is important to understand the basic interoperability support provided by the Chapel compiler.

Programmers inform the Chapel compiler about C concepts that they want to refer to in their Chapel code with explicit extern declarations or by creating extern declarations with extern blocks. Programmers use the extern C concepts that have been described.

That might include declaring variables, calling functions, or operating on already declared global variables. See Using Extern Declarations. The next section describes the basic information about how the Chapel compiler views common C types. This module defines a number of type aliases that accurately describe C types using their Chapel equivalents. These pointer types may only point to local memory. The intent is that they will be used to interoperate with C libraries that run within a single locale.

In addition, these pointer types must be treated carefully as it is possible to create the same kinds of problems as in C - in particular, it is necessary know that one of these types points to valid memory before it is used. As in C, these pointers could point to a stack variable that is no longer valid, or to memory that has been freed. Since the type is unknown, there is no way to dereference this pointer. Normally, a C function will return the void pointer, which will be passed to other C functions.

This pointer should normally only point to local memory - since no communication will be generated when it is dereferenced. Using the ref intent allows the arguments to the extern proc to be passed directly instead of needing to be converted to a C pointer first. For example, both the functions byRef and byPtr below have the same C prototype, but they must be used differently in Chapel:. A Chapel string can be constructed from a C string using the cast operator.

At present, there is no way to specify the argument types or return type of the function pointer. For example, given an external C function foo that takes in a pointer to a function that accepts an int and returns a double, the following code would declare that function and pass a Chapel function to it:.

Chapel allows users to refer to external C types, variables, and functions via extern declarations.



0コメント

  • 1000 / 1000