3

I want to use Boost.Python to create a Python wrapper for a C++ constructor with optional arguments. I want the Python wrapper to act like this:

class Foo():
  def __init__(self, filename, phase, stages=None, level=0):
    """
    filename -- string
    phase -- int
    stages -- optional list of strings
    level -- optional int
    """
    if stages is None:
      stages = []
    # ...

How do I do this with Boost.Python? I don't see how to do it with make_constructor, and I don't know how to make a constructor with raw_function. Is there some better documentation than this out there?

My specific problem is trying to add two optional arguments (stages and level) to these two constructors:

https://github.com/BVLC/caffe/blob/rc3/python/caffe/_caffe.cpp#L76-L96

4
  • 2
    Could you use the args class in you constructor def? That should let you generate the keyword-expression that make_constructor takes as well. Commented Mar 21, 2016 at 18:37
  • 1
    Some notes about how to make raw constructor are on the python wiki. Commented Mar 21, 2016 at 18:52
  • 1
    as in make_constructor(&Net_Init, default_call_policies(), (arg("param_file"), arg("phase"), arg("stages")=object(), arg("level")=0)) -- stages being object, so that you can deal with None. Commented Mar 21, 2016 at 19:05
  • Thanks @DanMašek - I think I've got something working! I'll post my solution after I double-check it ... Commented Mar 21, 2016 at 21:04

1 Answer 1

4

Thanks to Dan's comments, I found a solution that works. I'll copy most of it here since there are some interesting tidbits about how to extract objects from bp::object, etc.

// Net constructor
shared_ptr<Net<Dtype> > Net_Init(string param_file, int phase,
    const int level, const bp::object& stages,
    const bp::object& weights_file) {
  CheckFile(param_file);

  // Convert stages from list to vector
  vector<string> stages_vector;
  if (!stages.is_none()) {
      for (int i = 0; i < len(stages); i++) {
        stages_vector.push_back(bp::extract<string>(stages[i]));
      }   
  }   

  // Initialize net 
  shared_ptr<Net<Dtype> > net(new Net<Dtype>(param_file,
      static_cast<Phase>(phase), level, &stages_vector));

  // Load weights
  if (!weights_file.is_none()) {
      std::string weights_file_str = bp::extract<std::string>(weights_file);
      CheckFile(weights_file_str);
      net->CopyTrainedLayersFrom(weights_file_str);
  }   

  return net;
}   

BOOST_PYTHON_MODULE(_caffe) {
  bp::class_<Net<Dtype>, shared_ptr<Net<Dtype> >, boost::noncopyable >("Net",
    bp::no_init)
    .def("__init__", bp::make_constructor(&Net_Init,
          bp::default_call_policies(), (bp::arg("network_file"), "phase",
            bp::arg("level")=0, bp::arg("stages")=bp::object(),
            bp::arg("weights_file")=bp::object())))
}   

The generated signature is:

__init__(boost::python::api::object, std::string network_file, int phase, 
   int level=0, boost::python::api::object stages=None,
   boost::python::api::object weights_file=None)

And I can use it like:

net = caffe.Net('network.prototxt', weights_file='weights.caffemodel',
  phase=caffe.TEST, level=1, stages=['deploy'])

Full code available in pull request here: https://github.com/BVLC/caffe/pull/3863

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

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.