According to this question/answer in stackoverflow, it is not possible to directly rewrite C++ nested typedefs in cython. I have such a problem and I don't know which is the right/optimal way to proceed.
Let me be more specific with an example. Below, you can find the content of two C++ files (one header.h and one .cpp) and of two corresponding cython files (one .pxd and one .pyx). In the C++ header file called cpp_graph.h
you can see nested typedef declarations; for example, that corresponding to Graph::iterator::nn_iterator
. I don't know how to expose that in the corresponding graph.pxd
file. Or, in other words, I don't know what is the "official" or "standard" way to do it.
Some relevant information. If you check the cython wrapper for STL you can find nested typedefs. For example here in the utility.pxd
or here in the vector.pxd
file. However, those nested usages of ctypedef
are used for template declarations only. Is it the case that nested typedefs work for template declarations only in cython?
The C++ header file:
// file : cpp_graph.h
#ifndef Included_cpp_graph
#define Included_cpp_graph
#include <cassert>
#include <cstddef>
#include <set>
#include <map>
#include <iostream>
#include <string>
#include <sstream>
#include "to_string_patch.h"
#ifndef Defined_bint
#define Defined_bint
typedef int bint;
#endif
class Graph {
public:
typedef std::set< int > t_nn;
typedef std::set< int >::iterator nn_iterator;
typedef std::map< int , t_nn > t_node_to_nn;
class iterator
{
// To iterate over nodes.
friend class Graph;
public:
typedef iterator self_type;
typedef int value_type;
typedef int & reference;
typedef int * pointer;
typedef t_node_to_nn::iterator map_iterator;
typedef std::forward_iterator_tag iterator_category;
iterator( map_iterator map_it ) : _map_it( map_it ) { }
self_type operator++() { _map_it++; return *this; } // PREFIX
self_type operator++(int junk) { self_type i = *this; _map_it++; return i; } // POSTFIX
value_type operator*() { return ( * _map_it ).first; } // Return the index "i"
Graph::t_nn * operator->() { return & ( * _map_it ).second; } // Return a pointer to the contained t_nn.
bool operator==( const self_type & rhs ) { return _map_it == rhs._map_it; }
bool operator!=( const self_type & rhs ) { return _map_it != rhs._map_it; }
private:
map_iterator _map_it;
};
class const_iterator
{
friend class Vertex;
public:
typedef const_iterator self_type;
typedef int value_type;
typedef int & reference;
typedef int * pointer;
typedef t_node_to_nn::iterator map_iterator;
typedef std::forward_iterator_tag iterator_category;
const_iterator( map_iterator map_it ) : _map_it( map_it ) { }
self_type operator++() { _map_it++; return *this; } // PREFIX
self_type operator++(int junk) { self_type i = *this; _map_it++; return i; } // POSTFIX
const value_type operator*() { return ( * _map_it ).first; } // Return the index "i"
const Graph::t_nn * operator->() { return & ( * _map_it ).second; } // Return a pointer to the contained t_nn.
bool operator==( const self_type& rhs ) { return _map_it == rhs._map_it; }
bool operator!=( const self_type& rhs ) { return _map_it != rhs._map_it; }
private:
map_iterator _map_it;
};
iterator begin() { _node_to_nn.begin(); }
iterator end() { _node_to_nn.end(); }
const_iterator begin() const { _node_to_nn.begin(); }
const_iterator end() const { _node_to_nn.end(); }
nn_iterator nn_begin( int i ) { assert( has_node( i ) ); return _node_to_nn[ i ].begin(); }
nn_iterator nn_end( int i ) { assert( has_node( i ) ); return _node_to_nn[ i ].end(); }
Graph() : _num_links( 0 ) {}
~Graph() { _node_to_nn.clear(); _num_links = 0; }
Graph & subgraph( std::set< int > & nodes ) {
Graph * S = new Graph();
for ( std::set< int >::iterator n_it = nodes.begin() ; n_it != nodes.end() ; n_it++ ) {
int i = ( * n_it );
assert( has_node( i ) );
for ( nn_iterator j_it = nn_begin( i ) ; j_it != nn_end( i ) ; j_it++ ) {
int j = ( * j_it );
if ( nodes.count( j ) > 0 ) { S -> add_link( i , j ); }
}
}
return ( * S );
}
int num_nodes() { return _node_to_nn.size(); }
int num_links() { return _num_links; }
int degree( int i ) { return _node_to_nn[ i ].size(); }
double avrg_degree() { return ( ( double ) 2 * num_nodes() ) / ( ( double ) _num_links ); }
bool has_node( int i ) { return _node_to_nn.count( i ) > 0; }
bool has_nn( int i , int j ) {
if ( has_node( i ) ) { return _node_to_nn[ i ].count( j ) > 0; }
return false;
}
bool has_link( int i , int j ) { return has_nn( i , j ); }
void add_node( int i ) { _node_to_nn[ i ].count( 0 ); } // Trick...
void add_link( int i , int j ) {
if ( has_link( i , j ) ) { return; }
_node_to_nn[ i ].insert( j );
_node_to_nn[ j ].insert( i );
_num_links += 1;
}
void del_link( int i , int j ) {
if ( has_link( i , j ) ) {
_node_to_nn[ i ].erase( j );
_node_to_nn[ j ].erase( i );
_num_links -= 1;
}
}
void del_node( int i ) {
iterator i_it = _node_to_nn.find( i );
for( nn_iterator j_it = i_it -> begin() ; j_it != i_it -> end() ; j_it++ ) { del_link( i , ( * j_it ) ); }
_node_to_nn.erase( i_it._map_it );
}
void clear_node( int i ) { del_node( i ); add_node( i ); } // Trick...
private:
t_node_to_nn _node_to_nn;
int _num_links;
};
std::ostream& operator<<( std::ostream& os , Graph & G );
typedef Graph::t_nn Graph_t_nn
typedef
#endif // Included_cpp_graph
The C++ .cpp file:
// cpp_graph.cpp
#include <cassert>
#include <cstddef>
#include <set>
#include <map>
#include <iostream>
#include <string>
#include <sstream>
#include "to_string_patch.h"
#include "cpp_graph.h"
std::ostream& operator<<( std::ostream& os , Graph & G ) {
os << "Graph{";
// Print nodes.
for ( Graph::iterator i_it = G.begin() ; i_it != G.end() ; i_it++ ) {
int i = ( * i_it );
os << " " << patch::to_string( i );
}
os << " |";
// Print edges.
for ( Graph::iterator i_it = G.begin() ; i_it != G.end() ; i_it++ ) {
int i = ( * i_it );
for ( Graph::nn_iterator j_it = G.nn_begin( i ) ; j_it != G.nn_end( i ) ; j_it++ ) {
int j = ( * j_it );
if ( i < j ) { os << " " + patch::to_string( i ) << ":" << patch::to_string( j ); }
}
}
os << " }"; // << std::endl;
return os;
}
// === For testing purposes ===.
/*
int main() {
Graph G;
G.add_link( 1 , 2 );
G.add_link( 1 , 3 );
G.add_link( 2 , 3 );
G.add_link( 3 , 4 );
G.add_link( 4 , 5 );
G.add_link( 4 , 6 );
G.add_link( 5 , 6 );
std::cout << G << std::endl;
G.del_link( 3 , 4 );
std::cout << G << std::endl;
G.del_node( 3 );
std::cout << G << std::endl;
G.clear_node( 2 );
std::cout << G << std::endl;
G.add_link( 100 , 101 );
std::cout << G << std::endl;
std::cout << "N = " << G.num_nodes() << " M = " << G.num_links() << std::endl;
}
*/
The cython .pxd file:
# file : graph.pxd
# === Cython cimports ===
from libcpp cimport bool
from libcpp.set cimport set as cset
from libcpp.map cimport map as cmap
from cython.operator cimport dereference as deref, preincrement as inc
# === Exposing the C++ Graph class ===
cdef extern from "cpp_graph.h":
cdef cppclass Graph:
#public:
ctypedef cset[ int ] t_nn
ctypedef cset[ int ].iterator nn_iterator
ctypedef cmap[ int , t_nn ] t_node_to_nn
cppclass iterator:
#friend class Graph;
#public:
typedef iterator self_type
typedef int value_type
typedef int & reference
typedef int * pointer
typedef t_node_to_nn::iterator map_iterator
typedef std::forward_iterator_tag iterator_category
iterator( map_iterator map_it )
self_type operator++()
self_type operator++(int junk)
value_type operator*()
Graph::t_nn * operator->()
bool operator==( const self_type & rhs )
bool operator!=( const self_type & rhs )
#private:
# map_iterator _map_it;
cppclass const_iterator:
#friend class Vertex;
#public:
typedef const_iterator self_type
typedef int value_type
typedef int & reference
typedef int * pointer
typedef t_node_to_nn::iterator map_iterator
typedef std::forward_iterator_tag iterator_category
const_iterator( map_iterator map_it )
self_type operator++()
self_type operator++(int junk)
const value_type operator*()
const Graph::t_nn * operator->()
bool operator==( const self_type& rhs )
bool operator!=( const self_type& rhs )
#private:
# map_iterator _map_it;
iterator begin()
iterator end()
const_iterator begin() const
const_iterator end() const
nn_iterator nn_begin( int i )
nn_iterator nn_end( int i )
Graph()
~Graph()
Graph & subgraph( std::set< int > & nodes )
int num_nodes()
int num_links()
int degree( int i )
double avrg_degree()
bool has_node( int i )
bool has_nn( int i , int j )
bool has_link( int i , int j )
void add_node( int i )
void add_link( int i , int j )
void del_link( int i , int j )
void del_node( int i )
void clear_node( int i )
#private:
#t_node_to_nn _node_to_nn;
#int _num_links;
std::ostream& operator<<( std::ostream& os , Graph & G )
# === Python Wrapper for the C++ Graph class ===
cdef class PyGraph:
# === Data-members ===
# Pointer to a C++ Graph object.
cdef Graph * _c_graph
# === Function-members ===
# @ graph.pyx
And the cython .pyx file:
# file : graph.pyx
# === Cython cimports ===
from libcpp cimport bool
from libcpp.set cimport set as cset
from libcpp.map cimport map as cmap
from cython.operator cimport dereference as deref, preincrement as inc
# === Ctypedefs for Graph class ===
# @ graph.pxd
# === Exposing the C++ Graph class ===
cdef extern from "cpp_graph2.h":
cdef cppclass Graph:
#public:
ctypedef cset[ int ] t_nn
ctypedef cset[ int ].iterator nn_iterator
ctypedef cmap[ int , t_nn ] t_node_to_nn
cppclass iterator:
#friend class Graph;
#public:
typedef iterator self_type
typedef int value_type
typedef int & reference
typedef int * pointer
typedef t_node_to_nn::iterator map_iterator
typedef std::forward_iterator_tag iterator_category
iterator( map_iterator map_it )
self_type operator++()
self_type operator++(int junk)
value_type operator*()
Graph::t_nn * operator->()
bool operator==( const self_type & rhs )
bool operator!=( const self_type & rhs )
#private:
# map_iterator _map_it;
cppclass const_iterator:
#friend class Vertex;
#public:
typedef const_iterator self_type
typedef int value_type
typedef int & reference
typedef int * pointer
typedef t_node_to_nn::iterator map_iterator
typedef std::forward_iterator_tag iterator_category
const_iterator( map_iterator map_it )
self_type operator++()
self_type operator++(int junk)
const value_type operator*()
const Graph::t_nn * operator->()
bool operator==( const self_type& rhs )
bool operator!=( const self_type& rhs )
#private:
# map_iterator _map_it;
iterator begin()
iterator end()
const_iterator begin() const
const_iterator end() const
nn_iterator nn_begin( int i )
nn_iterator nn_end( int i )
Graph()
~Graph()
Graph & subgraph( std::set< int > & nodes )
int num_nodes()
int num_links()
int degree( int i )
double avrg_degree()
bool has_node( int i )
bool has_nn( int i , int j )
bool has_link( int i , int j )
void add_node( int i )
void add_link( int i , int j )
void del_link( int i , int j )
void del_node( int i )
void clear_node( int i )
#private:
#t_node_to_nn _node_to_nn;
#int _num_links;
# === Python Wrapper for the C++ Graph class ===
cdef class PyGraph:
# === Data-members ===
# @ graph.pxd
# === Function-members ===
def __cinit__( self ):
self._c_graph = new Graph()
def __dealloc__( self ):
del self._c_graph
# TODO : implement the methods for adding and deleting nodes/links.
Finally, when I attempt to compile/build this, I get the following error:
###########################################################
# setup build_ext...
###########################################################
Error compiling Cython file:
------------------------------------------------------------
...
# === Exposing the C++ Graph class ===
cdef extern from "cpp_graph2.h":
cdef cppclass Graph:
#public:
ctypedef cset[ int ] t_nn
^
------------------------------------------------------------
ExcessDegreeModel/graph.pxd:51:8: Expected an identifier, found 'ctypedef'
...