1. Simple Attempt Wrapping CPP Library
- Posted by Icy_Viking Feb 28, 2023
- 879 views
Hello all,
I am trying to wrap a CPP library, not regular C, but CPP. I am getting subscript from an atom error. I'm not suring what I am doing wrong. Is there something extra you need to do when wrapping CPP libraries?
//CPP Code #pragma once #ifdef GETCORETEMPINFO_EXPORTS #define GETCORETEMPINFO_API __declspec(dllexport) #else #define GETCORETEMPINFO_API __declspec(dllimport) #endif #define UNKNOWN_EXCEPTION 0x20000000 typedef struct core_temp_shared_data { unsigned int uiLoad[256]; unsigned int uiTjMax[128]; unsigned int uiCoreCnt; unsigned int uiCPUCnt; float fTemp[256]; float fVID; float fCPUSpeed; float fFSBSpeed; float fMultipier; char sCPUName[100]; unsigned char ucFahrenheit; unsigned char ucDeltaToTjMax; }CORE_TEMP_SHARED_DATA,*PCORE_TEMP_SHARED_DATA,**PPCORE_TEMP_SHARED_DATA; bool GETCORETEMPINFO_API fnGetCoreTempInfo(CORE_TEMP_SHARED_DATA *&pData); bool WINAPI fnGetCoreTempInfoAlt(CORE_TEMP_SHARED_DATA *pData); class GETCORETEMPINFO_API CoreTempProxy { public: CoreTempProxy(void); virtual ~CoreTempProxy(void); UINT GetCoreLoad(int Index) const; UINT GetTjMax(int Index) const; UINT GetCoreCount() const; UINT GetCPUCount() const; float GetTemp(int Index) const; float GetVID() const; float GetCPUSpeed() const; float GetFSBSpeed() const; float GetMultiplier() const; LPCSTR GetCPUName() const; bool IsFahrenheit() const; bool IsDistanceToTjMax() const; const CORE_TEMP_SHARED_DATA &GetDataStruct() const; bool GetData(); DWORD GetDllError() const { return GetLastError(); } LPCWSTR GetErrorMessage(); private: CORE_TEMP_SHARED_DATA m_pCoreTempData; WCHAR m_ErrorMessage[100]; };
include std/machine.e include std/ffi.e atom cpu = 0 cpu = open_dll("GetCoreTempInfo.dll") if cpu = -1 then puts(1,"Failed to load DLL!\n") abort(0) end if public constant core_temp_shared_data = define_c_type({ {C_UINT,256}, --uiLoad[256] {C_UINT,128}, --uiTjMax[128] C_UINT, --iCorecnt C_UINT, --uiCPUCnt {C_FLOAT,256}, --fTemp[256] C_FLOAT, --fVID C_FLOAT, --CPUSpeed C_FLOAT, --FSBSpeed C_FLOAT, --fMultiplier {C_CHAR,100}, --CPUName[100] C_UCHAR, --uFarenheight C_UCHAR --ucDeltaToTjMax }) export constant xGetCoreLoad = define_c_func(cpu,"+GetCoreLoad",{C_INT},C_UINT), xGetTjMax = define_c_func(cpu,"+GetTjMax",{C_INT},C_UINT), xGetCoreCount = define_c_func(cpu,"+GetCoreCount",{},C_UINT), xGetCPUCount = define_c_func(cpu,"+GetCPUCount",{},C_UINT), xGetTemp = define_c_func(cpu,"+GetTemp",{C_INT},C_FLOAT), xGetCPUName = define_c_func(cpu,"+GetCPUName",{},C_WSTRING) public function GetCPUName() return c_func(xGetCPUName,{}) end function sequence n = GetCPUName() --error comes from this line printf(1,"Cores: %s",{n})
2. Re: Simple Attempt Wrapping CPP Library
- Posted by ghaberek (admin) Mar 01, 2023
- 728 views
Bad news is, you're not going to be able to wrap C++ calls without basically recreating the vtable and name mangling used by various C++ compilers, which is unique to each major compiler. Good news is, you don't have to.
Looking at the Core Temp Plug-in SDK1 page we can see we just need those exported functions called fnGetCoreTempInfo (which uses CDECL) and fnGetCoreTempInfoAlt (which uses STDCALL) and that's all we really need to get at the data. The C++ class is just there to get access to the structure members so we can ignore it completely. Although on my first attempt to call either function they returned 0. The SDK page indicates that GetLastError should return 0x20000000 if there was an exception. When I call GetLastError I got back a value of "2" which is ERROR_FILE_NOT_FOUND. Turns out you need to have Core Temp installed and running and have the Enable global shared memory option turned on.
1Side note: it's helpful to link directly to these things when asking for help so I can get more information from the source without having to hunt it down.
Note: this code uses my recent changes to libffi-euphoria (I've been sitting on them lately so I pushed them all just now.) I renamed define_c_type() to define_c_struct() and added define_c_union() (and I think it works?). There's a new define_c_type() function thats it's strictly for creating or copying C types. Its parameter types are different so your existing code will need to be renamed to use define_c_struct(). I also added peek_member() and poke_member() to more easily access struct/union members, including nested members! I'd still like to add functionality for naming things so you can allocate structs and peek/poke members by name. Might even incorporate basic C parser so you can define structs by (more or less) copying the code from C headers. IIRC this is sort of how Pete does it for Phix.
include std/ffi.e include std/machine.e constant coretemp = open_dll( "GetCoreTempInfo.dll" ) constant fnGetCoreTempInfo = define_c_func( coretemp, "+fnGetCoreTempInfo", {C_POINTER}, C_BOOL ) constant CORE_TEMP_SHARED_DATA = define_c_struct({ {C_UINT,256}, -- unsigned int uiLoad[256] {C_UINT,128}, -- unsigned int uiTjMax[128] C_UINT, -- unsigned int uiCoreCnt C_UINT, -- unsigned int uiCPUCnt {C_FLOAT,256}, -- float fTemp[256] C_FLOAT, -- float fVID C_FLOAT, -- float fCPUSpeed C_FLOAT, -- float fFSBSpeed C_FLOAT, -- float fMultiplier {C_CHAR,100}, -- char sCPUName[100] C_UCHAR, -- unsigned char ucFarenheit C_UCHAR -- unsigned char ucDeltaToTjMax }) public function GetCoreTempInfo() -- N.B. it's not clear from the SDK docs but fnGetCoreTempInfo -- expects a pointer to the pointer of our structure. So we -- first allocate the structure (which gives us a pointer) and -- then allocate some memory and store our pointer there, then -- pass this "indirect" pointer when calling the function. atom data = allocate_struct( CORE_TEMP_SHARED_DATA ) atom pdata = allocate_data( sizeof(C_POINTER), 1 ) poke_pointer( pdata, data ) if not c_func( fnGetCoreTempInfo, {pdata} ) then free( data ) return NULL end if return delete_routine( data, routine_id("free") ) end function public function GetCoreLoad( atom data, integer nIndex ) return peek_member( data, CORE_TEMP_SHARED_DATA, {1,nIndex} ) end function public function GetTjMax( atom data, integer nIndex ) return peek_member( data, CORE_TEMP_SHARED_DATA, {2,nIndex} ) end function public function GetCoreCount( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 3 ) end function public function GetCPUCount( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 4 ) end function public function GetTemp( atom data, integer nIndex ) return peek_member( data, CORE_TEMP_SHARED_DATA, {5,nIndex} ) end function public function GetVID( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 6 ) end function public function GetCPUSpeed( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 7 ) end function public function GetFSBSpeed( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 8 ) end function public function GetMultiplier( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 9 ) end function public function GetCPUName( atom data ) sequence name = peek_member( data, CORE_TEMP_SHARED_DATA, 10 ) -- N.B. sCPUName is a character array, not a string pointer. Calling -- peek_member() will always return a sequence of 100 characters (the -- size of the array) so we need to trim any trailing null characters. -- Sometimes we get a trailing space so we'll just check for the last -- non-space printable character and trim off everything after that. for i = length( name ) to 1 by -1 do if name[i] > ' ' then name = name[1..i] exit end if end for return name end function public function IsFahrenheit( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 11 ) end function public function IsDistanceToTjMax( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 12 ) end function
-Greg
3. Re: Simple Attempt Wrapping CPP Library
- Posted by Icy_Viking Mar 01, 2023
- 634 views
Bad news is, you're not going to be able to wrap C++ calls without basically recreating the vtable and name mangling used by various C++ compilers, which is unique to each major compiler. Good news is, you don't have to.
Looking at the Core Temp Plug-in SDK1 page we can see we just need those exported functions called fnGetCoreTempInfo (which uses CDECL) and fnGetCoreTempInfoAlt (which uses STDCALL) and that's all we really need to get at the data. The C++ class is just there to get access to the structure members so we can ignore it completely. Although on my first attempt to call either function they returned 0. The SDK page indicates that GetLastError should return 0x20000000 if there was an exception. When I call GetLastError I got back a value of "2" which is ERROR_FILE_NOT_FOUND. Turns out you need to have Core Temp installed and running and have the Enable global shared memory option turned on.
1Side note: it's helpful to link directly to these things when asking for help so I can get more information from the source without having to hunt it down.
Note: this code uses my recent changes to libffi-euphoria (I've been sitting on them lately so I pushed them all just now.) I renamed define_c_type() to define_c_struct() and added define_c_union() (and I think it works?). There's a new define_c_type() function thats it's strictly for creating or copying C types. Its parameter types are different so your existing code will need to be renamed to use define_c_struct(). I also added peek_member() and poke_member() to more easily access struct/union members, including nested members! I'd still like to add functionality for naming things so you can allocate structs and peek/poke members by name. Might even incorporate basic C parser so you can define structs by (more or less) copying the code from C headers. IIRC this is sort of how Pete does it for Phix.
include std/ffi.e include std/machine.e constant coretemp = open_dll( "GetCoreTempInfo.dll" ) constant fnGetCoreTempInfo = define_c_func( coretemp, "+fnGetCoreTempInfo", {C_POINTER}, C_BOOL ) constant CORE_TEMP_SHARED_DATA = define_c_struct({ {C_UINT,256}, -- unsigned int uiLoad[256] {C_UINT,128}, -- unsigned int uiTjMax[128] C_UINT, -- unsigned int uiCoreCnt C_UINT, -- unsigned int uiCPUCnt {C_FLOAT,256}, -- float fTemp[256] C_FLOAT, -- float fVID C_FLOAT, -- float fCPUSpeed C_FLOAT, -- float fFSBSpeed C_FLOAT, -- float fMultiplier {C_CHAR,100}, -- char sCPUName[100] C_UCHAR, -- unsigned char ucFarenheit C_UCHAR -- unsigned char ucDeltaToTjMax }) public function GetCoreTempInfo() -- N.B. it's not clear from the SDK docs but fnGetCoreTempInfo -- expects a pointer to the pointer of our structure. So we -- first allocate the structure (which gives us a pointer) and -- then allocate some memory and store our pointer there, then -- pass this "indirect" pointer when calling the function. atom data = allocate_struct( CORE_TEMP_SHARED_DATA ) atom pdata = allocate_data( sizeof(C_POINTER), 1 ) poke_pointer( pdata, data ) if not c_func( fnGetCoreTempInfo, {pdata} ) then free( data ) return NULL end if return delete_routine( data, routine_id("free") ) end function public function GetCoreLoad( atom data, integer nIndex ) return peek_member( data, CORE_TEMP_SHARED_DATA, {1,nIndex} ) end function public function GetTjMax( atom data, integer nIndex ) return peek_member( data, CORE_TEMP_SHARED_DATA, {2,nIndex} ) end function public function GetCoreCount( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 3 ) end function public function GetCPUCount( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 4 ) end function public function GetTemp( atom data, integer nIndex ) return peek_member( data, CORE_TEMP_SHARED_DATA, {5,nIndex} ) end function public function GetVID( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 6 ) end function public function GetCPUSpeed( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 7 ) end function public function GetFSBSpeed( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 8 ) end function public function GetMultiplier( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 9 ) end function public function GetCPUName( atom data ) sequence name = peek_member( data, CORE_TEMP_SHARED_DATA, 10 ) -- N.B. sCPUName is a character array, not a string pointer. Calling -- peek_member() will always return a sequence of 100 characters (the -- size of the array) so we need to trim any trailing null characters. -- Sometimes we get a trailing space so we'll just check for the last -- non-space printable character and trim off everything after that. for i = length( name ) to 1 by -1 do if name[i] > ' ' then name = name[1..i] exit end if end for return name end function public function IsFahrenheit( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 11 ) end function public function IsDistanceToTjMax( atom data ) return peek_member( data, CORE_TEMP_SHARED_DATA, 12 ) end function
-Greg
Thanks Greg. Wow that's really cool you made those changes to the libffi library. I'm sure that will make wrapping libraries even easier. I'll also keep that in mind next time I need help regarding your side note.