Thread overview
Parameter passing
Oct 16, 2002
Antti Sykari
Oct 16, 2002
Burton Radons
Oct 17, 2002
Sandor Hojtsy
Oct 17, 2002
Sean L. Palmer
Oct 17, 2002
Burton Radons
Oct 17, 2002
Sean L. Palmer
Oct 28, 2002
Walter
Oct 17, 2002
Sean L. Palmer
Re: Parameter passing, properties of functions
Oct 17, 2002
antti.sykari
October 16, 2002
Current situation:
 - Integer values are passed by value
 - I assume that structs are passed by value as well, correct me
   if I'm wrong
 - Objects are passed by reference
 - Array objects are passed by the infamous "something in between"

Another problem is:
 - Objects cannot be passed with "read-only" semantics
 - The same holds for arrays, I think

What we would ideally like is:
- The visible effect of passing parameters should be the same
  regardless of the type of object
- The user should be able to specify "constness" (C++-term) in
  function arguments

Right?

One solution could be:

Pass everything "const" by default. Then it doesn't matter whether the parameters are passed by reference, value, or whatever. The compiler may determine the most efficient calling convention. For example, it could just pass everything in registers and the function would not change their values. Or it could pass a pointer to stack or heap, if a bigger object is passed. But still, the function should only be able to call read-only ("const") functions of that class.

int f(int x, complex y, int z[])
{
        // x, y, z are passed in registers (for efficiency)

        // f may not change x, y, z or their contents, but can use
        // their values
}

int f(BigObject o)
{
        // o is passed by reference (implemented as passing a pointer)

        // still f could not change its value, only call it's
        // "const" functions
}

If function should be able to change its arguments, it should declare it explicitly with a keyword. What keyword? I can't say. I use "inout" here which might not be the best solution.

int f(BigObject x, BigObject y, inout BigObject z)
{
        // may not change x or y, but can change z
}

Or maybe it could use a different syntax like:

int f(BigObject x, BigObject y, BigObject! z)
{
        // may not change x or y, but can change z
}

This would be D equivalent of the C++ function:

int f(const BigObject& x, const BigObject& y, BigObject& z) { .. }

but IMHO looks simpler and takes the attention to where it belongs: in the z variable, which is in the danger of being modified by the function.

Naturally, if function _could_ change its arguments, they should be passed by reference, whether they are integral types, objects, arrays, or structs.

The downside with this scheme is that D doesn't currently support the concept of "read-only object". Now, I don't like the "const" keyword in function declarations, since it already means something different (namely, the usage like "const int x_size = 640;"). However, now that we have properties, maybe objects could have a context-dependent property "readOnly":

f(BigObject o)
{
        assert(o.readOnly == true);
}

g(BigObject! o)
{
        assert(o.readOnly == false);
}

Similarly, member functions should have this property as well, and it should be visible from the interface. It could be specified by the programmer, or calculated by the compiler. I'm not sure which one would fit the D philosophy best. It would work exactly like C++ const member functions, so that you could call a non-readOnly member function only on an not readOnly object. (Wanna extend the concept of properties to apply to functions, too?)

Antti
October 16, 2002
Antti Sykari wrote:
> Current situation:
>  - Integer values are passed by value
>  - I assume that structs are passed by value as well, correct me    if I'm wrong
>  - Objects are passed by reference
>  - Array objects are passed by the infamous "something in between"

Well... everything is passed by cell.  The cell of an instance is its reference, the cell of a struct is its fields.  An array's cell is its length and data pointer.  It's not in-between at all, just a struct (explicitly so - DLI and DMD both exploit this in implementing Phobos), and could only be said to be in-between if you insist on thinking of it as an array object when it's clearly not.

> Another problem is:
>  - Objects cannot be passed with "read-only" semantics
>  - The same holds for arrays, I think
> 
> What we would ideally like is:
> - The visible effect of passing parameters should be the same
>   regardless of the type of object
> - The user should be able to specify "constness" (C++-term) in
>   function arguments
> 
> Right?

I don't see how that makes me work faster or better, so no.

> One solution could be:
> 
> Pass everything "const" by default. Then it doesn't matter whether the
> parameters are passed by reference, value, or whatever. The compiler
> may determine the most efficient calling convention. For example, it
> could just pass everything in registers and the function would not
> change their values. Or it could pass a pointer to stack or heap, if a
> bigger object is passed. But still, the function should only be able
> to call read-only ("const") functions of that class.

const has no effect on whether an argument can or should be put in a register.  That's a decision only the compiler can make and only when it has the function body and only when it's not dealing with a virtual method.  The compiler of one module should be able to find out what decisions it made when compiling another module, but that's a separate issue.  However, I doubt it would be worth the trouble as you can't dance anything in a register for very long; it needs to be put into the stack for most function calls, and the x86 has an excruciatingly finite set of registers (and many common opcodes require specific registers, making the dance even harder).  By which point you may as well have a ready slot waiting.  If a parameter can be put on a register, it can almost exclusively be inlined, so this is catering to an edge-case that I doubt even exists outside of compiler idiosyncracies (being unable to inline something because of some strange ill-formedness - as worthy of being handled by the language as register was).

Register arguments in virtual methods could be a win, but it could be an ugly loss as well, as the decision has to be made based on the arguments rather than the body.  I haven't looked into fastcall in any detail at all.

October 17, 2002
"Burton Radons" <loth@users.sourceforge.net> wrote in message news:aojne9$2hs1$1@digitaldaemon.com...
> Antti Sykari wrote:
> > Current situation:
> >  - Integer values are passed by value
> >  - I assume that structs are passed by value as well, correct me
> >    if I'm wrong
> >  - Objects are passed by reference
> >  - Array objects are passed by the infamous "something in between"
>
> Well... everything is passed by cell.  The cell of an instance is its reference, the cell of a struct is its fields.  An array's cell is its length and data pointer.  It's not in-between at all, just a struct (explicitly so - DLI and DMD both exploit this in implementing Phobos), and could only be said to be in-between if you insist on thinking of it as an array object when it's clearly not.

Yes. But I think we need a (possibly separate) array type with the
properties of an object.
Like self-encapsulation and inheritance.

> > Another problem is:
> >  - Objects cannot be passed with "read-only" semantics
> >  - The same holds for arrays, I think
> >
> > What we would ideally like is:
> > - The visible effect of passing parameters should be the same
> >   regardless of the type of object
> > - The user should be able to specify "constness" (C++-term) in
> >   function arguments
> >
> > Right?
>
> I don't see how that makes me work faster or better, so no.

Const helps in
1) Avoiding some bugs
2) Expressive power
3) Self documentation

Sandor



October 17, 2002
"Burton Radons" <loth@users.sourceforge.net> wrote in message news:aojne9$2hs1$1@digitaldaemon.com...
> Antti Sykari wrote:
> > Another problem is:
> >  - Objects cannot be passed with "read-only" semantics
> >  - The same holds for arrays, I think
> >
> > What we would ideally like is:
> > - The visible effect of passing parameters should be the same
> >   regardless of the type of object
> > - The user should be able to specify "constness" (C++-term) in
> >   function arguments
> >
> > Right?
>
> I don't see how that makes me work faster or better, so no.

That's essentially what the "in" keyword does on a parameter.

> > One solution could be:
> >
> > Pass everything "const" by default. Then it doesn't matter whether the parameters are passed by reference, value, or whatever. The compiler may determine the most efficient calling convention. For example, it could just pass everything in registers and the function would not change their values. Or it could pass a pointer to stack or heap, if a bigger object is passed. But still, the function should only be able to call read-only ("const") functions of that class.
>
> const has no effect on whether an argument can or should be put in a register.  That's a decision only the compiler can make and only when it has the function body and only when it's not dealing with a virtual method.  The compiler of one module should be able to find out what decisions it made when compiling another module, but that's a separate issue.  However, I doubt it would be worth the trouble as you can't dance anything in a register for very long; it needs to be put into the stack for most function calls, and the x86 has an excruciatingly finite set of registers (and many common opcodes require specific registers, making the dance even harder).  By which point you may as well have a ready slot waiting.  If a parameter can be put on a register, it can almost exclusively be inlined, so this is catering to an edge-case that I doubt even exists outside of compiler idiosyncracies (being unable to inline something because of some strange ill-formedness - as worthy of being handled by the language as register was).

The arguments should always go in registers when possible.  The callee should just promise not to change any of them.  The compiler should try to enforce it too.  Why should you be able to write to your parameters?  It should be an error unless they're declared inout or out.

I'm not saying it has to be explicit.  But any constraint here will only give the compiler leverage, something it doesn't have otherwise.  It's a form of contract, a fairly primitive one at that.

I agree about the puny X86 architecture.  I am truly surprised it has weathered the time.  Intel has never innovated much in this respect.  Always short on registers.

> Register arguments in virtual methods could be a win, but it could be an ugly loss as well, as the decision has to be made based on the arguments rather than the body.  I haven't looked into fastcall in any detail at
all.

Perhaps you should before spouting off about having to dump all registers to stack before every function call.  The calling convention just has to have the called function guarantee not to change any of one set of registers, while the calling function expects to have all registers except for those potentially (likely) trashed.  That's what fastcall is.  And it passes the first four int parameters in such and such registers etc.  Layout also. Using such a convention the caller can cache core values in the registers that aren't involved in parameter passing and that are guaranteed to be saved.  The callee will save and restore them if it needs them.  Isn't that better?

Sean


October 17, 2002
I agree:  Pass all parameters as readonly by default unless you declare "out" or "inout" parameter.

I do kinda like your syntax.  Bang isn't used for anything in D except unary not, but I am not sure it'd parse unambiguously in a parameter block.  If it looks like a type it is a type.  So I guess it would work.  It certainly does draw attention to the *modified* parameters, and cleans up the syntax tremendously compared to the equivalent C++, even more than D's current in/out/inout does.  You could use ? for inout probably.  I imagine : is usable in a parameter list also if you want to add more stuff there.  May be useful to separate "in" section from "out" section kinda like public:

Try this:

foo(in : int a, int b; out : float result)
{
    result = a + b;
}

Or even (add simple relational contracts!):

foo( in { int a >= 0, int b > 0 }; out { double result >= 1.0 } )
{
    result = a + b;
}

How would you extend the concept of properties to apply to functions?

Sean

"Antti Sykari" <antti.sykari@housemarque.fi> wrote in message news:87wuoig5h3.fsf@cs190188.pp.htv.fi...
> One solution could be:
>
> Pass everything "const" by default. Then it doesn't matter whether the parameters are passed by reference, value, or whatever. The compiler may determine the most efficient calling convention. For example, it could just pass everything in registers and the function would not change their values. Or it could pass a pointer to stack or heap, if a bigger object is passed. But still, the function should only be able to call read-only ("const") functions of that class.
>
> int f(int x, complex y, int z[])
> {
>         // x, y, z are passed in registers (for efficiency)
>
>         // f may not change x, y, z or their contents, but can use
>         // their values
> }
>
> int f(BigObject o)
> {
>         // o is passed by reference (implemented as passing a pointer)
>
>         // still f could not change its value, only call it's
>         // "const" functions
> }
>
> If function should be able to change its arguments, it should declare it explicitly with a keyword. What keyword? I can't say. I use "inout" here which might not be the best solution.
>
> int f(BigObject x, BigObject y, inout BigObject z)
> {
>         // may not change x or y, but can change z
> }
>
> Or maybe it could use a different syntax like:
>
> int f(BigObject x, BigObject y, BigObject! z)
> {
>         // may not change x or y, but can change z
> }
>
> This would be D equivalent of the C++ function:
>
> int f(const BigObject& x, const BigObject& y, BigObject& z) { .. }
>
> but IMHO looks simpler and takes the attention to where it belongs: in the z variable, which is in the danger of being modified by the function.
>
> Naturally, if function _could_ change its arguments, they should be passed by reference, whether they are integral types, objects, arrays, or structs.
>
> The downside with this scheme is that D doesn't currently support the concept of "read-only object". Now, I don't like the "const" keyword in function declarations, since it already means something different (namely, the usage like "const int x_size = 640;"). However, now that we have properties, maybe objects could have a context-dependent property "readOnly":
>
> f(BigObject o)
> {
>         assert(o.readOnly == true);
> }
>
> g(BigObject! o)
> {
>         assert(o.readOnly == false);
> }
>
> Similarly, member functions should have this property as well, and it should be visible from the interface. It could be specified by the programmer, or calculated by the compiler. I'm not sure which one would fit the D philosophy best. It would work exactly like C++ const member functions, so that you could call a non-readOnly member function only on an not readOnly object. (Wanna extend the concept of properties to apply to functions, too?)

Go on...  ;)

> Antti

Sean


October 17, 2002
Sean L. Palmer wrote:
> "Burton Radons" <loth@users.sourceforge.net> wrote in message
> news:aojne9$2hs1$1@digitaldaemon.com...
> 
>>Antti Sykari wrote:
>>
>>>Another problem is:
>>> - Objects cannot be passed with "read-only" semantics
>>> - The same holds for arrays, I think
>>>
>>>What we would ideally like is:
>>>- The visible effect of passing parameters should be the same
>>>  regardless of the type of object
>>>- The user should be able to specify "constness" (C++-term) in
>>>  function arguments
>>>
>>>Right?
>>
>>I don't see how that makes me work faster or better, so no.
> 
> 
> That's essentially what the "in" keyword does on a parameter.
> 
> 
>>>One solution could be:
>>>
>>>Pass everything "const" by default. Then it doesn't matter whether the
>>>parameters are passed by reference, value, or whatever. The compiler
>>>may determine the most efficient calling convention. For example, it
>>>could just pass everything in registers and the function would not
>>>change their values. Or it could pass a pointer to stack or heap, if a
>>>bigger object is passed. But still, the function should only be able
>>>to call read-only ("const") functions of that class.
>>
>>const has no effect on whether an argument can or should be put in a
>>register.  That's a decision only the compiler can make and only when it
>>has the function body and only when it's not dealing with a virtual
>>method.  The compiler of one module should be able to find out what
>>decisions it made when compiling another module, but that's a separate
>>issue.  However, I doubt it would be worth the trouble as you can't
>>dance anything in a register for very long; it needs to be put into the
>>stack for most function calls, and the x86 has an excruciatingly finite
>>set of registers (and many common opcodes require specific registers,
>>making the dance even harder).  By which point you may as well have a
>>ready slot waiting.  If a parameter can be put on a register, it can
>>almost exclusively be inlined, so this is catering to an edge-case that
>>I doubt even exists outside of compiler idiosyncracies (being unable to
>>inline something because of some strange ill-formedness - as worthy of
>>being handled by the language as register was).
> 
> The arguments should always go in registers when possible.  The callee
> should just promise not to change any of them.  The compiler should try to
> enforce it too.  Why should you be able to write to your parameters?  It
> should be an error unless they're declared inout or out.

Why should you deny the capability?  I don't have to argue in favour of variable in arguments; it's already there as it has been for thirty years.  You have to show that it would be a better world if they were normally const.

> I'm not saying it has to be explicit.  But any constraint here will only
> give the compiler leverage, something it doesn't have otherwise.  It's a
> form of contract, a fairly primitive one at that.

The compiler can't use it as leverage for anything.  Proving that a parameter isn't changed is a simple enough operation that is done for any optimisations.  If it's used in a final method call or function, that can still be determined if the body is available.  If a virtual method call is made or the body isn't available for any function that is called, it's dirtied regardless, even if const is applied, as it's impossible to say whether the object is modified in a non-const scope or if the const is casted off inside.

Being able to cast off const just shows how fragile it is.  I can't even get MSVC to report it as an error.  It should be stressed that it's not closing any doors by allowing this - the first restriction closes all of them already.

>>Register arguments in virtual methods could be a win, but it could be an
>>ugly loss as well, as the decision has to be made based on the arguments
>>rather than the body.  I haven't looked into fastcall in any detail at
> 
> all.
> 
> Perhaps you should before spouting off about having to dump all registers to
> stack before every function call.  The calling convention just has to have
> the called function guarantee not to change any of one set of registers,
> while the calling function expects to have all registers except for those
> potentially (likely) trashed.  That's what fastcall is.  And it passes the
> first four int parameters in such and such registers etc.  Layout also.
> Using such a convention the caller can cache core values in the registers
> that aren't involved in parameter passing and that are guaranteed to be
> saved.  The callee will save and restore them if it needs them.  Isn't that
> better?

I know what fastcall is; I was meaning in a comparison for virtual methods.  How on Earth could I possibly attach passing on registers with fastcall - which isn't mentioned in the first message - without knowing what I was talking about?

Who flushes the registers to the stack - caller or callee - is irrelevant if the operation must be done for almost all code, although it's slightly more expensive to do it in the callee.

I could find only one comparison on fastcall versus cdecl and a test shows it doesn't apply to current MSVC.  Using the function:

   int test (int x, int y, int z)
   {
       return x + y + z;
   }

I've lost my clock count table but a from-memory comparison shows that this function takes 12 cycles in cdecl and 6 in fastcall.  But when I do:

   int w ();

   int test (int x, int y, int z)
   {
       return x + y + z + w ();
   }

The cdecl form is 14 clock cycles, while the fastcall form is 24.  This IS partly evened out by the caller, but even that depends upon the vicissitudes of how it's being called.  If any argument in is a function call or an expression, it'll be more expensive overall, and will quickly equal cdecl in the first test, too.  A very temperamental optimisation applied with a sledge hammer.

Functions which both have to be bodyless and could benefit from fastcall (not only in the body, but in their usage) are, thankfully, vanishingly small.  Certainly not worth the effort to support them.

October 17, 2002
I'm not going to bend over backwards for you.  If you can't or won't see the benefit, I'm not going to be able to convince you otherwise.  It appears that others have tried and failed.

Just keep an open mind.

Isn't consistency worth anything?

A slight improvement to the status quo is not what I'm after.  I want a big paradigm shift.  Quibbling about calling conventions is irrelevant to my goals.  Some people were perfectly happy back in the C world.  I am not one of them.

But I will say that any calling convention that forces preemptive, speculative flushing of registers to stack is broken.  And any interface specification that gives me no guarantees that a function won't modify my parameter I'm sending in is also broken since to be safe I'd have to hand the function a *copy* of my data.  That drawback should be obvious to you. Why should I have to explain it to you?

Sean

"Burton Radons" <loth@users.sourceforge.net> wrote in message news:aom887$24sa$1@digitaldaemon.com...
> Sean L. Palmer wrote:
> > "Burton Radons" <loth@users.sourceforge.net> wrote in message news:aojne9$2hs1$1@digitaldaemon.com...
> >
> >>Antti Sykari wrote:
> >>
> >>>Another problem is:
> >>> - Objects cannot be passed with "read-only" semantics
> >>> - The same holds for arrays, I think
> >>>
> >>>What we would ideally like is:
> >>>- The visible effect of passing parameters should be the same
> >>>  regardless of the type of object
> >>>- The user should be able to specify "constness" (C++-term) in
> >>>  function arguments
> >>>
> >>>Right?
> >>
> >>I don't see how that makes me work faster or better, so no.
> >
> >
> > That's essentially what the "in" keyword does on a parameter.
> >
> >
> >>>One solution could be:
> >>>
> >>>Pass everything "const" by default. Then it doesn't matter whether the parameters are passed by reference, value, or whatever. The compiler may determine the most efficient calling convention. For example, it could just pass everything in registers and the function would not change their values. Or it could pass a pointer to stack or heap, if a bigger object is passed. But still, the function should only be able to call read-only ("const") functions of that class.
> >>
> >>const has no effect on whether an argument can or should be put in a register.  That's a decision only the compiler can make and only when it has the function body and only when it's not dealing with a virtual method.  The compiler of one module should be able to find out what decisions it made when compiling another module, but that's a separate issue.  However, I doubt it would be worth the trouble as you can't dance anything in a register for very long; it needs to be put into the stack for most function calls, and the x86 has an excruciatingly finite set of registers (and many common opcodes require specific registers, making the dance even harder).  By which point you may as well have a ready slot waiting.  If a parameter can be put on a register, it can almost exclusively be inlined, so this is catering to an edge-case that I doubt even exists outside of compiler idiosyncracies (being unable to inline something because of some strange ill-formedness - as worthy of being handled by the language as register was).
> >
> > The arguments should always go in registers when possible.  The callee should just promise not to change any of them.  The compiler should try
to
> > enforce it too.  Why should you be able to write to your parameters?  It should be an error unless they're declared inout or out.
>
> Why should you deny the capability?  I don't have to argue in favour of variable in arguments; it's already there as it has been for thirty years.  You have to show that it would be a better world if they were normally const.
>
> > I'm not saying it has to be explicit.  But any constraint here will only give the compiler leverage, something it doesn't have otherwise.  It's a form of contract, a fairly primitive one at that.
>
> The compiler can't use it as leverage for anything.  Proving that a parameter isn't changed is a simple enough operation that is done for any optimisations.  If it's used in a final method call or function, that can still be determined if the body is available.  If a virtual method call is made or the body isn't available for any function that is called, it's dirtied regardless, even if const is applied, as it's impossible to say whether the object is modified in a non-const scope or if the const is casted off inside.
>
> Being able to cast off const just shows how fragile it is.  I can't even get MSVC to report it as an error.  It should be stressed that it's not closing any doors by allowing this - the first restriction closes all of them already.
>
> >>Register arguments in virtual methods could be a win, but it could be an ugly loss as well, as the decision has to be made based on the arguments rather than the body.  I haven't looked into fastcall in any detail at
> >
> > all.
> >
> > Perhaps you should before spouting off about having to dump all
registers to
> > stack before every function call.  The calling convention just has to
have
> > the called function guarantee not to change any of one set of registers, while the calling function expects to have all registers except for
those
> > potentially (likely) trashed.  That's what fastcall is.  And it passes
the
> > first four int parameters in such and such registers etc.  Layout also. Using such a convention the caller can cache core values in the
registers
> > that aren't involved in parameter passing and that are guaranteed to be saved.  The callee will save and restore them if it needs them.  Isn't
that
> > better?
>
> I know what fastcall is; I was meaning in a comparison for virtual methods.  How on Earth could I possibly attach passing on registers with fastcall - which isn't mentioned in the first message - without knowing what I was talking about?
>
> Who flushes the registers to the stack - caller or callee - is irrelevant if the operation must be done for almost all code, although it's slightly more expensive to do it in the callee.
>
> I could find only one comparison on fastcall versus cdecl and a test shows it doesn't apply to current MSVC.  Using the function:
>
>     int test (int x, int y, int z)
>     {
>         return x + y + z;
>     }
>
> I've lost my clock count table but a from-memory comparison shows that this function takes 12 cycles in cdecl and 6 in fastcall.  But when I do:
>
>     int w ();
>
>     int test (int x, int y, int z)
>     {
>         return x + y + z + w ();
>     }
>
> The cdecl form is 14 clock cycles, while the fastcall form is 24.  This IS partly evened out by the caller, but even that depends upon the vicissitudes of how it's being called.  If any argument in is a function call or an expression, it'll be more expensive overall, and will quickly equal cdecl in the first test, too.  A very temperamental optimisation applied with a sledge hammer.
>
> Functions which both have to be bodyless and could benefit from fastcall (not only in the body, but in their usage) are, thankfully, vanishingly small.  Certainly not worth the effort to support them.
>


October 17, 2002
"Sean L. Palmer" <seanpalmer@directvinternet.com> writes:

> How would you extend the concept of properties to apply to functions?

http://www.digitalmars.com/d/property.html does not introduce very many of properties, but states:

"Every type and expression has properties that can be queried."

Uses of properties in general seem to fall into the following categories (I'll just categorize those of floating point numbers, since there are a lot of them):

1. A meta facility to get knowledge of a type or an expression:
.size       size in bytes
.init       initializer

2. A collection of constants closely related to the type:
.max        largest representable value that's not infinity
.min        smallest representable value that's not 0
.infinity   infinity value
.nan        NaN value
.mantissa   number of bits in mantissa
.maxExp     maximum exponent as power of 2 (?)
.digits     number of digits of precision
.epsilon    smallest increment

3. A set of functions which can be used to get a property of a
floating point or possibly integral number:
.sign       1 if -, 0 if +
.isnan      1 if nan, 0 if not
.isinfinite 1 if +-infinity, 0 if not
.isnormal   1 if not nan or infinity, 0 if

Now I don't know if the number and nature of these properties are fixed, but functions being first-class citizens and all, at least with regard to passing them in parameters, why not throw in some properties for them also:

class X
{
        void foo(int x)
        {
                m_x = 5;
        }

private:
        int m_x;
}

void g()
{
        if (X.foo.isMemberFunction) {
           // foo is a member function! Now this can't possibly be
           // useful, except in template code. Who knows?
        }

        if (X.foo.synchronized) {
            // foo is a synchronized function (is there such a concept
            // in D?)
        }

        if (X.foo.final) {
            // foo cannot be overridden in a derived class
        }

        if (X.foo.public) {
            // foo is public
        }
}

But I just realized that this pretty much conflicts with the getter/setter issue in case that X.foo happens to be a variable.

Other ideas for function properties: thread-safety; number of arguments; even types or names of arguments, and places and line numbers where the function is defined, as in __FILE__ && __LINE__;

Many of these ideas could be extended to the properties of classes, and they would mostly fit into the category 1 of properties ("collecting meta-linguistic information about types and functions")

I actually found most of the properties above in http://www.csci.csusb.edu/dick/samples/java.glossary.html, in the "Modifiers" section. How funny of that, the language is java. :)

Another idea that popped into my mind was that the programmer could set the properties for arbitrary types such as one could mark "the class is thread-safe" or something like that by saying T.threadSafe = true; This was, of course, a stupid idea since it can be already done like:

class T
{
        // all member functions and accessed synchronized!
        const bool threadSafe = true;
}

Well, these are just ideas, not a long-thought proposition or anything. They are presented in the hope that somebody may find inspiration in them.

Now, back to the "olden days, when EVIL ruled":

> I imagine : is usable in a parameter list also if you want to add more stuff there.  May be useful to separate "in" section from "out" section kinda like public:
>
> Try this:
>
> foo(in : int a, int b; out : float result)
> {
>     result = a + b;
> }

A silly idea along the lines of good old pre-ANSI C:

foo(a, b, result)
in:
        int a, b;
out:
        float result;
{
}

It is, after all, a sort of natural style of indentation if you
have to write a lot of stuff in the function declaration. If I had to
include "in" or "out" in the function declaration, I'd probably indent
it like:

void
foo(
    in  int     a,
    in  int     b,
    out float   result)
in
{
    assert(a >= 0);
    assert(b >= 0);
}
out (result)
{
    assert(result >= 1);
}
body
{
  // ...
}

so as to enhance readability. (Consequent series of <visibility class>
+ <type> + <variable name> is bit more difficult to read than just
<type> + <name>, hence the preference for vertical layout instead
of horizontal.)

> Or even (add simple relational contracts!):
>
> foo( in { int a >= 0, int b > 0 }; out { double result >= 1.0 } )
> {
>     result = a + b;
> }

The same in K&R style:

foo(a, b, result)
in:
        int a >= 0;     // or just invariant { assert(a >= 0); } or something
        int b > 0;
out:
        double result >= 1.0;
{
        result = 1.0 + a + b;
}

[I took the liberty of changing the code because it broke the contract]

It's more readable than if it was laid out on just one line, right? *grin*

I am not serious about this, of course. :)

Yet I have an afterthought: one of the reasons K&R style was abandoned must've been that it is tedious to maintain two longish interface specification for the function. (Although C isn't particularly picky on function declarations back then, it is considered good manners.)

But D does not have header files, so abandoning K&R style function declarations doesn't have that excuse anymore.

Antti.

October 28, 2002
"Burton Radons" <loth@users.sourceforge.net> wrote in message news:aojne9$2hs1$1@digitaldaemon.com...
> Register arguments in virtual methods could be a win, but it could be an ugly loss as well, as the decision has to be made based on the arguments rather than the body.  I haven't looked into fastcall in any detail at
all.

That's just the problem with fastcall. It can make things slower in many cases. A better algorithm would be to determine the parameter convention after optimizing the body of the function, then if any parameters are enregistered, pass them in those registers.

DMD doesn't do this, of course, but D is designed so the door is open for that kind of optimization, which could be a big win.