View mode: basic / threaded / horizontal-split · Log in · Help
July 24, 2004
Re: Three notable problems with method name resolution
On Sat, 24 Jul 2004 15:32:51 +1000, Derek wrote:

> I must be using a different version of D, because the three issues you
> mention are not a problem in v0.95.
> 
> The first one, referring to extern C functions do not produce any error
> messages. In fact, it compiles and runs just fine.

I'll back Kris up on this one.  It is most definitely an issue.  The
example may compile fine as is, and that's part of the problem! Nobody
sees the error until much later. Just go try to call a FileConduit
instance's close() or read( char[] dst ) from another module, class, main,
or whatever. You should quickly find that these methods no longer exist
due to the miraculous effects of privately importing (internal to the
class) std.c.linux.linux "close" and "read." These imported functions
completely hide the classes close and read methods with no hope of
recovery. DMD doesn't even attempt to overload these methods. Instead it
rudely ends with a compile error message grumbling confusingly about
incorrect arguments. The hapless programmer, not realizing what the import
is doing, looks back in the code and is thoroughly confused at trying to
figure out why his method calls are not valid even though they match the
signatures in the class perfectly.  This problem is definitely real.
July 24, 2004
Re: Three notable problems with method name resolution
On Sat, 24 Jul 2004 00:41:15 -0700, John Reimer wrote:

> On Sat, 24 Jul 2004 15:32:51 +1000, Derek wrote:
> 
>> I must be using a different version of D, because the three issues you
>> mention are not a problem in v0.95.
>> 
>> The first one, referring to extern C functions do not produce any error
>> messages. In fact, it compiles and runs just fine.
> 
> I'll back Kris up on this one.  It is most definitely an issue.  The
> example may compile fine as is, and that's part of the problem! Nobody
> sees the error until much later. Just go try to call a FileConduit
> instance's close() or read( char[] dst ) from another module, class, main,
> or whatever. You should quickly find that these methods no longer exist
> due to the miraculous effects of privately importing (internal to the
> class) std.c.linux.linux "close" and "read." These imported functions
> completely hide the classes close and read methods with no hope of
> recovery. DMD doesn't even attempt to overload these methods. Instead it
> rudely ends with a compile error message grumbling confusingly about
> incorrect arguments. The hapless programmer, not realizing what the import
> is doing, looks back in the code and is thoroughly confused at trying to
> figure out why his method calls are not valid even though they match the
> signatures in the class perfectly.  This problem is definitely real.

Before I wrote my original response to this thread, I wrote some test
programs to try and understand what the issues were. I based my response on
those test programs. As I've now had a number of people come back and tell
me I'm dead wrong (and possibly a bit simple too), I guess I really do not
understand the issues.

Anyhow, here are the three files I made to test the "extern C/private
import" issue.

<file: test3.d>
extern (C)
{
int read (int, void*, int);
int close (int);
}
<end of file>

<file: test4.d>
class Conduit
{
   abstract void close ();
   abstract void read (char[] dst);
}
class FileConduit : Conduit
{
   private import test3;
   private int handle;
   void close ()
   {
      printf("FileConduit Close\n");
      test3.close (handle);
   }
   void read (char[] dst)
   {
       test3.read (handle, dst, dst.length);
   }
}
<end of file>

<file: test2.d>
import test4;
void main()
{
   FileConduit f = new FileConduit;
   
   f.close();
}
<end of file>

<compile and run results>
G:\temp>dmd test2 test4 test3
F:\DMD\BIN\..\..\dm\bin\link.exe test2+test4+test3,,,user32+kernel32/noi;

G:\temp>test2
FileConduit Close

G:\temp>

<end of run>

What haven't I understood?

-- 
Derek
Melbourne, Australia
July 24, 2004
Re: Three notable problems with method name resolution
"Derek" <derek@psyc.ward> wrote in message
news:h0kf8z2x9zvu.tyujz7z737ln$.dlg@40tude.net...
> The second, using interfaces, is also correctly working here. By correct,
I
> mean that it fails in exactly the way you say it does, because that's what
> I was expecting it to do - fail.
>
> My understanding of an interface is that it is JUST a list of cuntions
that
> must be implemented in the class that you specified with the interface
> name.
>
> Here is the code in your example:
>
> <code>
>  interface IFoo
>  {
>   void foo ();
>  }
>
>  interface IBar
>  {
>   void bar ();
>  }
>
>  class Foo : IFoo
>  {
>   void foo ()
>   {
>   }
>  }
>
>  class Bar : Foo, IFoo, IBar
>  {
>   void bar()
>   {
>   }
>  }
> </code>
>
> The problem is with class Bar. Here you explictly say that it is derived
> from class Foo, and must implement interface IFoo and IBar. However, you
> have only got it implementing bar(). The foo() is implemented in Foo but
> not in Bar. You are obviously expecting implemented interfaces to be
> inherited. Now although the foo() function IS inherited by Bar, you have
> also said it must be *implemented* in Bar too, but you didn't implement
it.

You're correct, it is deliberately designed to work this way. To get it to
work the other way, just rewrite Bar as:

   class Bar : Foo, IBar
July 24, 2004
The Alias peek-a-boo Game
You describe it as "The latter is utter nonsense, and smacks of either an
implementation-specific hack or a half-baked and headless C-style
implementation of method hiding." However, consider the following C++
program:

--------------------------------------
C:\cbx>type foo.cpp

#include <stdio.h>

struct X { };

struct A
{
       int foo(void *p) { return 1; }
       int foo(struct X x) { return 2; }
};

struct B : A
{
//      using A::foo;
       int foo(int i) { return 3; }
};

void main()
{
   B b;
   X x;
   int i;

   i = b.foo((void*)0);
   printf("i = %d\n", i);
   i = b.foo(x);
   printf("i = %d\n", i);
   i = b.foo(1);
   printf("i = %d\n", i);
}

C:\cbx>sc foo
   i = b.foo((void*)0);
                     ^
foo.cpp(24) : Error: need explicit cast for function parameter 1 to get
from: void *
to  : int
   i = b.foo(x);
              ^
foo.cpp(26) : Error: need explicit cast for function parameter 1 to get
from: X
to  : int
--- errorlevel 1

C:\cbx>
-------------------------------------------
Now uncomment the 'using' declaration, and the program will compile
successfully and produce the 1, 2, 3 output. Therefore, D uses
overloading/hiding rules that are completely analogous to C++. I've read a
lot of critiques of C++, and this has not been mentioned. Or perhaps my C++
compiler has an egregious fault in it, but I find it hard to believe it
would have lasted this long with such a fundamental flaw <g>.
July 24, 2004
Re: Three notable problems with method name resolution
"some idiot" <fu@bar.org> wrote in message
news:cdsd5t$u5$1@digitaldaemon.com...
> Attached is a small pdf noting three outstanding issues ~ each of which
has
> various bug reports stretching back into the old NG. Given that the v1.0
> release is getting closer, it seemed appropriate to try getting some
> priority and/or traction.
>
> Such things might simply fall by the wayside otherwise.
>

External Names:
I can't seem to recreate that problem, but I have some thoughts on it. I
think importing in a class is like importing in a module. If there is a
symbol collision, you have to specify from which scope you want the symbol.
What comes to mind is this, which does not work:
  FileConduit fc = new FileConduit;
  fc.FileConduit.read("foo");
Although, I think if you don't have access (it's private, etc) to something
it should not cause a collision. I've been meaning to make a post on that.
A working solution is to wrap your imports in a struct or something, and
instead of std.linux.linux.read(), simply use mystruct.read().

Satisfying Interface Contracts:
From class.html#interface it says "A reimplemented interface must implement
all the interface functions, it does not inherit them from a super class." I
don't see why you're specifying to (re)implement IFoo without reimplementing
it. You can't specify a base class twice, why are you doing it for an
interface? It just so happens that specifying to implement an interface has
an added feature for reimplementing. In fact, when I tried it with a class,
the compiler crashed!

The Alias peek-a-boo Game:
I remember reading that it helps prevent the possibility of accidentally
calling the wrong function. Actually, I think there was a big long thread on
it, and I think people said they'd rather opt-into this danger using an
alias. However, there is a problem when trying to both override and alias
overload; I've been meaning to bug post it (wow I'm lazy ;).
July 24, 2004
External Names
Let me explain what is happening here.

In FileConduit, you have an import declaration, which brings a bunch of
names into FileConduit's scope. How name lookup happens in a scope is a
multi-stage process, each proceeding to the next if the name is not found in
the current stage:

   1) look for a member with the name
   2) look in any imports *in this scope*
   3) if current scope is a class scope, recursively look in base classes
   4) repeat the process with the most enclosing scope

I think it would be very strange to have base class symbols override imports
in the current scope, i.e. if imports were looked up in some other scope
than the one in which they were imported.

The solution is straightforward. Move the import declaration outside of the
class. Then it won't override base class names. There's no need whatsoever
to refactor class heirarchies. By where you place the import declaration,
you specify when the lookups for the names in the import happen.
July 24, 2004
Re: Three notable problems with method name resolution
> Before I wrote my original response to this thread, I wrote some test
> programs to try and understand what the issues were. I based my response
> on those test programs. As I've now had a number of people come back and
> tell me I'm dead wrong (and possibly a bit simple too), I guess I really
> do not understand the issues.

Ha! nobody would dare hint that you are simple.  That's a good one :-). 
Your point of view is appreciated. You obviously got some tough grit to
wrangle on these issues against multiple opposing opinions.  

In fact you've generated a good example that I'll have to think about. One
problem is that the situation in mango still generates the compiler error
that I'm referring too despite your example's apparent revelation of the
problem's non-existence. So somehow, the slightly more complicated example
does what yours is not doing.  Walter seems to have already described why
this issue even comes up.  So I assume it's existence is formally accepted
already (but the argument is that there is a good reason for it's
existence, such that it's not an important issue).

As for your example, I'll have to figure what's going on differently
between mango and it.

Many pardons if I made out that this is an obvious case.  Obviously it's
not as obvious as I obviously thought! ;-)

> Anyhow, here are the three files I made to test the "extern
C/private
> import" issue.
> 
> <file: test3.d>
> extern (C)
> {
> int read (int, void*, int);
> int close (int);
> }
> <end of file>
> 
> <file: test4.d>
> class Conduit
> {
>     abstract void close ();
>     abstract void read (char[] dst);
> }
> class FileConduit : Conduit
> {
>     private import test3;
>     private int handle;
>     void close ()
>     {
>        printf("FileConduit Close\n");
>        test3.close (handle);
>     }
>     void read (char[] dst)
>     {
>         test3.read (handle, dst, dst.length);
>     }
>     }
> <end of file>
> 
> <file: test2.d>
> import test4;
> void main()
> {
>     FileConduit f = new FileConduit;
>     
>     f.close();
> }
> <end of file>
> 
> <compile and run results>
> G:\temp>dmd test2 test4 test3
> F:\DMD\BIN\..\..\dm\bin\link.exe
> test2+test4+test3,,,user32+kernel32/noi;
> 
> G:\temp>test2
> FileConduit Close
> 
> G:\temp>
> 
> <end of run>
> 
> What haven't I understood?
July 24, 2004
Re: The Alias peek-a-boo Game
In article <cdt9m6$puh$1@digitaldaemon.com>, Walter says...
>
> Therefore, D uses
>overloading/hiding rules that are completely analogous to C++. I've read a
>lot of critiques of C++, and this has not been mentioned. Or perhaps my C++
>compiler has an egregious fault in it, but I find it hard to believe it
>would have lasted this long with such a fundamental flaw <g>.

Agreed.  The resolution rules might be complex but I think they make perfect
sense once understood.  And I personally find the consistency with C++ to be a
good thing.

Sean
July 24, 2004
Re: The Alias peek-a-boo Game
In article <cdt9m6$puh$1@digitaldaemon.com>,
"Walter" <newshound@digitalmars.com> wrote:

> You describe it as "The latter is utter nonsense, and smacks of either an
> implementation-specific hack or a half-baked and headless C-style
> implementation of method hiding." However, consider the following C++
> program:
> 
> --------------------------------------
> C:\cbx>type foo.cpp
> 
> #include <stdio.h>
> 
> struct X { };
> 
> struct A
> {
>         int foo(void *p) { return 1; }
>         int foo(struct X x) { return 2; }
> };
> 
> struct B : A
> {
> //      using A::foo;
>         int foo(int i) { return 3; }
> };
> 
> void main()
> {
>     B b;
>     X x;
>     int i;
> 
>     i = b.foo((void*)0);
>     printf("i = %d\n", i);
>     i = b.foo(x);
>     printf("i = %d\n", i);
>     i = b.foo(1);
>     printf("i = %d\n", i);
> }
> 
> C:\cbx>sc foo
>     i = b.foo((void*)0);
>                       ^
> foo.cpp(24) : Error: need explicit cast for function parameter 1 to get
> from: void *
> to  : int
>     i = b.foo(x);
>                ^
> foo.cpp(26) : Error: need explicit cast for function parameter 1 to get
> from: X
> to  : int
> --- errorlevel 1
> 
> C:\cbx>
> -------------------------------------------
> Now uncomment the 'using' declaration, and the program will compile
> successfully and produce the 1, 2, 3 output. Therefore, D uses
> overloading/hiding rules that are completely analogous to C++. I've read a
> lot of critiques of C++, and this has not been mentioned. Or perhaps my C++
> compiler has an egregious fault in it, but I find it hard to believe it
> would have lasted this long with such a fundamental flaw <g>.


I think his point has some validity to it.  Importing should have a 
method by which to NOT implicitly be included in the current namespace 
as well as the module namespace.   

At least by my understanding of D imports he cannot do: 

std.c.linux.linux.close(...) without first importing. 

And by importing, he automatically gets close() in the current 
namespace. 

If there is already a way to "include" the module without "importing" 
the namespace then his point is moot.  Otherwise I think there's 
something to this.
July 24, 2004
Re: The Alias peek-a-boo Game
In article <schancel-23ED78.09265224072004@digitalmars.com>,
Sha Chancellor <schancel@pacific.net> wrote:

> I think his point has some validity to it.  Importing should have a 
> method by which to NOT implicitly be included in the current namespace 
> as well as the module namespace.   
> 
> At least by my understanding of D imports he cannot do: 
> 
> std.c.linux.linux.close(...) without first importing. 
> 
> And by importing, he automatically gets close() in the current 
> namespace. 
> 
> If there is already a way to "include" the module without "importing" 
> the namespace then his point is moot.  Otherwise I think there's 
> something to this.

Pardon my stupidity, I meant to respond to external name resolution.
1 2 3 4 5 6
Top | Discussion index | About this forum | D home