fxm wrote: ↑Apr 28, 2023 18:51
About :
'Procptr ( identifier, virtual [ any|user_proctype ] )'
my opinion, I think VIRTUAL for this context should only be described on PROCPTR page and only a brief mention on VIRTUAL page.
VIRTUAL when used with PROCPTR changes the purpose of PROCPTR, which I suppose is bad design on my part, but avoids having to add any *new* global functions / operators / keywords. And if fbc did something better, VIRTUAL in this context probably wouldn't be needed.
In the meantime, procptr(...,virtual) can be used to help with work-arounds or prototyping something better in user code.
----
Fun fact,
PROCPTR( proc, virtual) is a compile time constant. If used in expressions or assigned to constants, will get optimized and constant folded at compile time.
Or can make decisions in the pre-processor for conditional compilation:
Code: Select all
#if procptr(proc, virtual) >= 0
'' do virtual call stuff
#else
'' do normal call stuff
#endif
...
---
An approximation of adeyblue's idea using a stub procedure for the virtual call thunk...
Code: Select all
'' make a unique-ish name
#macro mkVirtualThunkProcName( typeName, procName, signature... )
__FB_JOIN__( VirtualThunk##typeName, __FB_EVAL__( procptr( typeName.procName, virtual signature ) ) )
#endmacro
'' define a stub to call handle the virtual call
#macro mkVirtualThunk( typeName, procName, signature... )
'' only define a thunk if it is virtual ....
#if( procptr( typeName.procName, virtual signature ) >= 0 )
private sub mkVirtualThunkProcName( typeName, procName, signature ) naked ()
const index = procptr( typeName.procName, virtual signature )
'' assuming we can use EAX/RAX on any x86/x86_64
asm
#if defined(__FB_64BIT__)
#if defined(__FB_WIN32__)
mov rax, rcx '' rax = address of instance
#else
mov rax, rdi '' rax = address of instance
#endif
mov rax, [rax] '' rax = address of virtual table
jmp [rax+8*index] '' [rax+8*index] = address of virtual procedure
#else
mov eax, [esp+4] '' eax = address of instance
mov eax, [eax] '' eax = address of virtual table
jmp [eax+4*index] '' [eax+4*index] = address of virtual procedure
#endif
end asm
end sub
#endif
#endmacro
'' get an address to the thunk if it is virtual, otherwise just the address
#macro VirtualProcPtr( typeName, procName, signature... )
__FB_IIF__( procptr( typeName.procName, virtual signature ) >= 0, _
cast( typeof( procptr( typeName.procName, signature )), _
procptr( mkVirtualThunkProcName( typeName, procName, signature ) ) ), _
procptr( typeName.procName, signature ) )
#endmacro
'' ------------------------------------
type B extends object
declare abstract sub proc1()
declare abstract sub proc2()
end type
type D extends B
declare virtual sub proc1()
declare virtual sub proc2()
end type
sub D.proc1() : print "D.proc1" : end sub
sub D.proc2() : print "D.proc2" : end sub
mkVirtualThunk( B, proc1 )
mkVirtualThunk( B, proc2 )
dim x as B ptr = new D
'' call virtual members of derivied instance through base pointer
var fp1 = VirtualProcPtr( B, proc1 )
var fp2 = VirtualProcPtr( B, proc2 )
fp1( *x )
fp2( *x )
delete x
sleep
However, the NAKED procedure doesn't seem to be seen by gcc since it is defined in ASM ... gcc issues a warning that it is used but not defined.