Raku NativeCalls provide a way to interact with dynamic libraries that follow the C calling convention and are very useful for obtaining information from the operating system, such as memory usage.
In this article we will see how to get the memory usage from a Windows system.
Win32 API provides the MEMORYSTATUSEX structure:
typedef struct _MEMORYSTATUSEX {
DWORD dwLength;
DWORD dwMemoryLoad;
DWORDLONG ullTotalPhys;
DWORDLONG ullAvailPhys;
DWORDLONG ullTotalPageFile;
DWORDLONG ullAvailPageFile;
DWORDLONG ullTotalVirtual;
DWORDLONG ullAvailVirtual;
DWORDLONG ullAvailExtendedVirtual;
} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;that contains information about the current memory in bytes, including:
- Total physical memory:
ullTotalPhys - Available physical memory:
ullAvailPhys
These two members will allow us to calculate the memory usage by substracting ullAvailPhys from ullTotalPhys.
The MEMORYSTATUSEX structure has the dwLength member that needs to be set before calling the function GlobalMemoryStatusEx that populates the MEMORYSTATUSEX structure.
Raku Nativecalls use the repr('CStruct') trait into a declaration class to interact with C structures. We can declare this class to use the C++ MEMORYSTATUSEX structure as follows:
class MEMORYSTATUSEX is repr('CStruct') {
has uint32 $.dwLength is rw;
has uint32 $.dwMemoryLoad;
has uint64 $.ullTotalPhys;
has uint64 $.ullAvailPhys;
has uint64 $.ullTotalPageFile;
has uint64 $.ullAvailPageFile;
has uint64 $.ullTotalVirtual;
has uint64 $.ullAvailVirtual;
has uint64 $.ullAvailExtendedVirtual;
};It's important mapping the member types between C++ and Raku:
- The Raku type
uint32is equivalent to C++ Win32DWORDtype. - The Raku type
uint64is equivalent to C++ Win32DWORDLONGtype.
The $.dwLength member is rw (read and write) to set the size of the structure later.
This C++ function populates the MEMORYSTATUSEX structure using as argument the LPMEMORYSTATUSEX pointer type:
BOOL GlobalMemoryStatusEx( LPMEMORYSTATUSEX lpBuffer );Raku doesn't use pointers to perform a Native Call to this function, instead it uses a Raku function with the native trait as we will see below.
Raku Native Calls allows to use the C++ GlobalMemoryStatusEx function as follows:
use NativeCall;
sub GlobalMemoryStatusEx(MEMORYSTATUSEX) is native('Kernel32') returns int32 { * };use NativeCallloads the Raku NativeCall context.GlobalMemoryStatusExis the function name and must be the same as the original name in C++.MEMORYSTATUSEXis the name of the class we have declared before to interact with the C++ struct that will contain the members we need ($.ullTotalPhysand$.ullAvailPhys). Actually, this class acts as the pointer that goes into the argument of the C++GlobalMemoryStatusExfunction.- The trait
is native('Kernel32')exposes the Win32 library that contains theGlobalMemoryStatusExfunction. - This function returns a bool value that is represented by the
int32type.
To use the MEMORYSTATUSEX class with the GlobalMemoryStatusEx Native function we need to instanciate it in an object, $data for example:
my MEMORYSTATUSEX $data .=new;Also, let's not forget to pass the size of the structure (or $data object) setting the $data.dwLength member with the current size. The structure size is the $data object size and we can get it with the function nativesizeof:
$data.dwLength = nativesizeof($data);Now we are ready to populate the MEMORYSTATUSEX struct ($data object) calling the GlobalMemoryStatusEx Native function with the $data object as argument:
GlobalMemoryStatusEx($data);The $data object acts like the C++ LPMEMORYSTATUSEX pointer.
Finnaly, the results that we need are in the values of the members of the $data object:
my $memoryUsage = $data.ullTotalPhys - $data.ullAvailPhys;
say "Current Memory Usage: $memoryUsage Bytes.";Here you have available this example applied in a Raku module.
As we have seen in this example, the use of Raku Native Calls allows extending the Raku's functionality to the universe of the operating system through its dynamic libraries. Furthermore, by making the appropriate calls to different operating systems, we can create applications that work on any of them.
More information about Raku Native calling interface in the Raku documentation.