I have a base class in c that looks like:
class EdgeAttributePipelineStep {
public:
virtual void setAttributes(Edge& edge) = 0;
};
And then some classes that inherit from this class:
class FuelCostStep : public EdgeAttributePipelineStep {
public:
FuelCostStep(float fuelCostPerTonne)
: m_FuelCostPerTonne(fuelCostPerTonne) {};
void setAttributes(Edge& edge);
private:
float m_FuelCostPerTonne;
};
class HireCostStep : public EdgeAttributePipelineStep {
public:
HireCostStep(float hireCostPerDay) : m_HireCostPerHour(hireCostPerDay/24) {};
void setAttributes(Edge &edge);
private:
float m_HireCostPerHour;
};
And then finally, a class that accepts a vector of the base class in its constructor:
class EdgeAttributePipeline {
public:
EdgeAttributePipeline(std::vector<std::shared_ptr<EdgeAttributePipelineStep>> steps);
private:
std::vector<std::shared_ptr<EdgeAttributePipelineStep>> m_steps;
};
I want to be able to create the pipeline class in python like:
pipeline = EdgeAttributePipeline([
FuelCostStep(10),
HireCostStep(10)
])
And I attempted some python bindings using PyBind11 to do this:
#include "edge_attributes.h"
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
PYBIND11_MODULE(WeatherRouting, m)
{
py::class_<EdgeAttributePipelineStep>(m, "EdgeAttribtuePipelineStep");
// I tried both using std::shared_ptr
py::class_<FuelCostStep, std::shared_ptr<FuelCostStep>>(m, "FuelCostStep")
.def(py::init<double>());
// And not using std::shared_ptr
py::class_<HireCostStep, EdgeAttributePipelineStep>(m, "HireCostStep")
.def(py::init<double>());
py::class_<EdgeAttributePipeline>(m, "EdgeAttributePipeline")
.def(py::init<std::vector<std::shared_ptr<EdgeAttributePipelineStep>>>());
}
However both of these attempts resulted in the same error:
>>> EdgeAttributePipeline([FuelCostStep(10)])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Unable to load a custom holder type from a default-holder instance
>>> EdgeAttributePipeline([HireCostStep(10)])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Unable to load a custom holder type from a default-holder instance
I also had a look at making a trampoline class:
class PyEdgeAttributePipelineStep : public EdgeAttributePipelineStep {
public:
using EdgeAttributePipelineStep::EdgeAttributePipelineStep;
};
And modifying the pybind code:
py::class_<EdgeAttributePipelineStep, PyEdgeAttributePipelineStep, std::shared_ptr<EdgeAttributePipelineStep>>(m, "EdgeAttribtuePipelineStep");
py::class_<FuelCostStep, EdgeAttributePipelineStep>(m, "FuelCostStep")
.def(py::init<double>());
However this now makes a new error when ran in python:
ImportError: generic_type: type "FuelCostStep" does not have a non-default holder type while its base "EdgeAttributePipelineStep" does
I am a bit stuck trying to implement this. How do I get this code to work?
CodePudding user response:
You need to declare the same holder for all related types (so the base, child, grandchild, etc). You forgot to set a shared_ptr holder for HireCostStep
.
FWIW, I use the smart_holder branch of pybind11 and it removes all of this holder related pain. I don't understand why it isn't in master by now.