1

Let's say we have a following struct in C:

typedef struct buffer
{
    uint8_t* const data;
    const size_t maxSize;
    size_t size;
} buffer_t;

How can I make a SWIG wrapper for this so that when buffer_t is created in Python it allocates given number of bytes to data pointer and sets maxSize accordingly?

Details

So basically the problem here is related to constant struct members. By default SWIG initializes structs with default values. This leads into problems:

StructWithConsts_t struct;
struct.constant = 5; // Error. This should've been set when creating the struct.

The accepted answer with constructors and destructors provide solution for this problem.

2
  • You should provide a minimal reproducing example. Commented Sep 24, 2020 at 11:47
  • Sorry, I thought the problem was implicitly clear. I'll add details. Commented Sep 25, 2020 at 6:34

2 Answers 2

1

You can treat C structs as C++ classes in SWIG and extend them with creators and destroyers methods.

In your case your .i should have this

typedef struct
{
    uint8_t* const data;
    const size_t maxSize;
    size_t size;
} buffer_t;

%extend buffer_t {
    buffer_t(size_t size)
    {
        buffer_t* pBuffer = malloc(sizeof *pBuffer);
        pBuffer->data = malloc(size * sizeof (*pBuffer->data));
        pBuffer->maxSize = size;
        return pBuffer;
    }

    void ~buffer_t()
    {
        buffer_t *pBuffer = ($self);
        free(pBuffer->data);
        free(pBuffer);
        return;
    }
};

In python it will be used like this

from yourmodule import buffer_t

buffer = buffer_t(10)

The garbage collector will take care of freeing the memory.

There are other ways of doing this but you don't have a reproducing example to test it out.

Sign up to request clarification or add additional context in comments.

3 Comments

This is much cleaner interface! Thanks! This does not work though if the members are constant. To address that issue the const members needs to be cast into non-const for assignment. Looks filthy, but it's the only way.
@JuhoL You can initialize constants in a constructor but the syntax is different: buffer_t(size_t size) : data(malloc(sizeof *pBuffer)) { ... } for example. Difficult to error check, though. Casting a constant to non-constant has risks if the constant memory is read-only. You're invoking undefined behavior here. Better to make the members of the struct non-const, and return a const structure when it is used.
Malloc allocations are guaranteed to be in heap, so no risk there, but you are right that in general casting consts to non-consts is extremely bad idea and it is strictly forbidden.
0

I found one solution, but it is rather hackish due to pointer typecasts. I'm not convinced that this is the proper way to do this, but it works:

In the .i file I created a wrapper using %inline:

%inline %{
buffer_t* new_buffer(size_t size)
{
    buffer_t* pBuffer = (buffer_t*)malloc(sizeof(buffer_t));
    *(uint8_t**)&pBuffer->data = (uint8_t*)malloc(size * sizeof(uint8_t));
    *(size_t*)&pBuffer->maxSize = size;
    return (buffer_t*)pBuffer;
}

void free_buffer(buffer_t* pBuffer)
{
    free(pBuffer->data);
    free(pBuffer);
    return;
}
%}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.