From: Shakeeb A. <sha...@gm...> - 2021-05-25 16:33:33
|
Hi, Summary: I have inserted a basic declaration for a std::pair<double,double> which works in isolation but not in the context of a larger interface file even though there is evidence that the wrapper code has been generated. Problem description: I have a case of swig wrapping c++ code (to generate a python extension) where wrapper code is generated but is somehow inaccessible. The case in point is an effort to wrap the open-source c++ adaptagrams graph drawing library (https://github.com/mjwybrow/adaptagrams), which I managed to successfully use but discovered during testing that the fields (first, second) of a `dimensions` object which is a `typedef` of a `std::pair<double, double>` were not wrapped. Indeed, I checked out the interface file `adaptagrams.i` and noted that it did not contain a declaration to wrap the `dimensions` object: ``` /* This is a SWIG interface file for generating interfaces to the Adpatagrams * libraries for languages other than C++. It has currently been tested from * Java and Python, but could be adapted for other languages. */ /* File : adaptagrams.i */ %module(directors="1") adaptagrams /* Allow target language to subclass and implement virtual methods for * these classes. */ %feature("director") Avoid::Router; %feature("director") cola::TestConvergence; #ifdef SWIGJAVA /* Allow enum values to be logically ORed together as flags. */ %include "enumtypeunsafe.swg" #endif %{ #ifdef SWIGPYTHON #define SWIG_FILE_WITH_INIT #endif #include <vector> #include <libvpsc/rectangle.h> #include <libvpsc/assertions.h> #include <libcola/cola.h> #include <libcola/cluster.h> #include <libcola/convex_hull.h> #include <libcola/compound_constraints.h> #include <libcola/exceptions.h> #include <libtopology/topology_graph.h> #include <libtopology/cola_topology_addon.h> #include <libavoid/libavoid.h> #include <libtopology/orthogonal_topology.h> #include <libdialect/aca.h> #include <libdialect/chains.h> #include <libdialect/commontypes.h> #include <libdialect/constraints.h> #include <libdialect/faces.h> #include <libdialect/graphs.h> #include <libdialect/hola.h> #include <libdialect/io.h> #include <libdialect/nearalign.h> #include <libdialect/nodeconfig.h> #include <libdialect/opts.h> #include <libdialect/ortho.h> #include <libdialect/peeling.h> #include <libdialect/planarise.h> #include <libdialect/routing.h> #include <libdialect/treeplacement.h> #include <libdialect/trees.h> /* Includes the header in the wrapper code */ using namespace Avoid; using namespace cola; using namespace hull; using namespace vpsc; using namespace topology; using namespace dialect; %} %ignore Avoid::XDIM; %ignore Avoid::YDIM; %ignore Avoid::Point::operator[]; %ignore cola::PreIteration::operator(); %ignore cola::PreIteration::locksNotUsed; //%ignore cola::TestConvergence::operator(); %ignore operator<<(std::ostream &os, vpsc::Rectangle const &r); %ignore vpsc::Rectangle::setXBorder(double); %ignore vpsc::Rectangle::setYBorder(double); %ignore vpsc::assertNoOverlaps(const Rectangles& rs); %ignore Avoid::Point::operator==(const Point&) const; %ignore Avoid::Point::operator!=(const Point&) const; %ignore Avoid::Point::operator<(const Point&) const; %ignore Avoid::Point::operator-(const Point&) const; %ignore Avoid::Point::operator+(const Point&) const; %ignore Avoid::ShapeConnectionPin::operator<(const ShapeConnectionPin& rhs) const; %ignore Avoid::ShapeConnectionPin::operator==(const ShapeConnectionPin& rhs) const; %ignore Avoid::CmpConnPinPtr::operator()(const ShapeConnectionPin *lhs, const ShapeConnectionPin *rhs) const; %ignore cola::SubConstraint; %ignore cola::SubConstraintAlternatives; %ignore cola::SubConstraintInfo; %ignore std::vector<cola::SubConstraintInfo>; %ignore cola::NonOverlapConstraints; %ignore cola::Resize::Resize(); %ignore vpsc::Constraint::Constraint(); %ignore topology::Segment; %ignore topology::EdgePoint::inSegment; %ignore topology::EdgePoint::outSegment; %ignore topology::EdgePoint::bendConstraint; %ignore topology::EdgePoint::prune(); %ignore topology::EdgePoint::getBendConstraint(std::vector<TopologyConstraint*>* ts); %ignore topology::EdgePoint::createBendConstraint(); %ignore topology::EdgePoint::deleteBendConstraint(); %ignore topology::Edge::firstSegment; %ignore topology::Edge::lastSegment; %ignore topology::Edge::getTopologyConstraints(std::vector<TopologyConstraint*>* ts) const; %ignore topology::Edge::getRoute() const; %ignore topology::Edge::toString() const; %ignore topology::Edge::toString() const; %ignore topology::assertConvexBends(const Edges&); %ignore topology::assertNoSegmentRectIntersection(const Nodes&, const Edges&); %ignore topology::assertNoZeroLengthEdgeSegments(const Edges& es); %ignore topology::compute_stress(const Edges&); %ignore topology::printEdges(const Edges&); %ignore dialect::Node::operator=(const dialect::Node&); %ignore dialect::Edge::operator=(const dialect::Edge&); %ignore dialect::Graph::operator=(const Graph); %ignore dialect::PeeledNode::operator=(const PeeledNode&); %ignore operator+(const dialect::BoundingBox&, const dialect::BoundingBox&); %ignore operator+(const dialect::ProjSeq&, const dialect::ProjSeq&); %ignore operator==(const dialect::BoundingBox&, const dialect::BoundingBox&); %ignore operator!=(const dialect::BoundingBox&, const dialect::BoundingBox&); %ignore dialect::identifyRootNode(const Graph&); %ignore dialect::negateSepDir(SepDir); %ignore dialect::swap(Graph&, Graph&); %include "std_string.i" %include "std_vector.i" %include "std_deque.i" %include "std_pair.i" %include "std_map.i" %include "std_shared_ptr.i" #ifdef SWIGJAVA /* Wrap every C++ action in try/catch statement so we convert all * possible C++ exceptions (generated from C++ assertion failures) * into Java exceptions. */ %exception { try { $action } catch(vpsc::CriticalFailure cf) { jclass excep = jenv->FindClass("org/adaptagrams/ColaException"); if (excep) jenv->ThrowNew(excep, cf.what().c_str()); } catch(cola::InvalidVariableIndexException ivi) { jclass excep = jenv->FindClass("org/adaptagrams/ColaException"); if (excep) jenv->ThrowNew(excep, ivi.what().c_str()); } } /* Define a Java ColaException class. */ %typemap(javabase) cola::ColaException "java.lang.Exception"; %inline %{ namespace cola { class ColaException { public: ColaException(const std::string& msg) : message(msg) {} std::string getMessage() { return message; } private: std::string message; }; } %} #endif /* We have a problem where in garbage collected languages like Java and Python, * objects that appear to no longer be used and go out of scope can cause * their corresponding internal C++ instances to be freed prematurely. * For this reason we generate empty finialise methods for the following * classes and clean them up later. * * For libavoid, the Router instance takes ownership of these objects and * deletes them when it is freed. For the cola/vpsc classes, a Java/Python * user can call ConstrainedFDLayout::freeAssociatedObjects() to free this * memory. */ %nodefaultdtor vpsc::Rectangle; %nodefaultdtor cola::CompoundConstraint; %nodefaultdtor cola::AlignmentConstraint; %nodefaultdtor cola::BoundaryConstraint; %nodefaultdtor cola::DistributionConstraint; %nodefaultdtor cola::MultiSeparationConstraint; %nodefaultdtor cola::PageBoundaryConstraints; %nodefaultdtor cola::SeparationConstraint; %nodefaultdtor cola::OrthogonalEdgeConstraint; %nodefaultdtor cola::FixedRelativeConstraint; %nodefaultdtor cola::Cluster; %nodefaultdtor cola::RootCluster; %nodefaultdtor cola::ConvexCluster; %nodefaultdtor cola::RectangularCluster; %nodefaultdtor Avoid::ShapeRef; %nodefaultdtor Avoid::ConnRef; %nodefaultdtor Avoid::ClusterRef; %nodefaultdtor Avoid::JunctionRef; %nodefaultdtor Avoid::Obstacle; %nodefaultdtor Avoid::ShapeConnectionPin; %nodefaultdtor cola::PreIteration; %nodefaultdtor cola::TestConvergence; %nodefaultdtor cola::Lock; %nodefaultdtor cola::Resize; %nodefaultdtor topology::Node; %nodefaultdtor topology::Edge; %nodefaultdtor topology::EdgePoint; %nodefaultdtor topology::ColaTopologyAddon; %nodefaultdtor dialect::ACALayout; %nodefaultdtor dialect::Graph; %nodefaultdtor dialect::Node; %nodefaultdtor dialect::Edge; %include "libdialect/commontypes.h" %template(Chars) std::vector<char>; %template(Unsigneds) std::vector<unsigned>; %template(Doubles) std::vector<double>; %template(UnsatisfiableConstraintInfoPtrs) std::vector<cola::UnsatisfiableConstraintInfo *>; %template(ColaEdge) std::pair<unsigned,unsigned>; %template(ColaEdges) std::vector< std::pair<unsigned,unsigned> >; %template(RectanglePtrs) std::vector<vpsc::Rectangle*>; %template(CompoundConstraintPtrs) std::vector<cola::CompoundConstraint*>; %template(ColaLocks) std::vector<cola::Lock>; %template(ColaResizes) std::vector<cola::Resize>; %template(ColaDesiredPositions) std::vector<cola::DesiredPosition>; %template(TopologyEdgePointPtrs) std::vector<topology::EdgePoint*>; %template(TopologyEdgePointConstPtrs) std::vector<const topology::EdgePoint*>; %template(TopologyEdgePtrs) std::vector<topology::Edge*>; %template(TopologyNodePtrs) std::vector<topology::Node*>; %template(ColaClusters) std::vector<cola::Cluster*>; %template(AvoidPoints) std::vector<Avoid::Point>; %template(AvoidCheckpoints) std::vector<Avoid::Checkpoint>; %template(OrderedAlignmentPtrs) std::vector<dialect::OrderedAlignment*>; %template(TreePtrs) std::vector<std::shared_ptr<dialect::Tree>>; %template(ChainPtrs) std::vector<std::shared_ptr<dialect::Chain>>; %template(DialectNodes) std::vector<std::shared_ptr<dialect::Node>>; %template(DialectNodeLookup) std::map<dialect::id_type, std::shared_ptr<dialect::Node>>; %template(DialectNodeDeques) std::vector<std::deque<std::shared_ptr<dialect::Node>>>; %inline %{ void doubleArraySet(double *a, int i, double val) { a[i] = val; } double doubleArrayGet(const double *a, int i) { return a[i]; } double *newDoubleArray(int size) { return new double[size]; } void deleteDoubleArray(double* a) { delete a; } %} %rename(ColaTestConvergenceOperator) cola::TestConvergence::operator(); %rename(AvoidBox) Avoid::Box; %rename(AvoidEdge) Avoid::Edge; %rename(AvoidRectangle) Avoid::Rectangle; %rename(AvoidTopologyAddonInterface) Avoid::TopologyAddonInterface; %rename(getVarConst) topology::Node::getVar() const; %rename(DialectNode) dialect::Node; %rename(DialectEdge) dialect::Edge; %shared_ptr(dialect::Node) %shared_ptr(dialect::GhostNode) %shared_ptr(dialect::PeeledNode) %shared_ptr(dialect::Graph) %shared_ptr(dialect::Tree) %shared_ptr(dialect::Side) %shared_ptr(dialect::Nexus) %shared_ptr(dialect::Face) %shared_ptr(dialect::FaceSet) %shared_ptr(dialect::TreePlacement) %shared_ptr(dialect::Chain) /* %warnfilter(315) dialect::Edge; */ %ignore dialect::Edge::m_tgt; /* Parse the header file to generate wrappers */ %include "libvpsc/rectangle.h" %include "libvpsc/assertions.h" %include "libcola/compound_constraints.h" %include "libcola/cola.h" %include "libcola/cluster.h" %include "libcola/convex_hull.h" %include "libcola/exceptions.h" %include "libavoid/dllexport.h" %include "libavoid/geometry.h" %include "libavoid/geomtypes.h" %include "libavoid/connend.h" %include "libavoid/router.h" %include "libavoid/connector.h" %include "libavoid/obstacle.h" %include "libavoid/shape.h" %include "libavoid/junction.h" %include "libavoid/viscluster.h" %include "libavoid/connectionpin.h" %include "libtopology/topology_graph.h" %include "libtopology/cola_topology_addon.h" %include "libtopology/orthogonal_topology.h" %include "libdialect/aca.h" %include "libdialect/chains.h" %include "libdialect/constraints.h" %include "libdialect/faces.h" %include "libdialect/graphs.h" %include "libdialect/hola.h" %include "libdialect/io.h" %include "libdialect/nearalign.h" %include "libdialect/nodeconfig.h" %include "libdialect/opts.h" %include "libdialect/ortho.h" %include "libdialect/peeling.h" %include "libdialect/planarise.h" %include "libdialect/routing.h" %include "libdialect/treeplacement.h" %include "libdialect/trees.h" ``` I then inserted the following after the `%template` declarations: ``` %template() std::pair <double,double>; %template(dimensions) std::pair <double,double>; ``` I checked that the above worked after testing that it successfully worked in an isolated case of swig wrapping a `typedef` of std::pair<double, double> in a `dialect::` namespace. Note that the successful isolated test case used the following interface declaration: ``` %module test %{ #include "eg.h" %} %include "eg.h" %include "std_pair.i" %template() std::pair <double,double>; %template(dimensions) std::pair <double,double>; ``` Back to the `adaptagrams.i` interface declaration, although I was able to compile successfully and pass all tests, when I tried to import anything from the generated python extension it would give the following error: ```python In [1]: from adaptagrams import DialectNode --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-1-feb817791df2> in <module> ----> 1 from adaptagrams import DialectNode /usr/local/lib/python3.9/site-packages/adaptagrams.py in <module> 2593 _adaptagrams.DialectNodeDeques_swigregister(DialectNodeDeques) 2594 -> 2595 class dimensions(object): 2596 thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") 2597 __repr__ = _swig_repr /usr/local/lib/python3.9/site-packages/adaptagrams.py in dimensions() 2599 def __init__(self, *args): 2600 _adaptagrams.dimensions_swiginit(self, _adaptagrams.new_dimensions(*args)) -> 2601 first = property(_adaptagrams.dimensions_first_get, _adaptagrams.dimensions_first_set) 2602 second = property(_adaptagrams.dimensions_second_get, _adaptagrams.dimensions_second_set) 2603 def __len__(self): AttributeError: module '_adaptagrams' has no attribute 'dimensions_first_get' ``` This strange because when I search for `dimensions_first_get` in `adaptagrams_wrap.cxx` I get the expected: ```c++ SWIGINTERN PyObject *_wrap_dimensions_first_get(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; std::pair< double,double > *arg1 = (std::pair< double,double > *) 0 ; void *argp1 = 0 ; int res1 = 0 ; PyObject *swig_obj[1] ; double result; if (!args) SWIG_fail; swig_obj[0] = args; res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_std__pairT_double_double_t, 0 | 0 ); if (!SWIG_IsOK(res1)) { SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "dimensions_first_get" "', argument " "1"" of type '" "std::pair< double,double > *""'"); } arg1 = reinterpret_cast< std::pair< double,double > * >(argp1); result = (double) ((arg1)->first); resultobj = SWIG_From_double(static_cast< double >(result)); return resultobj; fail: return NULL; ``` and it also is in the method list: ```c++ static PyMethodDef SwigMethods[] = { //... { "dimensions_first_get", _wrap_dimensions_first_get, METH_O, NULL}, ``` So it seems properly generated but still not available in the extension! Any help to resolve this mystery would be greatly appreciated. S |