Products > Programming

Is a C struct flatten in memory (Python wrap for a C lib)?

(1/3) > >>

RoGeorge:
I've tried yesterday to add a Python3 wrapping to a function from a C library, and failed.  :-\

It all went like this:

 ;D



This is the C function I've tried to call from Python, and the struct for *info bamboozles me:

--- Code: ---typedef struct
{
    void (*broadcast)(const char *address, const char *interface);
    void (*device)(const char *address, const char *id);
    void (*service)(const char *address, const char *id, const char *service, int port);
} lxi_info_t;

...

int lxi_discover(lxi_info_t *info, int timeout, lxi_discover_t type);

--- End code ---

The code is from https://github.com/lxi-tools/liblxi/blob/master/src/lxi.h , a C library used to control SCPI instruments over LAN (LXI).  The lxi_discover is meant to discover SCPI instruments present in the LAN.

For the other functions present in the same lib, the author already added a Python wrap:
https://github.com/lxi-tools/python-liblxi/blob/master/lxi.py

My attempt to add the discover function is about learning.  My instruments are set with a fixed IP so no need to discover them with a function call, though maybe others might have a different setup and find the lxi_discover useful, IDK.

I'm not sure what I was doing wrong in Python, but no wrapping syntax worked.  I think I don't understand the lxi_info_t struct.

- Can somebody explain to me in words please "void (*broadcast)(const char *address, const char *interface);"?
- How is the lxi_info_t struct datatype represented in memory?  Is it (for first line only) "pointer to function broadcast", "pointer to IP string", "pointer to interface name string"?

- How do I wrap lxi_discover, more precisely how to deal with the *info argument in Python?

Siwastaja:
They are three function pointers.

On memory level, function pointer is like any other pointer - just a memory address.

The arguments to the functions (const char *address, const char *interface) are not stored in the function pointer itself. These are provided so that compiler knows how the functions look like, and can generate correct code when you call the function, through that function pointer.

Depending on the machine, most likely the address is either 32 or 64 bits, so this would be 3 * 4 bytes or 3 * 8 bytes, in that order. I don't exactly remember the padding rules but AFAIK pointers should be definitely self-aligning and not need padding, so struct is just these three memory addresses back-to-back without gaps. Hence, it should Just Work, even if you fail to provide "packed" attritube to the struct.

Regarding Python, I don't know. The only thing that comes in my mind is, do the C compiler and Python agree about the ABI; i.e., the calling conventions? But as I have no idea about Python, maybe someone else can give a better guess.

RoGeorge:
So, talking about C only and assuming a pointer length is 8 bytes, the line:

--- Code: ---lxi_discover(info1, 5, 0)
--- End code ---
means info1 is 8 bytes containing the memory address of a data structure, and that memory zone pointed by info1 will look like this

--- Code: ---- 8 bytes containing the start address of a function named "broadcast"
- 8 bytes containing the start address of a function named "device"
- 8 bytes containing the start address of a function named "service"

--- End code ---
The other arguments are easy, timeout 5 is an int (let's say is stored on 2 bytes), and 0 is an enum, so another int stored in another 2 bytes.

If I got the *info right, then how does one finds out (in C) what instruments were discovered by the "lxi_discover(info1, 5, 0)"?

This is a usage example of calling lxi_discover( ... ), from the file https://github.com/lxi-tools/lxi-tools/blob/master/src/discover.c

--- Code: ---static int device_count = 0;
static int service_count = 0;

static void broadcast(const char *address, const char *interface)
{
    UNUSED(address);
    printf("Broadcasting on interface %s\n", interface);
}

static void device(const char *address, const char *id)
{
    printf("  Found \"%s\" on address %s\n", id, address);
    device_count++;
}

static void service(const char *address, const char *id, const char *service, int port)
{
    printf("  Found \"%s\" on address %s\n    %s service on port %u\n", id, address, service, port);
    service_count++;
}



int discover(bool mdns, int timeout)
{
    lxi_info_t info;

    // Set up info callbacks
    info.broadcast = &broadcast;
    info.device = &device;
    info.service = &service;

    printf("Searching for LXI devices - please wait...\n\n");

    // Search for LXI devices / services
    if (mdns)
    {
        lxi_discover(&info, timeout, DISCOVER_MDNS);
        if (service_count == 0)
            printf("No services found\n");
        else
            printf("\nFound %d service%c\n", service_count, service_count > 1 ? 's' : ' ');
    }

... 
    return 0;
}

--- End code ---

I don't understand the interaction with those 3 callback functions.

- Does this means "address" and "interface" are global variables used to return data (here the IP of the discovered instruments) back to the main program?
- How does lxi_discover (together with the 3 callback functions) returns the discovered data about the existing instruments?

gf:

--- Quote from: Siwastaja on January 12, 2022, 12:59:33 pm ---Regarding Python, I don't know. The only thing that comes in my mind is, do the C compiler and Python agree about the ABI; i.e., the calling conventions? But as I have no idea about Python, maybe someone else can give a better guess.

--- End quote ---

Two possibilities are:
1) Use the C API (implement python bindings to the library in C, i.e. implement an adapter module)
2) Use the CFFI (enables direct calling of C functions from Python)

gmb42:
The lxi_discover function is passed the lxi_info_t structure containing the user specified notification callbacks and when necessary the code will call the callbacks to notify of something of interest.

It's up to the user of lxi_discover what those callbacks do, in the example you've posted they simply print something to the console and increment the count variables.  You could for example, accumulate devices in a list of some sort.

Navigation

[0] Message Index

[#] Next page

There was an error while thanking
Thanking...
Go to full version