Call and use c++ classes via interfaces

Share your advanced PureBasic knowledge/code with the community.
User avatar
idle
Always Here
Always Here
Posts: 5845
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Call and use c++ classes via interfaces

Post by idle »

macro to facilitate calling member functions in c++ classes
windows x86
pb 5.11

Currently there's no convenient way to call functions from c++ classes in windows x86
and without a compiler directive to differentiate between a com Interface or cpp Interface
we need to either resort to inline asm, use a compiler tool to rewrite the asm or patch the assembly

Interfaces can be made for a c++ classes public members when the functions in the class are declared as virtual functions
So as long as the c++ library exports it's classes constructor functions as extern c you can import the constructor
and assign the result to the interfaces and then call the functions without needing to wrap and recompiling the c++ library.

While the patch adds some overhead and needs to know when and what to patch
the cost of the overhead can be minimized by calling SetCppObj(0) when not in use, when your calling regular functions.
When you need to use an interface to a c++ class you need to call SetCppObj(*activeObject) before calling it's functions.

Code: Select all

;{
CompilerIf #PB_Compiler_OS = #PB_OS_Windows  And SizeOf(Integer) = 4  
  
  !Macro CALL proc {
   !cmp [p_cppobjptr], 0
   !je @f
   !push edx 
   !mov edx, dword [p_cppobjptr]
   !mov edx, [edx]
   !cmp edx, eax 
   !pop edx 
   !jne @f
   !pop eax
   !mov eax, dword [p_cppobjptr]
   !mov ecx, eax 
   !mov eax,[eax]
   !@@:
   !call proc   
   !}  
   
 Global *cppobjptr

  Macro SetCppObj(pInterface)
   *cppobjptr = pInterface 
 EndMacro   
 
 ;call SetCppOjb with the *objectptr so it knows to patch it to a this call 
 
CompilerEndIf   
;}

;you need to call SetCppObj with the active class interface befroe calling the class functions  
;you can set it to zero when you are calling regular functions though it's not required  

Macro SetCppObj(pInterface)
   *cppobjptr = pInterface 
EndMacro   

Mock up of how to use it

Code: Select all

Interface IrrlichtDevice
   Run()
   Yield()
   Sleep(timeMs, pauseTimer=#False)
   GetVideoDriver()
   GetFileSystem()
   GetGUIEnvironment()
   GetSceneManager()
  ;...
EndInterface 

Interface iVideoDriver
  
EndInterface 

 Import "Irrlicht.lib"
    IrrCreateDevice(deviceType, windowSize, bits, fullscreen, stencilbuffer, vsync, receiver=0) As "_createDevice"
 EndImport

Global *IrrDevice.IrrlichtDevice,*videoDriver.IVideoDriver  

*IrrDevice.IrrlichtDevice=irrCreateDevice(#EDT_OPENGL,@res,16,#False,#True,#True)
If *IrrDevice
   
   SetCppObj(*IrrDevice) ;set the active c++ class interface so calls can be patched        
   
  *videoDriver = *IrrDevice\GetvideoDriver()
 
   SetCppObj(*videoDriver) 
  *videoDriver\queryFeature(somefeatureflag) 
  
   SetCppObj(*IrrDevice)
  
   Repeat 
      x = *IrrDevice\run() 
     
  Until x = 0 
EndIf

Windows 11, Manjaro, Raspberry Pi OS
Image
User_Russian
Addict
Addict
Posts: 1520
Joined: Wed Nov 12, 2008 5:01 pm
Location: Russia

Re: Call and use c++ classes via interfaces

Post by User_Russian »

Another macro for FASM, which allows to convert stdcall in thiscall.
Me a gave it to idle, and after small improvements, the macro runs on current versions of PB (tested in PB 5.11).

Code: Select all

; Thiscall macro.
!macro CALL arg
! {
! clabel = $
! CALL arg
! plabel = clabel-3
! callsize = $-clabel
! load ops dword from plabel
! load opc byte from plabel+4
! If ops=$FF008B50 & (opc=$10 | opc=$50 | opc=$90)
!   If opc=$10
!     db $10
!   Else If (opc=$50 | opc=$90)
!     db $00
!     Repeat callsize
!       load op byte from clabel+callsize-%
!       store byte op at clabel+callsize-%+1
!     End Repeat
!   End If
!   store dword $008BC189 at plabel
!   store byte $FF at plabel+4
! End If
! }
Macro must be placed at the beginning of the program code.
Macro automatically corrects all method calls interfaces, so that the calling convention stdcall, changed to thiscall.
Post Reply