[refactor] more understandable function names

Clearing confusions between pxd and pyx classes and objects!
release/4.3a0
Duy-Nguyen Ta 2016-11-22 12:13:33 -05:00
parent 52a85f23f8
commit 74f80fea4f
13 changed files with 84 additions and 84 deletions

View File

@ -4,14 +4,14 @@ TODO:
☐ Global functions
☐ wrap VariableIndex: why is it in inference? If need to, shouldn't have constructors to specific FactorGraphs
☐ [REFACTOR] better name for uninstantiateClass: very vague!!
☐ [REFACTOR] typesEqual --> equalSignature
☐ Unify cython/gtsam.h and the original gtsam.h
☐ CMake install script
Completed/Cancelled:
✔ Proper overloads (constructors, static methods, methods)
✔ [REFACTOR] typesEqual --> isSameSignature @done (22-11-16 21:00)
✔ Proper overloads (constructors, static methods, methods) @done (20-11-16 21:00)
✔ Allow overloading methods. The current solution is annoying!!! @done (20-11-16 21:00)
✔ Casting from parent and grandparents
✔ Casting from parent and grandparents @done (16-11-16 17:00)
✔ Allow overloading constructors. The current solution is annoying!!! @done (16-11-16 17:00)
✔ Support "print obj" @done (16-11-16 17:00)
✔ methods for FastVector: at, [], ... @done (16-11-16 17:00)
@ -36,7 +36,7 @@ Completed/Cancelled:
- inference before symbolic/linear
- what's the purpose of "virtual" ??
- Need default constructor and default copy constructor for almost every class... :(
- Need default constructor and default copy constructor for almost every classes... :(
☐ support these constructors by default and declare "delete" for special classes?
Installation:

View File

@ -100,7 +100,7 @@ void Argument::proxy_check(FileWriter& proxyFile, const string& s) const {
/* ************************************************************************* */
void Argument::emit_cython_pxd(FileWriter& file, const std::string& className) const {
string typeName = type.cythonClass();
string typeName = type.pxdClassName();
if (typeName == "This") typeName = className;
string cythonType = typeName;
@ -117,15 +117,15 @@ void Argument::emit_cython_pxd(FileWriter& file, const std::string& className) c
/* ************************************************************************* */
void Argument::emit_cython_pyx(FileWriter& file) const {
file.oss << type.pythonArgumentType() << " " << name;
file.oss << type.pyxArgumentType() << " " << name;
}
/* ************************************************************************* */
std::string Argument::pyx_asParam() const {
string cythonType = type.cythonClass();
string cythonType = type.pxdClassName();
string cythonVar;
if (type.isNonBasicType()) {
cythonVar = name + "." + type.pyxCythonObj();
cythonVar = name + "." + type.shared_pxd_obj_in_pyx();
if (!is_ptr) cythonVar = "deref(" + cythonVar + ")";
} else if (type.isEigen()) {
cythonVar = "<" + cythonType + ">" + "(Map[" + cythonType + "](" + name + "))";
@ -258,7 +258,7 @@ std::string ArgumentList::pyx_castParamsToPythonType() const {
// cast params to their correct python argument type to pass in the function call later
string s;
for (size_t j = 0; j < size(); ++j)
s += "\t\t\t" + at(j).name + " = <" + at(j).type.pythonArgumentType()
s += "\t\t\t" + at(j).name + " = <" + at(j).type.pyxArgumentType()
+ ">(__params['" + at(j).name + "'])\n";
return s;
}

View File

@ -39,7 +39,7 @@ struct Argument {
type(t), name(n), is_const(false), is_ref(false), is_ptr(false) {
}
bool typesEqual(const Argument& other) const {
bool isSameSignature(const Argument& other) const {
return type == other.type
&& is_const == other.is_const && is_ref == other.is_ref
&& is_ptr == other.is_ptr;
@ -98,9 +98,9 @@ struct ArgumentList: public std::vector<Argument> {
ArgumentList expandTemplate(const TemplateSubstitution& ts) const;
bool typesEqual(const ArgumentList& other) const {
bool isSameSignature(const ArgumentList& other) const {
for(size_t i = 0; i<size(); ++i)
if (!at(i).typesEqual(other[i])) return false;
if (!at(i).isSameSignature(other[i])) return false;
return true;
}

View File

@ -476,7 +476,7 @@ void Class::removeInheritedNontemplateMethods(vector<Class>& classes) {
[&](boost::tuple<ReturnValue, ArgumentList> const&
parentOverload) {
return overload.get<0>() == parentOverload.get<0>() &&
overload.get<1>().typesEqual(parentOverload.get<1>());
overload.get<1>().isSameSignature(parentOverload.get<1>());
}) != parentMethodOverloads.end();
return found;
});
@ -732,7 +732,7 @@ void Class::python_wrapper(FileWriter& wrapperFile) const {
void Class::emit_cython_pxd(FileWriter& pxdFile, const std::vector<Class>& allClasses) const {
pxdFile.oss << "cdef extern from \"" << includeFile << "\" namespace \""
<< qualifiedNamespaces("::") << "\":" << endl;
pxdFile.oss << "\tcdef cppclass " << cythonClass() << " \"" << qualifiedName("::") << "\"";
pxdFile.oss << "\tcdef cppclass " << pxdClassName() << " \"" << qualifiedName("::") << "\"";
if (templateArgs.size()>0) {
pxdFile.oss << "[";
for(size_t i = 0; i<templateArgs.size(); ++i) {
@ -741,10 +741,10 @@ void Class::emit_cython_pxd(FileWriter& pxdFile, const std::vector<Class>& allCl
}
pxdFile.oss << "]";
}
if (parentClass) pxdFile.oss << "(" << parentClass->cythonClass() << ")";
if (parentClass) pxdFile.oss << "(" << parentClass->pxdClassName() << ")";
pxdFile.oss << ":\n";
constructor.emit_cython_pxd(pxdFile, cythonClass());
constructor.emit_cython_pxd(pxdFile, pxdClassName());
if (constructor.nrOverloads()>0) pxdFile.oss << "\n";
for(const StaticMethod& m: static_methods | boost::adaptors::map_values)
@ -767,18 +767,18 @@ void Class::pyxInitParentObj(FileWriter& pyxFile, const std::string& pyObj,
const std::string& cySharedObj,
const std::vector<Class>& allClasses) const {
if (parentClass) {
pyxFile.oss << pyObj << "." << parentClass->pyxCythonObj() << " = "
<< "<" << parentClass->pyxSharedCythonClass() << ">("
pyxFile.oss << pyObj << "." << parentClass->shared_pxd_obj_in_pyx() << " = "
<< "<" << parentClass->shared_pxd_class_in_pyx() << ">("
<< cySharedObj << ")\n";
// Find the parent class with name "parentClass" and point its cython obj
// to the same pointer
auto parent_it = find_if(allClasses.begin(), allClasses.end(),
[this](const Class& cls) {
return cls.cythonClass() ==
this->parentClass->cythonClass();
return cls.pxdClassName() ==
this->parentClass->pxdClassName();
});
if (parent_it == allClasses.end()) {
cerr << "Can't find parent class: " << parentClass->cythonClass();
cerr << "Can't find parent class: " << parentClass->pxdClassName();
throw std::runtime_error("Parent class not found!");
}
parent_it->pyxInitParentObj(pyxFile, pyObj, cySharedObj, allClasses);
@ -788,25 +788,25 @@ void Class::pyxInitParentObj(FileWriter& pyxFile, const std::string& pyObj,
/* ************************************************************************* */
void Class::pyxDynamicCast(FileWriter& pyxFile, const Class& curLevel,
const std::vector<Class>& allClasses) const {
std::string me = this->pythonClass(), sharedMe = this->pyxSharedCythonClass();
std::string me = this->pyxClassName(), sharedMe = this->shared_pxd_class_in_pyx();
if (curLevel.parentClass) {
std::string parent = curLevel.parentClass->pythonClass(),
parentObj = curLevel.parentClass->pyxCythonObj(),
parentCythonClass = curLevel.parentClass->pyxCythonClass();
std::string parent = curLevel.parentClass->pyxClassName(),
parentObj = curLevel.parentClass->shared_pxd_obj_in_pyx(),
parentCythonClass = curLevel.parentClass->pxd_class_in_pyx();
pyxFile.oss << "def dynamic_cast_" << me << "_" << parent << "(" << parent
<< " parent):\n";
pyxFile.oss << "\treturn " << me << ".cyCreateFromShared(<" << sharedMe
<< ">dynamic_pointer_cast[" << pyxCythonClass() << ","
<< ">dynamic_pointer_cast[" << pxd_class_in_pyx() << ","
<< parentCythonClass << "](parent." << parentObj
<< "))\n";
// Move up higher to one level: Find the parent class with name "parentClass"
auto parent_it = find_if(allClasses.begin(), allClasses.end(),
[&curLevel](const Class& cls) {
return cls.cythonClass() ==
curLevel.parentClass->cythonClass();
return cls.pxdClassName() ==
curLevel.parentClass->pxdClassName();
});
if (parent_it == allClasses.end()) {
cerr << "Can't find parent class: " << parentClass->cythonClass();
cerr << "Can't find parent class: " << parentClass->pxdClassName();
throw std::runtime_error("Parent class not found!");
}
pyxDynamicCast(pyxFile, *parent_it, allClasses);
@ -815,29 +815,29 @@ void Class::pyxDynamicCast(FileWriter& pyxFile, const Class& curLevel,
/* ************************************************************************* */
void Class::emit_cython_pyx(FileWriter& pyxFile, const std::vector<Class>& allClasses) const {
pyxFile.oss << "cdef class " << pythonClass();
if (parentClass) pyxFile.oss << "(" << parentClass->pythonClass() << ")";
pyxFile.oss << "cdef class " << pyxClassName();
if (parentClass) pyxFile.oss << "(" << parentClass->pyxClassName() << ")";
pyxFile.oss << ":\n";
// shared variable of the corresponding cython object
pyxFile.oss << "\tcdef " << pyxSharedCythonClass() << " " << pyxCythonObj() << "\n";
pyxFile.oss << "\tcdef " << shared_pxd_class_in_pyx() << " " << shared_pxd_obj_in_pyx() << "\n";
// __cinit___
pyxFile.oss << "\tdef __cinit__(self, *args, **kwargs):\n"
"\t\tself." << pyxCythonObj() << " = "
<< pyxSharedCythonClass() << "()\n";
"\t\tself." << shared_pxd_obj_in_pyx() << " = "
<< shared_pxd_class_in_pyx() << "()\n";
pyxFile.oss << constructor.pyx_checkDuplicateNargsKwArgs();
for (size_t i = 0; i<constructor.nrOverloads(); ++i) {
pyxFile.oss << "\t\t" << (i == 0 ? "if" : "elif") << " self."
<< pythonClass() << "_" << i
<< pyxClassName() << "_" << i
<< "(*args, **kwargs):\n\t\t\tpass\n";
}
if (constructor.nrOverloads()>0)
pyxFile.oss << "\t\telse:\n\t\t\traise TypeError('" << pythonClass()
pyxFile.oss << "\t\telse:\n\t\t\traise TypeError('" << pyxClassName()
<< " construction failed!')\n";
pyxInitParentObj(pyxFile, "\t\tself", "self." + pyxCythonObj(), allClasses);
pyxInitParentObj(pyxFile, "\t\tself", "self." + shared_pxd_obj_in_pyx(), allClasses);
pyxFile.oss << "\n";
// Constructors
@ -846,10 +846,10 @@ void Class::emit_cython_pyx(FileWriter& pyxFile, const std::vector<Class>& allCl
// cyCreateFromShared
pyxFile.oss << "\t@staticmethod\n";
pyxFile.oss << "\tcdef " << pythonClass() << " cyCreateFromShared(const "
<< pyxSharedCythonClass() << "& other):\n"
<< "\t\tcdef " << pythonClass() << " ret = " << pythonClass() << "()\n"
<< "\t\tret." << pyxCythonObj() << " = other\n";
pyxFile.oss << "\tcdef " << pyxClassName() << " cyCreateFromShared(const "
<< shared_pxd_class_in_pyx() << "& other):\n"
<< "\t\tcdef " << pyxClassName() << " ret = " << pyxClassName() << "()\n"
<< "\t\tret." << shared_pxd_obj_in_pyx() << " = other\n";
pyxInitParentObj(pyxFile, "\t\tret", "other", allClasses);
pyxFile.oss << "\t\treturn ret" << "\n";

View File

@ -149,8 +149,8 @@ void Constructor::emit_cython_pyx(FileWriter& pyxFile, const Class& cls) const {
"(self, *args, **kwargs):\n";
pyxFile.oss << pyx_resolveOverloadParams(args, true);
pyxFile.oss << "\t\tself." << cls.pyxCythonObj() << " = "
<< cls.pyxSharedCythonClass() << "(new " << cls.pyxCythonClass()
pyxFile.oss << "\t\tself." << cls.shared_pxd_obj_in_pyx() << " = "
<< cls.shared_pxd_class_in_pyx() << "(new " << cls.pxd_class_in_pyx()
<< "(" << args.pyx_asParams() << "))\n";
pyxFile.oss << "\t\treturn True\n\n";
}

View File

@ -95,10 +95,10 @@ string Method::wrapper_call(FileWriter& wrapperFile, Str cppClassName,
void Method::emit_cython_pxd(FileWriter& file, const Class& cls) const {
for (size_t i = 0; i < nrOverloads(); ++i) {
file.oss << "\t\t";
returnVals_[i].emit_cython_pxd(file, cls.cythonClass());
returnVals_[i].emit_cython_pxd(file, cls.pxdClassName());
file.oss << pyRename(name_) + " \"" + name_ + "\""
<< "(";
argumentList(i).emit_cython_pxd(file, cls.cythonClass());
argumentList(i).emit_cython_pxd(file, cls.pxdClassName());
file.oss << ")";
if (is_const_) file.oss << " const";
file.oss << "\n";
@ -125,7 +125,7 @@ void Method::emit_cython_pyx_no_overload(FileWriter& file,
file.oss << "):\n";
/// Call cython corresponding function and return
string caller = "self." + cls.pyxCythonObj() + ".get()";
string caller = "self." + cls.shared_pxd_obj_in_pyx() + ".get()";
string ret = pyx_functionCall(caller, funcName, 0);
if (!returnVals_[0].isVoid()) {
file.oss << "\t\tcdef " << returnVals_[0].pyx_returnType()
@ -169,7 +169,7 @@ void Method::emit_cython_pyx(FileWriter& file, const Class& cls) const {
file.oss << pyx_resolveOverloadParams(args, false); // lazy: always return None even if it's a void function
/// Call cython corresponding function
string caller = "self." + cls.pyxCythonObj() + ".get()";
string caller = "self." + cls.shared_pxd_obj_in_pyx() + ".get()";
string ret = pyx_functionCall(caller, funcName, i);
if (!returnVals_[i].isVoid()) {

View File

@ -145,13 +145,13 @@ std::string MethodBase::pyx_functionCall(
string ret;
if (!returnVals_[iOverload].isPair && !returnVals_[iOverload].type1.isPtr &&
returnVals_[iOverload].type1.isNonBasicType()) {
ret = returnVals_[iOverload].type1.pyxSharedCythonClass() + "(new " +
returnVals_[iOverload].type1.pyxCythonClass() + "(";
ret = returnVals_[iOverload].type1.shared_pxd_class_in_pyx() + "(new " +
returnVals_[iOverload].type1.pxd_class_in_pyx() + "(";
}
// actual function call ...
ret += caller + "." + funcName;
if (templateArgValue_) ret += "[" + templateArgValue_->pyxCythonClass() + "]";
if (templateArgValue_) ret += "[" + templateArgValue_->pxd_class_in_pyx() + "]";
//... with argument list
ret += "(" + argumentList(iOverload).pyx_asParams() + ")";

View File

@ -353,14 +353,14 @@ void Module::emit_cython_pxd(FileWriter& pxdFile) const {
for (const Class& expCls : expandedClasses) {
if (!expCls.templateClass || expCls.templateClass->name_ != cls.name_)
continue;
pxdFile.oss << "ctypedef " << expCls.templateClass->cythonClass()
pxdFile.oss << "ctypedef " << expCls.templateClass->pxdClassName()
<< "[";
for (size_t i = 0; i < expCls.templateInstTypeList.size(); ++i)
pxdFile.oss << expCls.templateInstTypeList[i].cythonClass()
pxdFile.oss << expCls.templateInstTypeList[i].pxdClassName()
<< ((i == expCls.templateInstTypeList.size() - 1)
? ""
: ", ");
pxdFile.oss << "] " << expCls.cythonClass() << "\n";
pxdFile.oss << "] " << expCls.pxdClassName() << "\n";
}
pxdFile.oss << "\n\n";
}
@ -372,12 +372,12 @@ void Module::emit_cython_pyx(FileWriter& pyxFile) const {
// headers...
string pxdHeader = name + "_wrapper";
pyxFile.oss << "cimport numpy as np\n"
"cimport " << pxdHeader << " as " << name << "\n"
"cimport " << pxdHeader << " as " << "pxd" << "\n"
"from "<< pxdHeader << " cimport shared_ptr\n"
"from "<< pxdHeader << " cimport dynamic_pointer_cast\n";
// import all typedefs, e.g. from gtsam cimport Key, so we don't need to say gtsam.Key
// import all typedefs, e.g. from gtsam_wrapper cimport Key, so we don't need to say gtsam.Key
for(const Qualified& q: Qualified::BasicTypedefs) {
pyxFile.oss << "from " << pxdHeader << " cimport " << q.cythonClass() << "\n";
pyxFile.oss << "from " << pxdHeader << " cimport " << q.pxdClassName() << "\n";
}
pyxFile.oss << "from eigency.core cimport *\n"
"from libcpp cimport bool\n\n"

View File

@ -185,10 +185,10 @@ public:
return result;
}
/// name of the Cython class in pxd
/// name of Cython classes in pxd
/// Normal classes: innerNamespace_ClassName, e.g. GaussianFactor, noiseModel_Gaussian
/// Eigen type: Vector --> VectorXd, Matrix --> MatrixXd
std::string cythonClass() const {
std::string pxdClassName() const {
if (isEigen())
return name_ + "Xd";
else
@ -198,17 +198,17 @@ public:
/// name of Python classes in pyx
/// They have the same name with the corresponding Cython classes in pxd
/// But note that they are different: These are Python classes in the pyx file
/// To refer to a Cython class in pyx, we need to add "gtsam.", e.g. gtsam.noiseModel_Gaussian
/// see the other function pyxCythoClass for that purpose.
std::string pythonClass() const {
return cythonClass();
/// To refer to a Cython class in pyx, we need to add "pxd.", e.g. pxd.noiseModel_Gaussian
/// see the other function pxd_class_in_pyx for that purpose.
std::string pyxClassName() const {
return pxdClassName();
}
/// Python type of function arguments in pyx to interface with normal python scripts
/// Eigen types become np.ndarray (There's no Eigen types, e.g. VectorXd, in
/// Python. We have to pass in numpy array in the arguments, which will then be
/// converted to Eigen types in Cython)
std::string pythonArgumentType() const {
std::string pyxArgumentType() const {
if (isEigen())
return "np.ndarray";
else
@ -216,12 +216,12 @@ public:
}
/// return the Cython class in pxd corresponding to a Python class in pyx
std::string pyxCythonClass() const {
std::string pxd_class_in_pyx() const {
if (isNonBasicType()) {
if (namespaces_.size() > 0)
return namespaces_[0] + "." + cythonClass();
return "pxd." + pxdClassName();
else {
std::cerr << "Class without namespace: " << cythonClass() << std::endl;
std::cerr << "Class without namespace: " << pxdClassName() << std::endl;
throw std::runtime_error("Error: User type without namespace!!");
}
}
@ -232,12 +232,12 @@ public:
}
/// the internal Cython shared obj in a Python class wrappper
std::string pyxCythonObj() const {
return "gt" + cythonClass() + "_";
std::string shared_pxd_obj_in_pyx() const {
return "shared_pxd_" + pxdClassName() + "_";
}
std::string pyxSharedCythonClass() const {
return "shared_ptr[" + pyxCythonClass() + "]";
std::string shared_pxd_class_in_pyx() const {
return "shared_ptr[" + pxd_class_in_pyx() + "]";
}
friend std::ostream& operator<<(std::ostream& os, const Qualified& q) {

View File

@ -68,7 +68,7 @@ void ReturnType::wrapTypeUnwrap(FileWriter& wrapperFile) const {
/* ************************************************************************* */
void ReturnType::emit_cython_pxd(FileWriter& file,
const std::string& className) const {
string typeName = cythonClass();
string typeName = pxdClassName();
string cythonType;
if (isPtr)
cythonType = "shared_ptr[" + typeName + "]";
@ -80,7 +80,7 @@ void ReturnType::emit_cython_pxd(FileWriter& file,
/* ************************************************************************* */
std::string ReturnType::pyx_returnType(bool addShared) const {
string retType = pyxCythonClass();
string retType = pxd_class_in_pyx();
if (isPtr || (isNonBasicType() && addShared))
retType = "shared_ptr[" + retType + "]";
return retType;
@ -93,11 +93,11 @@ std::string ReturnType::pyx_casting(const std::string& var,
return "ndarray_copy(" + var + ")";
else if (isNonBasicType()) {
if (isPtr || isSharedVar)
return pythonClass() + ".cyCreateFromShared(" + var + ")";
return pyxClassName() + ".cyCreateFromShared(" + var + ")";
else {
// construct a shared_ptr if var is not a shared ptr
return pythonClass() + ".cyCreateFromShared(" + pyxSharedCythonClass() +
"(new " + pyxCythonClass() + "(" + var + ")))";
return pyxClassName() + ".cyCreateFromShared(" + shared_pxd_class_in_pyx() +
"(new " + pxd_class_in_pyx() + "(" + var + ")))";
}
} else
return var;

View File

@ -62,9 +62,9 @@ void StaticMethod::emit_cython_pxd(FileWriter& file, const Class& cls) const {
for(size_t i = 0; i < nrOverloads(); ++i) {
file.oss << "\t\t@staticmethod\n";
file.oss << "\t\t";
returnVals_[i].emit_cython_pxd(file, cls.cythonClass());
returnVals_[i].emit_cython_pxd(file, cls.pxdClassName());
file.oss << name_ + ((i>0)?"_" + to_string(i):"") << " \"" << name_ << "\"" << "(";
argumentList(i).emit_cython_pxd(file, cls.cythonClass());
argumentList(i).emit_cython_pxd(file, cls.pxdClassName());
file.oss << ")\n";
}
}
@ -79,7 +79,7 @@ void StaticMethod::emit_cython_pyx_no_overload(FileWriter& file,
file.oss << "):\n";
/// Call cython corresponding function and return
string ret = pyx_functionCall(cls.pyxCythonClass(), name_, 0);
string ret = pyx_functionCall(cls.pxd_class_in_pyx(), name_, 0);
file.oss << "\t\t";
if (!returnVals_[0].isVoid()) {
file.oss << "return " << returnVals_[0].pyx_casting(ret) << "\n";
@ -102,7 +102,7 @@ void StaticMethod::emit_cython_pyx(FileWriter& file, const Class& cls) const {
file.oss << pyx_checkDuplicateNargsKwArgs();
for (size_t i = 0; i < N; ++i) {
string funcName = name_ + "_" + to_string(i);
file.oss << "\t\tsuccess, results = " << cls.pythonClass() << "."
file.oss << "\t\tsuccess, results = " << cls.pyxClassName() << "."
<< funcName << "(*args, **kwargs)\n";
file.oss << "\t\tif success:\n\t\t\treturn results\n";
}
@ -118,7 +118,7 @@ void StaticMethod::emit_cython_pyx(FileWriter& file, const Class& cls) const {
file.oss << pyx_resolveOverloadParams(args, false); // lazy: always return None even if it's a void function
/// Call cython corresponding function and return
string ret = pyx_functionCall(cls.pyxCythonClass(), pxdFuncName, i);
string ret = pyx_functionCall(cls.pxd_class_in_pyx(), pxdFuncName, i);
if (!returnVals_[i].isVoid()) {
file.oss << "\t\tcdef " << returnVals_[i].pyx_returnType()
<< " ret = " << ret << "\n";

View File

@ -24,9 +24,9 @@ using namespace wrap;
void TemplateMethod::emit_cython_pxd(FileWriter& file, const Class& cls) const {
for(size_t i = 0; i < nrOverloads(); ++i) {
file.oss << "\t\t";
returnVals_[i].emit_cython_pxd(file, cls.cythonClass());
returnVals_[i].emit_cython_pxd(file, cls.pxdClassName());
file.oss << name_ << "[" << argName << "]" << "(";
argumentList(i).emit_cython_pxd(file, cls.cythonClass());
argumentList(i).emit_cython_pxd(file, cls.pxdClassName());
file.oss << ")\n";
}
}

View File

@ -21,8 +21,8 @@ struct TypedefPair {
void emit_cython_pxd(FileWriter& file) const {
file.oss << "cdef extern from \"" << includeFile << "\" namespace \""
<< oldType.qualifiedNamespaces("::") << "\":\n";
file.oss << "\tctypedef " << oldType.cythonClass() << " "
<< newType.cythonClass() << "\n";
file.oss << "\tctypedef " << oldType.pxdClassName() << " "
<< newType.pxdClassName() << "\n";
}
};
}