0

I have a C++ class with a method that is templated to follow the callable trait:

// A general data object
struct MyObject
{
    // ... hold some data, parameters, ...
};

class MyOptimizationAlgorithm
{
// ...
public:

    // An optimization function that uses a user-supplied 
    // callable to evaluate a data object:
    template <class Callable> void optimize(MyObject o, Callable evaluator) {
          // ... optimize, optimize, ...
          auto value = evaluator(o);
          // ... are we good enough yet?
    }
};

Here, the MyOptimizationAlgorithm class implements an optimization algorithm. The user supplies a data object (a vector of doubles, no problem here) and an objective function. This function is the user-configurable part on which the optimization algorithm relies. For example, valid callable evaluators could implement Ackley's function, the Cross-in Tray function, etc.

The pattern is actually pretty standard: In C++, the callable/predicate trait allows me to template a method so that I can pass a Functor, or a std::function. E.g.,

struct Ackley
{
    double operator()(MyObject const& o)
    {
        return /* ackley() applied to the values in o */
    }
};

MyOptimizationAlgorithm optimizer;
MyObject initialData;

// ... supply data,
// ... tune some parameters of the optimizer, then:

optimizer.optimize(initialData, Ackley());

// ... or:

optimizer.optimize(initalData, [](MyObject const& o) { /* ... */ });

I would now like to create a wrapper for Python with swig. The goal is, of course, to create the evaluator functor in Python and pass it to the C++ routine, like so:

def CrossInTray:
    def __call__(self, obj):
         # calculate the cross-in tray function.

optimzer = MyOptimizationAlgorithm()
initial_data = MyObject()
# ... again, setup, then:
optimizer.optimize(initial_data, CrossInTray())

I am new to swig. I have gathered that I need to specialize the template (using %template) and that I need to create a director (%director). I have tried to create a Functor wrapper, like so:

%inline %{
    struct MyEvaluator
    {
        virtual double operator()(MyObject const& o) { return 0.0; }
        virtual ~MyEvaluator() {}
    };
%}

%feature("director") MyEvaluator;
%extend MyAlgorithm {
    %template(runPredicated) optimize<MyEvaluator>;
}

I had hoped that I could then create subclasses of my Functor in Python, define __call__ there and have it used, but it only calls MyEvaluator::operator(), which is pretty pointless (and understandable, since I specialized the template to use MyEvaluator).

So: What do I need to add to the interface file to make use of the callable trait of C++ code in Python?

Technaton
  • 944
  • 4
  • 15
  • The trait can never be realized using a Python implementation, `%template(runPredicated) optimize;` is a template instantiation and at the point of instantiation, `MyEvaluator` must be known. What you can do easily is defined an interface in `C++`, implement this in Python and use this in C++ and even in a C++ program, see https://stackoverflow.com/questions/9040669/how-can-i-implement-a-c-class-in-python-to-be-called-by-c/9042139 – Jens Munk Sep 21 '17 at 14:31

1 Answers1

0

This is a bit too generic. If you want to restrict yourself to std::function interface like

class MyOptimizationAlgorithm
{
// ...
public:

    // An optimization function that uses a user-supplied 
    // callable to evaluate a data object:
    template<typename T>
    void optimize(MyObject o, std::function<T(MyObject const&)> evaluator) {
          // ... optimize, optimize, ...
          T value = evaluator(o);
          // ... are we good enough yet?
    }
};

a possible wrapper can look like

class MyOptimizationAlgorithm:
  def `optimize<double>` as optimize(o: MyObject, evaluator: (o: MyObject)->float)

and can be called with any Python function taking MyObject and returning a float. The Python code in the question should just work.

The wrapper above is PyCLIF (instead of SWIG).

Mike
  • 1,107
  • 6
  • 7