2

I have a struct as follows:

struct power_model {
    int64_t (*estimate_energy)(statistics *stats, statistics *scaled_stats, parameters *from, parameters *to, energy_container *energy_container);
    int64_t (*estimate_performance)(statistics *stats, parameters *params);
    uint32_t (*freq_to_volt)(uint32_t freq);
};

There are multiple power models that my code contains. I would like to wrap these models with SWIG and expose them to Python so that I can run my unit tests.

While the SWIG documentation talks about exposing function pointers, it does not talk about function pointers contained within structs. I tried to encapsulate the calls in my interface file

%{
#include "models.h"
%}

%include "models.h"

%extend power_model {
  %pythoncallback;
  int64_t (*estimate_energy)(statistics *stats, statistics *scaled_stats, parameters *from, parameters *to, energy_container *energy_container);
  int64_t (*estimate_performance)(statistics *stats, parameters *params);
  uint32_t (*freq_to_volt)(uint32_t freq);
  %nopythoncallback;
}

I also tried prefixing the field names with %constant.

With these approaches, I always end up with the same error:

In [3]: model.estimate_energy()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-b2e3ace2fc9b> in <module>()
----> 1 model.estimate_energy()

TypeError: 'SwigPyObject' object is not callable

How can I call the underlying functions referenced by the function pointers contained within struct power_model?

Edit: To elaborate on my setup, I am also sources of two additional files to better explain the setup I'm trying to achieve with the power_model interface.

nexus5.c

static int64_t estimate_energy(statistics *stats, statistics *scaled_stats, parameters *from, parameters *to, energy_container *energy) {
    ...
}
static int64_t estimate_performance(statistics *stats, parameters *params) {
    ...
}
static uint32_t freq_to_volt(uint32_t freq) {
    ...
}

struct power_model nexus5_power_model = {
     .estimate_energy = estimate_energy,
     .estimate_performance = estimate_performance,
     .freq_to_volt = freq_to_volt,
};

galaxy_s.c

static int64_t estimate_energy(statistics *stats, statistics *scaled_stats, parameters *from, parameters *to, energy_container *energy) {
    ...
}
static int64_t estimate_performance(statistics *stats, parameters *params) {
    ...
}
static uint32_t freq_to_volt(uint32_t freq) {
    ...
}

struct power_model galaxy_s_power_model = {
     .estimate_energy = estimate_energy,
     .estimate_performance = estimate_performance,
     .freq_to_volt = freq_to_volt,
};
Guru Prasad
  • 4,053
  • 2
  • 25
  • 43
  • The example here http://stackoverflow.com/questions/22923696/how-to-wrap-a-c-function-which-takes-in-a-function-pointer-in-python-using-swi demonstrates many aspects of function pointers, for all solutions 1-5, you add a struct, e.g. `typedef struct A { fptr_t f;} A;` and store and execute functions. No problem – Jens Munk Sep 28 '15 at 18:39
  • This does not work in my case. I cannot create the struct in my interface file. The struct is present in a header file and I just include it in my interface file. Also, I have 5 files with `static int64_t estimate_energy(...)`. How would I include these functions in my interface file and wrap them around `%pythoncallback;`. For me, the whole point of using struct with function pointers is to provide an interface that different models can implement in differing ways. – Guru Prasad Sep 29 '15 at 00:03
  • As long as the prototype for the functions are known, it works for me. Try working up an example from scratch to get familiar with the ordering of the header files included, SWIG does not recurse header files for definitions. The error is most likely due to this rather than function pointers. – Jens Munk Sep 29 '15 at 06:57
  • I'm not relying on recursive header file definitions. I am including `models.h` that contains the `struct power_model`. The actual implementation of all of these function pointers are all static functions which I cannot prototype in header files or in the interface file. Even if I could prototype them in the interface file, they all have the same name - something I can easily change..but that makes me question whether this is the right approach – Guru Prasad Sep 29 '15 at 14:25
  • You don't need to prototype static functions, just their prototype like shown below. You do need to expose the many static function to be able to use their address for assigning pointers. – Jens Munk Sep 29 '15 at 14:29
  • I'm not sure I understand. Could you kindly elaborate in your answer below? – Guru Prasad Sep 29 '15 at 14:33
  • Imagine you have a dozen of the functions like f5. You can then use any of them and store their address in a bla struct. Of course each of the many functions must be properly defined. I am at the gym right now. – Jens Munk Sep 29 '15 at 14:39

1 Answers1

1

This works for me. The solution 5 is the preferred one.

test.i

%module test

%{
#include "test.h"
%}

// Solution 5 (right one)
%pythoncallback;
double f5(double);
%nopythoncallback;
%ignore f5;

%include "test.h"    

test.h

typedef double (*fptr_t)(double);

// Solution 5
double f5(double x) {
  return x*x;
}

typedef struct bla {
  fptr_t f;
} bla;

From within Python

import test
s = test.bla
# Assign function pointer
s.f = test.f5
# Execute
s.f(2)

f is a function taking a function pointer as argument

Jens Munk
  • 4,627
  • 1
  • 25
  • 40