Thread overview
[Issue 7897] New: Problem with alias template parameter
Apr 13, 2012
Manu
Apr 20, 2012
Walter Bright
Apr 20, 2012
Walter Bright
Apr 20, 2012
Manu
Apr 20, 2012
Manu
Apr 20, 2012
Walter Bright
Apr 20, 2012
Walter Bright
Apr 21, 2012
Manu
Apr 21, 2012
Walter Bright
Apr 21, 2012
Manu
April 13, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897

           Summary: Problem with alias template parameter
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: normal
          Priority: P2
         Component: DMD
        AssignedTo: nobody@puremagic.com
        ReportedBy: turkeyman@gmail.com


--- Comment #0 from Manu <turkeyman@gmail.com> 2012-04-13 06:35:59 PDT ---
I've boiled this down as much as I can. My use case isn't clearly reflected here (significantly more complex).



import std.traits;

struct ExternFunction
{
    string name;
    void* pFunction;
}


// loose function
extern(C) void function() globalFunction;

// functions contained in a struct
struct Debug
{
    extern(C) void function( const char* format, ... ) outputDebug;
}
__gshared Debug d;


void main()
{
    const(ExternFunction)* pImports = null;

    // register loose function is okay
    HookupEngineFunction!( globalFunction )( pImports );

    // register function in struct (doesn't work, won't take alias for some
reason?)
    HookupEngineFunction!( d.outputDebug )( pImports );

    // register whole struct (fails via internal call to the function above,
but with a different complaint)
    HookupEngineModule!( d )( pImports );
}

private:

// hook up a struct full of callback pointers
void HookupEngineModule( alias s )( const(ExternFunction)* pImports )
{
    // iterate struct members
    foreach( m; __traits( allMembers, typeof( s ) ) )
    {
        // !!! getMember doesn't work here...
        HookupEngineFunction!( __traits( getMember, s, m ) )( pImports );
    }
}


void HookupEngineFunction( alias funcptr )( const(ExternFunction)* pImports )
{
    alias typeof( funcptr ) Type;

    static if( isFunctionPointer!Type )
    {
        const(ExternFunction)* pExtern = pImports; // FindImport(...);

        funcptr = cast(Type)pExtern.pFunction;
    }
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 20, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897


Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla@digitalmars.com


--- Comment #1 from Walter Bright <bugzilla@digitalmars.com> 2012-04-20 11:44:45 PDT ---
======
    // register function in struct (doesn't work, won't take alias for some
reason?)
    HookupEngineFunction!( d.outputDebug )( pImports );
======

This is failing to compile because outputDebug is a non-static member function of struct Debug. This means that a 'this' pointer is required. The parameter 'alias funcptr' to HookupEngineFunction requires an argument that is a symbolic alias. But you're supplying a runtime value, d.outputDebug, that cannot be evaluated at compile time.

In other words, d.outputDebug would be a delegate, and delegates can only be constructed at runtime because they consist of a pair: a 'this' pointer and the address of the function. The 'this' pointer cannot be determined at compile time.

If you make Debug.outputDebug a static member function, it will compile.

This is not a compiler bug.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 20, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897


Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |INVALID


--- Comment #2 from Walter Bright <bugzilla@digitalmars.com> 2012-04-20 11:52:56 PDT ---
========
 // !!! getMember doesn't work here...
 HookupEngineFunction!( __traits( getMember, s, m ) )( pImports );
========

This is a similar issue. s here is a variable, and getMember is expecting a type. Rewriting as:

 HookupEngineFunction!( __traits( getMember, typeof(s), m ) )( pImports );

gets past that. But then we're back to the previous issue, no 'this' pointer at compile time.

This is not a compiler bug, either.

I suspect what you are doing is trying to do C++ "pointers to members". Perhaps this will help:

http://www.drdobbs.com/blogs/cpp/231600610

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 20, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897



--- Comment #3 from Manu <turkeyman@gmail.com> 2012-04-20 12:50:03 PDT ---
Actually no, I'm not trying to do pointer to member type stuff.

As I saw it, this is just a single function pointer (surely that is strictly defined by the 'function' keyword?) in a struct.

I'm addressing a static (well, __gshared) instance, 'd'.
Shouldn't d be a singular instance, allocated in the data block, and thereby
accessible at compile time? I figure this should effectively be no different
than a __gshared void*.

Have I misunderstood something rather fundamental about __gshared? This should
make it effectively identical to a C global right? Ie, singular instance,
allocated in the data block, and address known at compile time.
Under that assumption, there's no technical reason the compiler shouldn't be
able to alias that variable at compile time and generate appropriate code.

If I'm mistaken about that, how do I produce a variable effectively identical to a C global?

Like I say, this is a very boiled down case, and infact, this particular case is no longer directly relevant in my code, but the same scenario is coming up frequently. That is, I have a global instance of a structure, which I expect should be accessible at compile time...

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 20, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897


Manu <turkeyman@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |


--- Comment #4 from Manu <turkeyman@gmail.com> 2012-04-20 16:14:55 PDT ---
Here, I've simplified it some more. This is functionally identical to the first example, I just rephrased it to 'int' to remove any confusion about function pointers and intent.


// member contained in a struct
struct Thing
{
    int x;
}

Thing thing; // this is effectively identical to declaring 'int x;' globally, it's just wrapped in a thin struct

int x; // i'll also do it directly, to prove it works.

void main()
{
    // these 3 statements should be effectively identical
    thing.x = 10;               // this works, obviously
    AliasTheStruct!( thing )(); // this works
    AliasTheInt!( thing.x )();  // this is the problem

    AliasTheInt!( x )();        // of course, this works fine
}

void AliasTheStruct( alias a )()
{
    a.x = 10;
}

void AliasTheInt( alias a )()
{
    a = 10;
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 20, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897



--- Comment #5 from Walter Bright <bugzilla@digitalmars.com> 2012-04-20 16:43:49 PDT ---
(In reply to comment #3)
> As I saw it, this is just a single function pointer (surely that is strictly defined by the 'function' keyword?) in a struct.

It isn't a function pointer. Non-static member methods are never a simple function pointer, they always require a 'this' pointer as well, as they are delegates. This is not like C++ member function pointers.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 20, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897


Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |RESOLVED
         Resolution|                            |INVALID


--- Comment #6 from Walter Bright <bugzilla@digitalmars.com> 2012-04-20 16:49:40 PDT ---
> AliasTheInt!( thing.x )();  // this is the problem

Right. It's a problem because thing.x is being passed as an *alias* parameter. An alias must be symbol, not a computation on a symbol. thing.x is semantically equivalent to:

   *(&thing + x.offsetof)

which is not a symbol.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 21, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897



--- Comment #7 from Manu <turkeyman@gmail.com> 2012-04-20 17:16:54 PDT ---
(In reply to comment #5)
> (In reply to comment #3)
> > As I saw it, this is just a single function pointer (surely that is strictly defined by the 'function' keyword?) in a struct.
> 
> It isn't a function pointer. Non-static member methods are never a simple function pointer, they always require a 'this' pointer as well, as they are delegates. This is not like C++ member function pointers.

extern(C) void function( const char* format, ... ) outputDebug; <- this is a function pointer. It's not a method, or a delegate. I have no idea why you're introducing methods and delegates into the conversation. But it doesn't matter, it's irrelevant to the problem here...


> > AliasTheInt!( thing.x )();  // this is the problem
> 
> Right. It's a problem because thing.x is being passed as an *alias* parameter. An alias must be symbol, not a computation on a symbol. thing.x is semantically equivalent to:
> 
>    *(&thing + x.offsetof)
> 
> which is not a symbol.

Oooohkay, so now we get to the bottom of it.
Right, well I didn't realise that restriction. I just assumed any statically
addressable variable was alias-able.

So this is a feature request then, not a bug.
There's still technically no reason it shouldn't work, it's just not quite how
alias is written?
How much trouble would it be to extend 'alias' to include an offset value?

This restriction makes it very difficult to drill down into structures via recursive enumeration. The workaround I've had to use is to use strings instead of aliases that can mixin an absolute reference, but it's really untidy, and again, messes with the editor a lot (syntax hilighting, auto-complete, etc). It also requires mixin(a) everywhere, and there are lots of cases where mixin() is invalid in the middle of an expression. This requires a further workaround to build the whole expression into a string and mix the whole thing in.

I wonder if you could use lowering here to produce the same result via lowered mixin? Although it sounds much cleaner to simply add an offset to aliases.

Another alternative could be to just invent the appropriate symbol when a reference like this appears. Symbol names follow the d scoping patterns right? In this case, the symbol would just be my.module.thing.x (or with whatever mangling happens to appear in D symbols).

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 21, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897



--- Comment #8 from Walter Bright <bugzilla@digitalmars.com> 2012-04-20 18:00:42 PDT ---
(In reply to comment #7)
> Oooohkay, so now we get to the bottom of it.
> Right, well I didn't realise that restriction. I just assumed any statically
> addressable variable was alias-able.

Any *symbol* is aliasable. It isn't about addresses.


> So this is a feature request then, not a bug.
> There's still technically no reason it shouldn't work, it's just not quite how
> alias is written?
> How much trouble would it be to extend 'alias' to include an offset value?

This is a significant redesign of what alias is.


> This restriction makes it very difficult to drill down into structures via recursive enumeration. The workaround I've had to use is to use strings instead of aliases that can mixin an absolute reference, but it's really untidy, and again, messes with the editor a lot (syntax hilighting, auto-complete, etc). It also requires mixin(a) everywhere, and there are lots of cases where mixin() is invalid in the middle of an expression. This requires a further workaround to build the whole expression into a string and mix the whole thing in.
> 
> I wonder if you could use lowering here to produce the same result via lowered mixin? Although it sounds much cleaner to simply add an offset to aliases.

It isn't simple, because having an offset makes it an expression, not a symbol. It's a substantial redesign.


> Another alternative could be to just invent the appropriate symbol when a reference like this appears. Symbol names follow the d scoping patterns right? In this case, the symbol would just be my.module.thing.x (or with whatever mangling happens to appear in D symbols).

Can't you just pass a pointer around?

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
April 21, 2012
http://d.puremagic.com/issues/show_bug.cgi?id=7897



--- Comment #9 from Manu <turkeyman@gmail.com> 2012-04-21 05:08:49 PDT ---
(In reply to comment #8)
> (In reply to comment #7)
> > Oooohkay, so now we get to the bottom of it.
> > Right, well I didn't realise that restriction. I just assumed any statically
> > addressable variable was alias-able.
> 
> Any *symbol* is aliasable. It isn't about addresses.

Yeah I see now, but I don't think that's what people intuitively expect.
I've asked for help on various manifestations of this matter on IRC quite a few
times, and so far, zero out of all the people who have helped me seem to
understand that either. They almost all just said something like "yeah it seems
to be a bit finicky, have you tried this, try passing a this kind of thing, try
changing it this way", etc .. suggesting that nobody had any understanding of
what they were trying to do, just fiddling with it until it stopped
complaining.

Perhaps it would be a good idea to add some informative error message stating the limitation. The whinge about 'this' certainly lost all meaning to me at least, I was absolutely convinced 'this' was perfectly calculable at compile time, and it should work. There was no mention of symbols.


> > So this is a feature request then, not a bug.
> > There's still technically no reason it shouldn't work, it's just not quite how
> > alias is written?
> > How much trouble would it be to extend 'alias' to include an offset value?
> 
> This is a significant redesign of what alias is.

:/ .. I think though that the mistake I made is what most people expect should be possible. At least all those that have tried to help me with it so far.


> > Another alternative could be to just invent the appropriate symbol when a reference like this appears. Symbol names follow the d scoping patterns right? In this case, the symbol would just be my.module.thing.x (or with whatever mangling happens to appear in D symbols).
> 
> Can't you just pass a pointer around?

I don't think I can get a pointer to TLS data at compile time can I? Using the alias still allows the compiler to generate the correct TLS dereferencing code.

In the cases where I use __gshared I suppose I could, but I'm not sure this
would simplify the code at all. I can imagine it being more difficult to follow
than the mixin version.
Carrying a pointer interferes with typeof(), it also interferes with the
ability to do allMemers, getMember on the instance (as opposed to the type).
getMember only works on aliases(?), so I think I would need to generate mixin
code that uses the strings produced by allMembers to generate expressions that
reference the data I'm interested in anyway.

I'll have a go at it, see how far I get. But I can imagine the result being very similar to the mixin approach, and probably no simpler for a 3rd person to follow. A direct alias is certainly be the most clear and concise way to go.

Is it impossible to invent a new symbol when this case is encountered like I suggest? That could be a simple solution that fits the existing model.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------