Thread overview
How to overload member function pointer and a regualr member function
Apr 24, 2017
ParticlePeter
Apr 25, 2017
Basile B.
Apr 25, 2017
ParticlePeter
Apr 25, 2017
Basile B.
Apr 25, 2017
ParticlePeter
Apr 25, 2017
Ali Çehreli
Apr 25, 2017
Ali Çehreli
Apr 26, 2017
Basile B.
Apr 27, 2017
ParticlePeter
April 24, 2017
I would like to have this kind of struct:

struct Foo {
  private int i;
  void function( int i, float f ) bar;  // will be defined at runtime
  void bar( float f ) {
    bar( i, f );
  }
}

But apparently the function pointer and the member function cannot have the same name: Error: function main.Foo.bar conflicts with variable main.Foo.bar ...

I tried with an inner struct:
struct Foo {
  private int i;
  void function( int i, float f ) bar;  // will be defined at runtime
  private struct Inner {
    void bar( float f ) {
      bar( i, f );
    }
  }
  Inner inner;
}

But this time I get following error:
Error: need 'this' for 'i' of type 'int'

What does this message tell me? Should the inner struct not be able to access Foo.i?

How else can I get the required behavior?

I would prefer to avoid another indirection like this:
struct Foo {
  private int i;
  void function( int i, float f ) bar;  // will be defined at runtime
  void baz( float f ) {
    bar( i, f );
  }
  void baz( int ii, float f ) {
    bar( ii, f );
  }
}

April 25, 2017
On Monday, 24 April 2017 at 16:46:21 UTC, ParticlePeter wrote:
> I would like to have this kind of struct:
>
> struct Foo {
>   private int i;
>   void function( int i, float f ) bar;  // will be defined at runtime
>   void bar( float f ) {
>     bar( i, f );
>   }
> }
>
> [...]
> How else can I get the required behavior?

Like this:

struct Foo1
{
    private void function(int,float) _bar;
    void bar(float){}
    void bar(int i, float f){_bar(i,f);}
}

Or like this:

struct Foo2
{
    private void function(int,float) _bar;
    void bar(float) {}
    void function(int,float) bar() {return _bar;}
}


First solution looks better:

(new Foo2).bar()(0,0f) // less good
(new Foo1).bar(0,0f) // better


April 25, 2017
On Tuesday, 25 April 2017 at 09:50:14 UTC, Basile B. wrote:
> On Monday, 24 April 2017 at 16:46:21 UTC, ParticlePeter wrote:
>> I would like to have this kind of struct:
>>
>> struct Foo {
>>   private int i;
>>   void function( int i, float f ) bar;  // will be defined at runtime
>>   void bar( float f ) {
>>     bar( i, f );
>>   }
>> }
>>
>> [...]
>> How else can I get the required behavior?
>
> Like this:
>
> struct Foo1
> {
>     private void function(int,float) _bar;
>     void bar(float){}
>     void bar(int i, float f){_bar(i,f);}
> }

Thanks for your reply, but that's what I would like to avoid, the additional indirection to call the function pointer with the original argument count.
Do you have any idea about the likelihood of the compiler removing this indirection as an optimizations?
April 25, 2017
On Tuesday, 25 April 2017 at 15:43:48 UTC, ParticlePeter wrote:
> On Tuesday, 25 April 2017 at 09:50:14 UTC, Basile B. wrote:
>> On Monday, 24 April 2017 at 16:46:21 UTC, ParticlePeter wrote:
>
> Thanks for your reply, but that's what I would like to avoid, the additional indirection to call the function pointer with the original argument count.

Oops, i can believe i didn't read the last part of your question.

> Do you have any idea about the likelihood of the compiler removing this indirection as an optimizations?

with pragma(inline, true), the function body should be injected at the call sites.



April 25, 2017
On Tuesday, 25 April 2017 at 16:27:43 UTC, Basile B. wrote:
> On Tuesday, 25 April 2017 at 15:43:48 UTC, ParticlePeter wrote:
>> On Tuesday, 25 April 2017 at 09:50:14 UTC, Basile B. wrote:
>>> On Monday, 24 April 2017 at 16:46:21 UTC, ParticlePeter wrote:
>>
>> Thanks for your reply, but that's what I would like to avoid, the additional indirection to call the function pointer with the original argument count.
>
> Oops, i can believe i didn't read the last part of your question.
>
>> Do you have any idea about the likelihood of the compiler removing this indirection as an optimizations?
>
> with pragma(inline, true), the function body should be injected at the call sites.

This would not help I fear, the body of the function pointer is unknown in an external lib. I rather hoped that the compiler "sees" the parameter forwarding to the fp and is able to directly call it. Best thing would be for both overloads, but I would not know how to verify this.
April 25, 2017
On 04/25/2017 11:28 AM, ParticlePeter wrote:
> On Tuesday, 25 April 2017 at 16:27:43 UTC, Basile B. wrote:

>> with pragma(inline, true), the function body should be injected at the
>> call sites.
>
> This would not help I fear, the body of the function pointer is unknown
> in an external lib. I rather hoped that the compiler "sees" the
> parameter forwarding to the fp and is able to directly call it. Best
> thing would be for both overloads, but I would not know how to verify this.

pragma(inline, true) works because all you need inlined in this case is the body of bar(int, float). The compiler does call the function pointer directly.

import std.stdio;

struct Foo1
{
    private void function(int,float) _bar;
    void bar(float f) {
        pragma(inline, true);
        writefln("Called with %s", f);
    }
    void bar(int i, float f) {
        pragma(inline, true);
        _bar(i,f);
    }
}

void func(int i, float f) {
    writefln("Called with %s and %s", i, f);
}

void main() {
    auto f = Foo1(&func);
    f.bar(1.5);
    f.bar(42, 2.5);
}

Compile with -inline (and perhaps with -O):

  dmd -inline deneme.d

Generate the disassembly with obj2asm that comes with dmd (or with any other disassembly tool):

  obj2asm deneme.o > deneme.asm

You can open deneme.asm in an editor and search for function "_Dmain:" in it. Here is what my dmd 2.074 produced:

_Dmain:
		push	RBP
		mov	RBP,RSP
		sub	RSP,010h
		mov	RAX,_D6deneme4funcFifZv@GOTPCREL[RIP]
		mov	-010h[RBP],RAX
		movss	XMM0,FLAT:.rodata[00h][RIP]
		movss	-8[RBP],XMM0
		lea	RDX,_TMP0@PC32[RIP]
		mov	EDI,0Eh
		mov	RSI,RDX
		movss	XMM0,-8[RBP]
		call	  _D3std5stdio17__T8writeflnTaTfZ8writeflnFNfxAafZv@PLT32
		mov	EAX,02Ah
		movss	XMM1,FLAT:.rodata[00h][RIP]
		movss	-4[RBP],XMM1
		mov	RDI,RAX
		movss	XMM0,-4[RBP]
		call	qword ptr -010h[RBP]
		xor	EAX,EAX
		leave
		ret
		add	[RAX],AL
.text._Dmain	ends

The call to jumbled writefln() is a direct call inside func():

		call	  _D3std5stdio17__T8writeflnTaTfZ8writeflnFNfxAafZv@PLT32

So, you're good... :)

Ali

April 25, 2017
On 04/25/2017 11:54 AM, Ali Çehreli wrote:

> _Dmain:
>         push    RBP
>         mov    RBP,RSP
>         sub    RSP,010h
>         mov    RAX,_D6deneme4funcFifZv@GOTPCREL[RIP]
>         mov    -010h[RBP],RAX
>         movss    XMM0,FLAT:.rodata[00h][RIP]
>         movss    -8[RBP],XMM0
>         lea    RDX,_TMP0@PC32[RIP]
>         mov    EDI,0Eh
>         mov    RSI,RDX
>         movss    XMM0,-8[RBP]
>         call      _D3std5stdio17__T8writeflnTaTfZ8writeflnFNfxAafZv@PLT32
>         mov    EAX,02Ah
>         movss    XMM1,FLAT:.rodata[00h][RIP]
>         movss    -4[RBP],XMM1
>         mov    RDI,RAX
>         movss    XMM0,-4[RBP]
>         call    qword ptr -010h[RBP]
>         xor    EAX,EAX
>         leave
>         ret
>         add    [RAX],AL
> .text._Dmain    ends
>
> The call to jumbled writefln() is a direct call inside func():
>
>         call      _D3std5stdio17__T8writeflnTaTfZ8writeflnFNfxAafZv@PLT32

My analysis is wrong because that writefln() is for the bar(float) overload but I still think what you want is achieved.

Ali

April 26, 2017
On Tuesday, 25 April 2017 at 18:58:58 UTC, Ali Çehreli wrote:
> On 04/25/2017 11:54 AM, Ali Çehreli wrote:
> My analysis is wrong because that writefln() is for the bar(float) overload but I still think what you want is achieved.
>
> Ali

No it's ok, it works. The additional indirection is well avoided:

Let's take this module:

==================
#!dmd -release -inline -O
module runnable;

struct Foo
{
    private void function(int,float) _bar;
    void bar(float){}
    pragma(inline, false) void bar(int i, float f){_bar(i,f);}
}

struct FooInline
{
    private void function(int,float) _bar;
    void bar(float){}
    pragma(inline, true) void bar(int i, float f){_bar(i,f);}
}

void testInlined(ref FooInline foo)
{
    foo.bar(0,0);
}

void test(ref Foo foo)
{
    foo.bar(0,0);
}

void main()
{
    import disassembler, std.stdio;
    disassembler.symbolTable.addModule!runnable;
    prettyDisasm(&testInlined).writeln;
    prettyDisasm(&test, 2).writeln; // dig up to 2 levels, required for the indir.
}
==================

and looks at the output:


;------- SUB 0000000000459970h -------
; NAMED: testInlined
0000000000459970h  push rbp
0000000000459971h  mov rbp, rsp
0000000000459974h  sub rsp, 20h
0000000000459978h  mov qword ptr [rbp-08h], rdi
000000000045997Ch  xor edi, edi
000000000045997Eh  mov dword ptr [rbp-20h], edi
0000000000459981h  movss xmm0, dword ptr [rbp-20h]
0000000000459986h  mov rax, qword ptr [rbp-08h]
000000000045998Ah  call qword ptr [rax]
000000000045998Dh  mov rsp, rbp
0000000000459990h  pop rbp
0000000000459991h  ret
;-------------------------------------


;------- SUB 0000000000459934h -------
; XREFS: [00000000004599A6h]
0000000000459934h  push rbp
0000000000459935h  mov rbp, rsp
0000000000459938h  sub rsp, 10h
000000000045993Ch  mov qword ptr [rbp-08h], rdi
0000000000459940h  mov rdi, rsi
0000000000459943h  mov rax, qword ptr [rbp-08h]
0000000000459947h  call qword ptr [rax]
000000000045994Ah  mov rsp, rbp
000000000045994Dh  pop rbp
000000000045994Eh  ret
;-------------------------------------

;------- SUB 0000000000459994h -------
; NAMED: test
0000000000459994h  push rbp
0000000000459995h  mov rbp, rsp
0000000000459998h  sub rsp, 10h
000000000045999Ch  xor esi, esi
000000000045999Eh  mov dword ptr [rbp-10h], esi
00000000004599A1h  movss xmm0, dword ptr [rbp-10h]
00000000004599A6h  call 0000000000459934h
00000000004599ABh  mov rsp, rbp
00000000004599AEh  pop rbp
00000000004599AFh  ret
;-------------------------------------

 - testInlined() contains only the delegate call. (call qword ptr [rax])
 - test() contains a call (call 0000000000459934h) which contains the
   delegate call (call qword ptr [rax])

Actually i've even had to add (pragma inline false) to show the difference since DMD inlined automatically bar() in test().




April 27, 2017
On Wednesday, 26 April 2017 at 08:24:08 UTC, Basile B. wrote:
> On Tuesday, 25 April 2017 at 18:58:58 UTC, Ali Çehreli wrote:
>> On 04/25/2017 11:54 AM, Ali Çehreli wrote:
>> My analysis is wrong because that writefln() is for the bar(float) overload but I still think what you want is achieved.
>>
>> Ali
>
> No it's ok, it works. The additional indirection is well avoided:
>
> Let's take this module:
>
> ==================
> #!dmd -release -inline -O
> module runnable;
>
> struct Foo
> {
>     private void function(int,float) _bar;
>     void bar(float){}
>     pragma(inline, false) void bar(int i, float f){_bar(i,f);}
> }
>
> struct FooInline
> {
>     private void function(int,float) _bar;
>     void bar(float){}
>     pragma(inline, true) void bar(int i, float f){_bar(i,f);}
> }
>
> void testInlined(ref FooInline foo)
> {
>     foo.bar(0,0);
> }
>
> void test(ref Foo foo)
> {
>     foo.bar(0,0);
> }
>
> void main()
> {
>     import disassembler, std.stdio;
>     disassembler.symbolTable.addModule!runnable;
>     prettyDisasm(&testInlined).writeln;
>     prettyDisasm(&test, 2).writeln; // dig up to 2 levels, required for the indir.
> }
> ==================
>
> and looks at the output:
>
>
> ;------- SUB 0000000000459970h -------
> ; NAMED: testInlined
> 0000000000459970h  push rbp
> 0000000000459971h  mov rbp, rsp
> 0000000000459974h  sub rsp, 20h
> 0000000000459978h  mov qword ptr [rbp-08h], rdi
> 000000000045997Ch  xor edi, edi
> 000000000045997Eh  mov dword ptr [rbp-20h], edi
> 0000000000459981h  movss xmm0, dword ptr [rbp-20h]
> 0000000000459986h  mov rax, qword ptr [rbp-08h]
> 000000000045998Ah  call qword ptr [rax]
> 000000000045998Dh  mov rsp, rbp
> 0000000000459990h  pop rbp
> 0000000000459991h  ret
> ;-------------------------------------
>
>
> ;------- SUB 0000000000459934h -------
> ; XREFS: [00000000004599A6h]
> 0000000000459934h  push rbp
> 0000000000459935h  mov rbp, rsp
> 0000000000459938h  sub rsp, 10h
> 000000000045993Ch  mov qword ptr [rbp-08h], rdi
> 0000000000459940h  mov rdi, rsi
> 0000000000459943h  mov rax, qword ptr [rbp-08h]
> 0000000000459947h  call qword ptr [rax]
> 000000000045994Ah  mov rsp, rbp
> 000000000045994Dh  pop rbp
> 000000000045994Eh  ret
> ;-------------------------------------
>
> ;------- SUB 0000000000459994h -------
> ; NAMED: test
> 0000000000459994h  push rbp
> 0000000000459995h  mov rbp, rsp
> 0000000000459998h  sub rsp, 10h
> 000000000045999Ch  xor esi, esi
> 000000000045999Eh  mov dword ptr [rbp-10h], esi
> 00000000004599A1h  movss xmm0, dword ptr [rbp-10h]
> 00000000004599A6h  call 0000000000459934h
> 00000000004599ABh  mov rsp, rbp
> 00000000004599AEh  pop rbp
> 00000000004599AFh  ret
> ;-------------------------------------
>
>  - testInlined() contains only the delegate call. (call qword ptr [rax])
>  - test() contains a call (call 0000000000459934h) which contains the
>    delegate call (call qword ptr [rax])
>
> Actually i've even had to add (pragma inline false) to show the difference since DMD inlined automatically bar() in test().

Guys, you're great! Thanks a lot!