Last update March 7, 2012

Doc Comments /
Function



Functions    

Table of contents of this page
Functions   
Messages   
Virtual Functions concerns   
Pure Virtual Functions   
Portability and Variadic Functions   
Nested Functions   
How in/out/inout Works With Arrays   
How C Function Pointers Differ from D Function Pointers and Delegates   
The bare() function   
Links   

Messages    

Function parameters, sound, clear and clean

I'm new to D. I think D is a great idea, a much needed thing. But I find its specs are terribly sketchy. With sketchy specs D can't go anywhere, I think.

Here is an attempt of mine to establish sound, clear and clean specs in a simple area to start: function parameters.

I read parameters in D could be of sort "in", "out", "ref", "lazy", "final", "const", "immutable, and "scope", but I see almost no definitions of these.

This is what I get so far:

in - Function gets value but no access to the external variable related to it.

out - Allows update of original external variables but first there's an innitial reset of them.

ref - Not explained here. "Tango w/D" says this is like "out" (access to the original variable) but without changing their values before entering the body of the function.

lazy - Apparently this applies only to parameters that are references to functions (or to expressions that somehow are packed in a function-like manner). It ensures those functions or expressions are executed ("evaluated") only if, when, and as many times, as the code inside the function body requests it.

scope and others - I find no explanations about what will imply to qualify a parameter with these.

But OK, I get at least what some of the intentions are.

Question: Can't all the above be achieved just by having the option of adding a one-character token (say the semicolon ';') somewhere in the parameter list of the function?

The semicolon would divide the list in two zones: first the "input" and then the "input-output" zone. If there is no ';', all would be input.

All variables in the input zone would be of the classical "by value" type. If an object in this zone is big, the compiler should know not to copy it and use a reference to constant (the original variables will contribute their values and it won't be possible to change them in the function body).

And in the second zone all would be references, with complete access to the original variables. If there would be some advantage (I've never had need of this) of resetting some of those variables before entering the body of the function, then that could be done manually or applying some keyword, like "reset" or "init", to the corresponding parameter.

About those "lazy" function-expresion references, that behaviour is just what is commonly needed, it should be the default. Maybe an optional "run" command could be implemented to be added to the reference *in the function calls*, to have it the other way too if desired.

It would look like this:

storclass rettype funcid( inparams ; outparams ){ body }

This seems to me what essentially would be a huge improvement over C/C++ and surely over other languages too, in reference to types of function parameters.

If there is any serious inconveniences to this way I propose, or something that I'm missing, let's talk about it. And come to conclusions, and leave it clearly established.

If there are other functionalities in this area that are worth while, someone should describe and justify them *clearly and completely*. So other additions could be eventually incorporated and then they would also be *clearly established*.

I repeat, with sketchy specs D can't really move ahead. I think.

Virtual Functions concerns    

I think the "Virtual Functions" example has a few problems, mostly with the abc functions (one is declared private in A, and another is declared in B).
  1. How can the test function even "see" abc?
  2. The comment on abc in B seems to miss a point: it's not that A.abc is not virtual, it's that A.abc is not even visible! Also, it says that B.abc is virtual, which implies that even in this simple example, D is unable to "prove it can be made non-virtual".
Suppose you allow/support the useful feature of dynamically loading images. Suppose that new image defines a derived class which overrides a function. What happened to any proof that the base class's function "can be made non-virtual"?

Pure Virtual Functions    

Pure virtual functions are marked with the abstract attribute (see DigitalMars:d/attribute.html )

  class C {
    abstract int f();
  }

Portability and Variadic Functions    

The current information is DMD-specific, and not portable to GDC (and any other D compilers?)

"_argptr" should be of the type std.stdarg.va_list and one must use the templates in there. Manipulating it as a pointer is *not* portable. The offical page should show using templates.

Under "Variadic Functions With Type Info" it says "An additional hidden argument with the name _arguments and type TypeInfo?[] is passed to the function. _arguments gives the number of arguments and the type of each, enabling the creation of typesafe variadic functions." but perhaps that should end with "enabling the creation of variadic functions with type safety" so that it's not confused with the actual "Typesafe Variadic Functions" feature.

Nested Functions    

(by Vincent Thorn) Example after words "Nested functions can be accessed only if the name is in scope" contains error:

void A()

   {
     B();   // ok
     C();   // error, C undefined
   }
Call to function B() is NOT OK! Cause for internal scope compiler looks for definitions in physical order. OF COURSE it must be done like in module scope - in ANY order! And notification about lexical order must be first because people can miss this important point.

How in/out/inout Works With Arrays    

There should be a clear indication of how in/out/inout works with arrays. Someone discovering that arrays are first-class objects may be tempted to think that, if an array is passed as in, then the function cannot modify the array. The current behaviour is consistent with the fact that dynamic arrays have reference semantics; however, I can see that some may either overlook it or wonder if arrays are an exception with more intuitive behaviour.

Something along the lines of this:

For dynamic array parameters, which have reference semantics, the in/out/inout applies only to the address and length of the array, not to its contents. Therefore, a function that has a dynamic array as an in parameter can modify the contents of the array in place. If a dynamic array is an out or inout parameter, the function can also set the argument to point to a new array or change its length.

Source: NG:digitalmars.D/5468

This diagram might be useful as well:

http://bayimg.com/NaeOgaaCC

How C Function Pointers Differ from D Function Pointers and Delegates    

The first thing to remember is that C function pointers are simply pointers to some address in memory. They contain, in their type data, information about the arguments and the return type, but at runtime, they are just a pointer like any other pointer. You can cast a function pointer to a void* and then get the address of the function in memory, for example. Likewise, you can cast a void* back to a function pointer.

D function pointers are the same thing. They are a simple, single pointer.

When you call a function pointer in either language, the code puts your arguments (if any) onto the stack, and then makes a subroutine call to whatever address is stored in the function pointer.

The only difference between D and C function pointers is that (in some architectures) the calling convention may be different. There are various assumptions made by each language (who saves copies of registers, who cleans up the stack, the order of arguments on the stack, etc.) which may vary from language to language. The point of

 extern(C)
is to inform the D compiler that it is talking to a C function, and so it must talk like a C function. This may be different than D's conventions.

This is why you can't store a pointer to a D function in an extern(C) function pointer, or vice-versa. If the function pointer is declared extern(C), then you are saying that the compiler should use C calling conventions when it calls the function. If it is actually a D function, then things will break. To solve this, you implement a trivial wrapper function, which calls the function pointer. It serves as an interpreter between the D function and the C function. You can use wrappers in either direction; to store a pointer to a C function in a D function pointer, then create a D wrapper which calls the C function; the opposite also works.

Delegates are another beast. They are a bit of a conceptual jump, but once you get used to them you'll never want to go back. Delegates are actually two pointers stored in a single variable. One of the pointers is a function pointer to a D function; the other is a void*. When you call a delegate, it passes the void* as the implicit first argument. This is exactly like how class member functions are called.

Think about how a class member function works. Look at this example:

 class Foo {
    int val;
    int foo() { return val; }
 }
 int bar(Foo f) { return f.val; }
The functions Foo.foo() and bar(Foo) are pretty much the same function. The class member function has an implicit argument (Foo) which is automatically passed when you call the member. So the code below, which calls the two functions, is almost identical when you get down to the bare metal:

 Foo f = new Foo;
 ...
 int x = f.foo();
 int y = bar(f);
Delegates work the same way. They just carry around the "this" pointer hidden inside the delegate. So this delegate:

 int delegate() dg = *f.foo;
is pretty much the same thing as this struct:

 struct my_delegate {
    int function(void*) func;
    void *ptr;
 };
 my_delegate my_dg;
 my_dg.func = cast(int function(void*))&bar;
 my_dg.ptr  = cast(void*)f;
And calling the delegate:

 int a = dg();
is just like calling the function pointer with the stored argument:

 int b = my_dg.func(my_dg.ptr);
I would highly recommend that, in your D programs, you always use a delegate any time that you might think about using a function pointer. They are far more flexible; if you ever, in the future, decide that you needed a delegate, they are there. You can always write a wrapper function which turns a function into a delegate:

 int myFunc();
 int delegate() dg = &myFunc();
 	/* syntax error, can't save funcptr in a delegate */
 int delegate() dg = delegate int() { return myFunc(); }
 	/* ok! */
(Technically, you've created a stack delegate above. The 'ptr' of the delegate is actually a pointer to the current stack frame...but since we don't ever use any stack variables, we don't care about the fact that the stack frame isn't valid later on.)

The only complexity here is that if you have a function pointer variable and you have to turn it into a delegate. Then you have to write a tiny struct, and take a delegate from a member function of that struct:

 int delegate() ConvertFuncPtrToDelegate(int function() func) {
    struct Storer {
      int function() func;
      int Call() { return func(); }
    };
    Storer *temp = new Storer[1];
    temp.func = func;
    return &temp.Call();
 }
Source: From NG:digitalmars.D/13707

The bare() function    

All compilers have a predefined piece of code that preceeds the main() function to set everything up prior to main. Sometimes it is required to write your own (not often but sometimes). The concept of the bare() function would be to define the entry point for the application, a function with no preceeding code.

pragma(entry_point, 0x03000000);   // Set the entry point
void bare()
{
    // Set up the stack

    // Set up the heap

    // Do any initialisation

    int rtn = main();

    // Pass return  to the OS

}

Links    


FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: March 7, 2012 20:24 (diff))