1. Simple Attempt Wrapping CPP Library

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}) 
new topic     » topic index » view message » categorize

2. Re: Simple Attempt Wrapping CPP Library

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

new topic     » goto parent     » topic index » view message » categorize

3. Re: Simple Attempt Wrapping CPP Library

ghaberek said...

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.

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu