Thread overview
Re: Can we have context functions?
Mar 15, 2003
Nic Tiger
Mar 19, 2003
Nic Tiger
Mar 24, 2003
Farmer
Mar 25, 2003
Nic Tiger
Mar 26, 2003
Farmer
Apr 02, 2003
David Leonard
Apr 02, 2003
Sean L. Palmer
Apr 02, 2003
Nic Tiger
Apr 02, 2003
Sean L. Palmer
Apr 02, 2003
Walter
March 15, 2003
Hi Walter,

Recently I thought a lot of concept which would provide support for logging and tracking without Cish macroses.

This thoughts led me to the notion of context functions.

DEFINITION. Context function is a function which accepts along  with it's own parameters several hidden parameters such as FILE, LINE or FUNC. This hidden parameters are always passed when the function is called and have values describing the place where call is originated.

Let's suppose that __context keyword enables us to define context function,
and __file, __line, __func are keywords that designate hidden parameters
inside context function.
Now let's look at concepts of using logging and tracking in C and D.

1. Logging - the method to store some information along with description of
it's sourse
============ C

#define LogStr(str)  DoWriteLogStr ( "%s,%d: %s\n", __FILE__, __LINE__,
str )
...
main ()
{
 LogStr ("got here" );
 ...
 LogStr ("got here" );
}

============ D
__context void LogStr ( char[] str )
{
 printf ( "%.*s,%d: %.*s\n", __file, __line, str );
}
...
main ()
{
 LogStr ("got here" );
 ...
 LogStr ("got here" );
}

2. Tracking - the method to determine who called particular function
============ C
#define _malloc(s)  malloc_tracked (s, __FILE__, __LINE__)

void *malloc_tracked ( size_t size, char *file, int line )
{
 printf ("malloc(%d) from %s,%d\n", size, file, line );
 return malloc ( size );
}

main ()
{
 void *p = _malloc ( 10 );
}

============ D
__context void *malloc ( size_t size )
{
 printf ("malloc(%d) from %s,%d\n", size, __file, __line );

 // do allocation here...
}

main()
{
 void *p = malloc ( 10 );
}

========================== end of examples

Note that in D we not only got rid of preprocessor usage and have the identical behaviour, but also eliminated intermediate function malloc_tracked in the second example.

As I see the implementation for this concept is trivial - pass context
parameters to function just like you pass object pointer to class methods.
The keywords used bear imagination. I don't think that names __context and
others are perfect, but they are relevant to problem being solved.
Another issue is that we need support via version statement in order to find
out whether current function is context or not. Then we could rewrite the
second example as:
/*__context*/ void *malloc ( size_t size )
{
 version(__context) {
  printf ("malloc(%d) from %s,%d\n", size, __file, __line );
 }

 // do allocation here...
}

And now we can turn functions into context functions and vice versa easily.

I would like you to consider the examples given and say your word about it. Any critique and afterthought are welcome.

Nic Tiger.


March 19, 2003
More thoughts: we can specify context parameters passed with this simple syntax.

__context(__file, __line) void LogStr ( char[] str )
{
 printf ( "%.*s,%d: %.*s\n", __file, __line, str );
}

Then only declared context parameter should be available inside context function. This simplifies compiler logic - simply pass context parameters function wanted.

As for inapplicable context parameters (such as __class when calling not
from member-function), the can be passed either NULL or "" (void string).

I really would like to here anyone's opinion on this.

Nic Tiger.

"Nic Tiger" <nictiger@progtech.ru> ÓÏÏÂÝÉÌ/ÓÏÏÂÝÉÌÁ × ÎÏ×ÏÓÔÑÈ ÓÌÅÄÕÀÝÅÅ: news:b503u0$22db$1@digitaldaemon.com...
> Hi Walter,
>
> Recently I thought a lot of concept which would provide support for
logging
> and tracking without Cish macroses.
>
> This thoughts led me to the notion of context functions.
>
> DEFINITION. Context function is a function which accepts along  with it's own parameters several hidden parameters such as FILE, LINE or FUNC. This hidden parameters are always passed when the function is called and have values describing the place where call is originated.
>
> Let's suppose that __context keyword enables us to define context
function,
> and __file, __line, __func are keywords that designate hidden parameters
> inside context function.
> Now let's look at concepts of using logging and tracking in C and D.
>
> 1. Logging - the method to store some information along with description
of
> it's sourse
> ============ C
>
> #define LogStr(str)  DoWriteLogStr ( "%s,%d: %s\n", __FILE__, __LINE__,
> str )
> ...
> main ()
> {
>  LogStr ("got here" );
>  ...
>  LogStr ("got here" );
> }
>
> ============ D
> __context void LogStr ( char[] str )
> {
>  printf ( "%.*s,%d: %.*s\n", __file, __line, str );
> }
> ...
> main ()
> {
>  LogStr ("got here" );
>  ...
>  LogStr ("got here" );
> }
>
> 2. Tracking - the method to determine who called particular function
> ============ C
> #define _malloc(s)  malloc_tracked (s, __FILE__, __LINE__)
>
> void *malloc_tracked ( size_t size, char *file, int line )
> {
>  printf ("malloc(%d) from %s,%d\n", size, file, line );
>  return malloc ( size );
> }
>
> main ()
> {
>  void *p = _malloc ( 10 );
> }
>
> ============ D
> __context void *malloc ( size_t size )
> {
>  printf ("malloc(%d) from %s,%d\n", size, __file, __line );
>
>  // do allocation here...
> }
>
> main()
> {
>  void *p = malloc ( 10 );
> }
>
> ========================== end of examples
>
> Note that in D we not only got rid of preprocessor usage and have the identical behaviour, but also eliminated intermediate function malloc_tracked in the second example.
>
> As I see the implementation for this concept is trivial - pass context parameters to function just like you pass object pointer to class methods. The keywords used bear imagination. I don't think that names __context and others are perfect, but they are relevant to problem being solved. Another issue is that we need support via version statement in order to
find
> out whether current function is context or not. Then we could rewrite the
> second example as:
> /*__context*/ void *malloc ( size_t size )
> {
>  version(__context) {
>   printf ("malloc(%d) from %s,%d\n", size, __file, __line );
>  }
>
>  // do allocation here...
> }
>
> And now we can turn functions into context functions and vice versa
easily.
>
> I would like you to consider the examples given and say your word about
it.
> Any critique and afterthought are welcome.
>
> Nic Tiger.
>
>


March 24, 2003
Hi.

Your idea of passing "compiler context" implicitly to functions seems very useful. I think, it could cover all common uses of the C preprocessor constants (__file, __line, etc.).

Here are my critiques and afterthoughts ;-)


What about making it look like a usual function, that has some parameters that are set implicitly by the compiler ?

E.g. instead of

> __context(__file, __line) void LogStr ( char[] str )
> {
>  printf ( "%.*s,%d: %.*s\n", __file, __line, str );
> }


void LogStr ( char[] str, char[] __file, int __line )
{
  printf ( "%.*s,%d: %.*s\n", __file, __line, str );
}

or shorter
void LogStr ( char[] str,  __file,  __line );

or similar to C default parameters
void LogStr ( char[] str,  char[] fileName=__file,  int lineNo=__line )
{  printf ( "%.*s,%d: %.*s\n", fileName, lineNo, str ); }

or
void LogStr ( char[] str,  __file fileName,  __line lineNo)
{  printf ( "%.*s,%d: %.*s\n", fileName, lineNo, str ); }


or use a context structure in order to safe keywords and make it more
extendable:
void LogStr( char[] str,  __context )
{  printf ( "%.*s,%d: %.*s\n", __context.file, __context.line, str ); }

maybe you should be able to pass only some information of the context
structure:
void LogStr( char[] str,  __context.file)
{  printf ( "%.*s,%d: %.*s\n", __context.file, __context.line, str ); }
// above,  __context.line is no longer valid.


In some examples, I used the common D types int and char[]; to avoid any conflicts with argument overloading, the special context parameters should have their own type. Should be easy with typedef.


One drawback compared with your proposed syntax is that versioning would require more typing, possibly cluttering interfaces a lot:


version (tracedMalloc)
   void *malloc ( size_t size, __file, __line )
   {
     printf ("malloc(%d) from %s,%d\n", size, __file, __line );
   }
else
   void *malloc ( size_t size)
   {
     printf ("malloc(%d) from %s,%d\n", size);
   }

instead of just
>/*__context*/ void *malloc ( size_t size )
>{
> version(__context) {
>  printf ("malloc(%d) from %s,%d\n", size, __file, __line );
> }


But hey, you are cheating! __context is just commented out! That won't scale up to many functions.



Farmer.
March 25, 2003
Thanks for reply.

I don't think it is convenient for programmer and for compiler vendor too to
specify context functions via adding some magic names of parameters to
function declaration.
We don't add __class_object to all member functions, for example. Instead,
we have it implicit and have access to it via this.
Context functions are quite the same.

And also we can make __context keyword working just like extern(C), for
example;

__context(__file, __line) void LogStr ( char[] str ) {...}

or

__context(__file, __line) {
 void LogStr ( char[] str ) {...}
 void Log2Str ( char[] str ) {...}
 // and so on.
}

Anyway, adding hidden parameters to function parameters list seems muddy for
me.
How can I understand that I should not pass this parameters and they are
passed automatically by compiler?

On overloading issue I would say that context functions are just like member function (from implementation point of view). We have overloaded member functions? Why do we get troubles with context ones?

And also I would prohibited such declarations:
   __context(__file, __line) void LogStr ( char[] str ) {...}
   __context(__file) void LogStr ( char[] str ) {...}
because they are ambiguous.

Anyway I would like to hear Walter's opinion on that.

Nic Tiger.
"Farmer" <itsFarmer.@freenet.de> ÓÏÏÂÝÉÌ/ÓÏÏÂÝÉÌÁ × ÎÏ×ÏÓÔÑÈ ÓÌÅÄÕÀÝÅÅ:
news:Xns9348E7CA7DABEitsFarmer@63.105.9.61...
> Hi.
>
> Your idea of passing "compiler context" implicitly to functions seems very useful. I think, it could cover all common uses of the C preprocessor constants (__file, __line, etc.).
>
> Here are my critiques and afterthoughts ;-)
>
>
> What about making it look like a usual function, that has some parameters that are set implicitly by the compiler ?
>
> E.g. instead of
>
> > __context(__file, __line) void LogStr ( char[] str )
> > {
> >  printf ( "%.*s,%d: %.*s\n", __file, __line, str );
> > }
>
>
> void LogStr ( char[] str, char[] __file, int __line )
> {
>   printf ( "%.*s,%d: %.*s\n", __file, __line, str );
> }
>
> or shorter
> void LogStr ( char[] str,  __file,  __line );
>
> or similar to C default parameters
> void LogStr ( char[] str,  char[] fileName=__file,  int lineNo=__line )
> {  printf ( "%.*s,%d: %.*s\n", fileName, lineNo, str ); }
>
> or
> void LogStr ( char[] str,  __file fileName,  __line lineNo)
> {  printf ( "%.*s,%d: %.*s\n", fileName, lineNo, str ); }
>
>
> or use a context structure in order to safe keywords and make it more
> extendable:
> void LogStr( char[] str,  __context )
> {  printf ( "%.*s,%d: %.*s\n", __context.file, __context.line, str ); }
>
> maybe you should be able to pass only some information of the context
> structure:
> void LogStr( char[] str,  __context.file)
> {  printf ( "%.*s,%d: %.*s\n", __context.file, __context.line, str ); }
> // above,  __context.line is no longer valid.
>
>
> In some examples, I used the common D types int and char[]; to avoid any conflicts with argument overloading, the special context parameters should have their own type. Should be easy with typedef.
>
>
> One drawback compared with your proposed syntax is that versioning would require more typing, possibly cluttering interfaces a lot:
>
>
> version (tracedMalloc)
>    void *malloc ( size_t size, __file, __line )
>    {
>      printf ("malloc(%d) from %s,%d\n", size, __file, __line );
>    }
> else
>    void *malloc ( size_t size)
>    {
>      printf ("malloc(%d) from %s,%d\n", size);
>    }
>
> instead of just
> >/*__context*/ void *malloc ( size_t size )
> >{
> > version(__context) {
> >  printf ("malloc(%d) from %s,%d\n", size, __file, __line );
> > }
>
>
> But hey, you are cheating! __context is just commented out! That won't scale up to many functions.
>
>
>
> Farmer.


March 26, 2003
"Nic Tiger" <nictiger@progtech.ru> wrote in news:b5q2jt$1v3h$1@digitaldaemon.com:

> Thanks for reply.
> 
> I don't think it is convenient for programmer and for compiler vendor
> too to specify context functions via adding some magic names of
> parameters to function declaration.
> We don't add __class_object to all member functions, for example.
> Instead, we have it implicit and have access to it via this.
> Context functions are quite the same.
> 
> And also we can make __context keyword working just like extern(C),
> for example;

Yes, making the __context keyord like extern(C) is quite appealing. I could think of __context(...) as a kind of calling convention, which it is from a implementation point of view.


> 
> __context(__file, __line) void LogStr ( char[] str ) {...}
> 
> or
> 
> __context(__file, __line) {
>  void LogStr ( char[] str ) {...}
>  void Log2Str ( char[] str ) {...}
>  // and so on.
> }
> 
> Anyway, adding hidden parameters to function parameters list seems
> muddy for me.
> How can I understand that I should not pass this parameters and they
> are passed automatically by compiler?

You should be able to pass these "context" parameters; a D compiler is merely kind enough to provide them for you. Sometimes an extra level of control is handy. It also makes calling a "context" function from outside D very obvious. Of course, when using your syntax, language interoperability isn't a problem, either.


> 
> On overloading issue I would say that context functions are just like member function (from implementation point of view). We have overloaded member functions? Why do we get troubles with context ones?

Right. There aren't any problems. I just commented on my own style of syntax. When using my syntax, overloading ambiguities could arise, as they do for default parameters in C++. By using non-standard types, these ambiguities can practically be avoided.


> 
> And also I would prohibited such declarations:
>    __context(__file, __line) void LogStr ( char[] str ) {...}
>    __context(__file) void LogStr ( char[] str ) {...}
> because they are ambiguous.
> 
> Anyway I would like to hear Walter's opinion on that.

Ok, let's wait.


Farmer.
April 02, 2003
How about a library module that allows access to the call stack? Help stop feeping creaturism.

import callstack;

void LogStr(char[] str)
{
    callstack.Frame f = callstack.getPreviousFrame(2); // caller's caller
    printf("%s:%d:%s(): %s\n", f.sourceFile, f.lineNo, f.symbol, str);
}


April 02, 2003
You're basically making it mandatory for D to include a .map file or line # debug info in every executable.

I doubt that'll fly, but yeah it'd be handy sometimes.  Who knows?

Sean

"David Leonard" <david.leonard@itee.uq.edu.au> wrote in message news:b6dn1k$cil$1@digitaldaemon.com...
> How about a library module that allows access to the call stack? Help stop feeping creaturism.
>
> import callstack;
>
> void LogStr(char[] str)
> {
>     callstack.Frame f = callstack.getPreviousFrame(2); // caller's caller
>     printf("%s:%d:%s(): %s\n", f.sourceFile, f.lineNo, f.symbol, str);
> }


April 02, 2003
I want context functions to be fast and they *must not* interfere
performance of all other code.
Your approach is surely the way, but it could be very inefficient.

I just want to get fast and useful tool for development.

Nic Tiger.

"David Leonard" <david.leonard@itee.uq.edu.au> ÓÏÏÂÝÉÌ/ÓÏÏÂÝÉÌÁ × ÎÏ×ÏÓÔÑÈ ÓÌÅÄÕÀÝÅÅ: news:b6dn1k$cil$1@digitaldaemon.com...
> How about a library module that allows access to the call stack? Help stop feeping creaturism.
>
> import callstack;
>
> void LogStr(char[] str)
> {
>     callstack.Frame f = callstack.getPreviousFrame(2); // caller's caller
>     printf("%s:%d:%s(): %s\n", f.sourceFile, f.lineNo, f.symbol, str);
> }
>
>


April 02, 2003
The devil is in the details.  Map files and debug info do not slow down the rest of the code, they're just big.  Doing it some other way such as writing special info to the stack frames would slow the whole game down.

How would you implement this library?  There are libraries like this for C++.

Don't we all want that?

Sean

"Nic Tiger" <nictiger@progtech.ru> wrote in message news:b6f530$1g2r$1@digitaldaemon.com...
> I want context functions to be fast and they *must not* interfere
> performance of all other code.
> Your approach is surely the way, but it could be very inefficient.
>
> I just want to get fast and useful tool for development.
>
> Nic Tiger.
>
> "David Leonard" <david.leonard@itee.uq.edu.au> ÓÏÏÂÝÉÌ/ÓÏÏÂÝÉÌÁ × ÎÏ×ÏÓÔÑÈ ÓÌÅÄÕÀÝÅÅ: news:b6dn1k$cil$1@digitaldaemon.com...
> > How about a library module that allows access to the call stack? Help stop feeping creaturism.
> >
> > import callstack;
> >
> > void LogStr(char[] str)
> > {
> >     callstack.Frame f = callstack.getPreviousFrame(2); // caller's
caller
> >     printf("%s:%d:%s(): %s\n", f.sourceFile, f.lineNo, f.symbol, str);
> > }


April 02, 2003
I think the concept is a good one, and addresses a problem I've wrestled with for a while.