Move type-resolving code to cxx_wrappers

This is also not specific to class wrappers, but can, and will be, used
for the global functions, so move it to a place where it can be reused.

No changes yet.
This commit is contained in:
Vadim Zeitlin 2021-12-07 00:52:21 +01:00
commit f4ecc3c06a

View file

@ -406,6 +406,9 @@ struct cxx_wrappers
cxx_wrappers() :
except_check_start(NULL), except_check_end(NULL),
sect_cxx_h(NULL), sect_types(NULL), sect_decls(NULL), sect_impls(NULL) {
node_func_ = NULL;
rtype_desc_ = NULL;
ptype_desc_ = NULL;
}
void initialize() {
@ -458,6 +461,100 @@ struct cxx_wrappers
bool is_initialized() const { return sect_types != NULL; }
// All the functions below are only used when is_initialized() returns true.
// Fill the provided rtype_desc with the type information for the given function node.
//
// Returns false in case of error, i.e. if function wrapper can't be generated at all.
bool lookup_cxx_ret_type(cxx_rtype_desc& rtype_desc, Node* n) {
String* const func_type = Getattr(n, "type");
if (SwigType_type(func_type) == T_VOID) {
// Nothing to do, rtype_desc is void by default.
return true;
}
// As above, ensure our replaceSpecialVariables() is used.
temp_ptr_setter<cxx_rtype_desc*> set(&rtype_desc_, &rtype_desc);
bool use_cxxout = true;
String* type(Swig_typemap_lookup("cxxouttype", n, "", NULL));
if (!type) {
use_cxxout = false;
type = Swig_typemap_lookup("ctype", n, "", NULL);
}
if (!type) {
Swig_warning(WARN_C_TYPEMAP_CTYPE_UNDEF, Getfile(n), Getline(n),
"No ctype typemap defined for the return type \"%s\" of %s\n",
SwigType_str(func_type, NULL),
Getattr(n, "sym:name")
);
return false;
}
rtype_desc.set_type(type);
do_resolve_type(n, func_type, rtype_desc.type(), NULL, &rtype_desc);
if (use_cxxout) {
if (String* out_tm = Swig_typemap_lookup("cxxout", n, "", NULL))
rtype_desc.apply_out_typemap(out_tm);
}
return true;
}
// Return the type description for the given parameter of the function.
cxx_ptype_desc lookup_cxx_parm_type(Node* n, Parm* p) {
cxx_ptype_desc ptype_desc;
// Ensure our own replaceSpecialVariables() is used for $typemap() expansion.
temp_ptr_setter<cxx_ptype_desc*> set(&ptype_desc_, &ptype_desc);
bool use_cxxin = true;
String* type = Swig_typemap_lookup("cxxintype", p, "", NULL);
if (!type) {
use_cxxin = false;
type = Swig_typemap_lookup("ctype", p, "", NULL);
}
if (type) {
ptype_desc.set_type(type);
do_resolve_type(n, Getattr(p, "type"), ptype_desc.type(), &ptype_desc, NULL);
}
if (use_cxxin) {
if (String* in_tm = Getattr(p, "tmap:cxxin"))
ptype_desc.apply_in_typemap(Copy(in_tm));
}
return ptype_desc;
}
// This function is called from C::replaceSpecialVariables() but only does something non-trivial when it's called by our own lookup_cxx_xxx_type() functions.
bool replaceSpecialVariables(String *method, String *tm, Parm *parm) {
if (!ptype_desc_ && !rtype_desc_)
return false;
if (Cmp(method, "ctype") != 0) {
Swig_warning(WARN_C_UNSUPPORTTED, input_file, line_number, "Unsupported %s typemap %s\n", method, tm);
return false;
}
if (SwigType *type = Getattr(parm, "type")) {
if (ptype_desc_)
ptype_desc_->set_type(type);
if (rtype_desc_)
rtype_desc_->set_type(type);
do_resolve_type(node_func_, type, tm, ptype_desc_, rtype_desc_);
}
return true;
}
// Used for generating exception checks around the calls, see initialize_exceptions().
const char* except_check_start;
const char* except_check_end;
@ -477,6 +574,236 @@ struct cxx_wrappers
// Implementation of the classes.
String* sect_impls;
private:
// Replace "resolved_type" occurrences in the string with the value corresponding to the given type.
//
// Note that the node here is the function itself, but type may be either its return type or the type of one of its parameters, so it's passed as a different
// parameter.
//
// Also fills in the start/end wrapper parts of the provided type descriptions if they're not null, with the casts needed to translate from C type to C++ type
// (this is used for the parameters of C++ functions, hence the name) and from C types to C++ types (which is used for the function return values).
static void do_resolve_type(Node* n, String* type, String* s, cxx_ptype_desc* ptype_desc, cxx_rtype_desc* rtype_desc) {
enum TypeKind
{
Type_Ptr,
Type_Ref,
Type_Obj,
Type_Enm,
Type_Max
} typeKind = Type_Max;
// These correspond to the typemaps for SWIGTYPE*, SWIGTYPE&, SWIGTYPE and enum SWIGTYPE, respectively, defined in c.swg.
static const char* typemaps[Type_Max] = {
"$resolved_type*",
"$*resolved_type*",
"$&resolved_type*",
"$resolved_type",
};
for (int i = 0; i < Type_Max; ++i) {
if (Strstr(s, typemaps[i])) {
typeKind = static_cast<TypeKind>(i);
break;
}
}
if (typeKind == Type_Max) {
if (Strstr(s, "resolved_type")) {
Swig_warning(WARN_C_UNSUPPORTTED, input_file, line_number,
"Unsupported typemap \"%s\" used for type \"%s\" of \"%s\"\n",
s, type, Getattr(n, "name")
);
}
return;
}
// The logic here is somewhat messy because we use the same "$resolved_type*" typemap for pointers/references to both enums and classes, but we actually
// need to do quite different things for them. It could probably be simplified by changing the typemaps to be distinct, but this would require also updating
// the code for C wrappers generation in substituteResolvedTypeSpecialVariable().
//
// An even better idea might be to try to define this using cxx{in,out} typemaps for the various types and let the generic SWIG machinery do all the
// matching instead of doing it in the code here.
scoped_dohptr resolved_type(SwigType_typedef_resolve_all(type));
scoped_dohptr base_resolved_type(SwigType_base(resolved_type));
scoped_dohptr typestr;
if (SwigType_isenum(base_resolved_type)) {
String* enumname = NULL;
if (Node* const enum_node = Language::instance()->enumLookup(base_resolved_type)) {
// This is the name of the enum in C wrappers, it should be already set by getEnumName().
enumname = Getattr(enum_node, "enumname");
if (enumname) {
String* const enum_symname = Getattr(enum_node, "sym:name");
if (Checkattr(enum_node, "ismember", "1")) {
Node* const parent_class = parentNode(enum_node);
typestr = NewStringf("%s::%s", Getattr(parent_class, "sym:name"), enum_symname);
} else {
typestr = Copy(enum_symname);
}
}
}
if (!enumname) {
// Unknown enums are mapped to int and no casts are necessary in this case.
typestr = NewString("int");
}
if (SwigType_ispointer(type))
Append(typestr, " *");
else if (SwigType_isreference(type))
Append(typestr, " &");
if (enumname) {
switch (typeKind) {
case Type_Ptr:
if (rtype_desc) {
rtype_desc->apply_out_typemap(NewStringf("(%s)$cresult", typestr.get()));
}
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewStringf("(%s*)$1", enumname));
}
break;
case Type_Ref:
if (rtype_desc) {
rtype_desc->apply_out_typemap(NewStringf("(%s)(*($cresult))", typestr.get()));
}
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewStringf("(%s*)&($1)", enumname));
}
break;
case Type_Enm:
if (rtype_desc) {
rtype_desc->apply_out_typemap(NewStringf("(%s)$cresult", typestr.get()));
}
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewStringf("(%s)$1", enumname));
}
break;
case Type_Obj:
case Type_Max:
// Unreachable, but keep here to avoid -Wswitch warnings.
assert(0);
}
} else {
// This is the only thing we need to do even when we don't have the enum name.
if (typeKind == Type_Ref && ptype_desc)
ptype_desc->apply_in_typemap(NewString("&($1)"));
}
} else {
scoped_dohptr stripped_type(SwigType_strip_qualifiers(resolved_type));
String* classname;
if (Node* const class_node = Language::instance()->classLookup(stripped_type)) {
typestr = SwigType_str(type, 0);
classname = Getattr(class_node, "sym:name");
// We don't use namespaces, but the type may contain them, so get rid of them by replacing the base type name, which is fully qualified, with just the
// class name, which is not.
scoped_dohptr basetype(SwigType_base(type));
scoped_dohptr basetypestr(SwigType_str(basetype, 0));
if (Cmp(basetypestr, classname) != 0) {
Replaceall(typestr, basetypestr, classname);
}
} else {
// This is something unknown, so just use an opaque typedef already declared in C wrappers section for it.
typestr = NewStringf("SWIGTYPE%s*", SwigType_manglestr(stripped_type));
classname = NULL;
}
const char* const owns = GetFlag(n, "feature:new") ? "true" : "false";
switch (typeKind) {
case Type_Ptr:
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewString("$1->swig_self()"));
}
if (rtype_desc) {
if (classname) {
rtype_desc->apply_out_typemap(NewStringf(
"$cresult ? new %s($cresult, %s) : nullptr;",
classname, owns
));
}
}
break;
case Type_Ref:
if (rtype_desc) {
if (classname) {
// We can't return a reference, as this requires an existing object and we don't have any, so we have to return an object instead, and this object
// must be constructed using the special ctor not taking the pointer ownership.
typestr = Copy(classname);
rtype_desc->apply_out_typemap(NewStringf("%s{$cresult, false}", classname));
} else {
// We can't do anything at all in this case.
Swig_error(input_file, line_number, "Unknown reference return type \"%s\"\n", typestr.get());
}
}
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewString("$1.swig_self()"));
}
break;
case Type_Obj:
if (rtype_desc) {
if (classname) {
// The pointer returned by C function wrapping a function returning an object should never be null unless an exception happened, so we don't test
// for it here, unlike in Type_Ptr case.
//
// Also, normally all returned objects should be owned by their wrappers, but there is a special case of objects not being returned by value: this
// seems not to make sense, but can actually happen when typemaps map references or pointers to objects, like they do for e.g. shared_ptr<>.
//
// Note that we must use the type of the function, retrieved from its node, here and not the type passed to us which is the result of typemap
// expansion and so may not be a reference any more.
rtype_desc->apply_out_typemap(NewStringf("%s{$cresult, %s}",
typestr.get(),
SwigType_isreference(Getattr(n, "type")) ? owns : "true"
));
} else {
Swig_error(input_file, line_number, "Unknown object return type \"%s\"\n", typestr.get());
}
}
if (ptype_desc) {
// It doesn't seem like it can ever be useful to pass an object by value to a wrapper function and it can fail if it doesn't have a copy ctor (see
// code related to has_copy_ctor_ in our dtor above), so always pass it by const reference instead.
Append(typestr, " const&");
ptype_desc->apply_in_typemap(NewString("$1.swig_self()"));
}
break;
case Type_Enm:
case Type_Max:
// Unreachable, but keep here to avoid -Wswitch warnings.
assert(0);
}
}
Replaceall(s, typemaps[typeKind], typestr);
}
// These pointers are temporarily set to non-null value only while expanding a typemap for C++ wrappers, see replaceSpecialVariables().
cxx_ptype_desc* ptype_desc_;
cxx_rtype_desc* rtype_desc_;
// This one is set from the outside, so make it public for simplicity.
public:
Node* node_func_;
};
/*
@ -490,13 +817,9 @@ public:
// If the provided cxx_wrappers object is not initialized, this class doesn't do anything.
//
// The node pointer must be valid, point to a class and remain valid for the lifetime of this object.
cxx_class_wrapper(const cxx_wrappers& cxx_wrappers, Node* n) : cxx_wrappers_(cxx_wrappers) {
cxx_class_wrapper(cxx_wrappers& cxx_wrappers, Node* n) : cxx_wrappers_(cxx_wrappers) {
class_node_ = NULL;
node_func_ = NULL;
rtype_desc_ = NULL;
ptype_desc_ = NULL;
if (!cxx_wrappers_.is_initialized())
return;
@ -589,7 +912,7 @@ public:
return;
}
temp_ptr_setter<Node*> set(&node_func_, n);
temp_ptr_setter<Node*> set(&cxx_wrappers_.node_func_, n);
// As mentioned elsewhere, we can't use Swig_storage_isstatic() here because the "storage" attribute is temporarily saved in another view when this
// function is being executed, so rely on another attribute to determine if it's a static function instead.
@ -600,7 +923,7 @@ public:
// Deal with the return type: it may be different from the type of the C wrapper function if it involves objects, and so we may need to add a cast.
cxx_rtype_desc rtype_desc;
if (!lookup_cxx_ret_type(rtype_desc, n))
if (!cxx_wrappers_.lookup_cxx_ret_type(rtype_desc, n))
return;
// We also need the list of parameters to take in the C++ function being generated and the list of them to pass to the C wrapper.
@ -646,7 +969,7 @@ public:
for (; p; p = nextSibling(p)) {
String* const name = Getattr(p, "lname");
const cxx_ptype_desc ptype_desc = lookup_cxx_parm_type(n, p);
const cxx_ptype_desc ptype_desc = cxx_wrappers_.lookup_cxx_parm_type(n, p);
if (!ptype_desc.type()) {
Swig_warning(WARN_C_TYPEMAP_CTYPE_UNDEF, Getfile(p), Getline(p),
"No ctype typemap defined for the parameter \"%s\" of %s\n",
@ -940,29 +1263,6 @@ public:
);
}
// This function is called from C::replaceSpecialVariables() but only does something non-trivial when it's called by our own lookup_cxx_xxx_type() functions.
bool replaceSpecialVariables(String *method, String *tm, Parm *parm) {
if (!ptype_desc_ && !rtype_desc_)
return false;
if (Cmp(method, "ctype") != 0) {
Swig_warning(WARN_C_UNSUPPORTTED, input_file, line_number, "Unsupported %s typemap %s\n", method, tm);
return false;
}
if (SwigType *type = Getattr(parm, "type")) {
if (ptype_desc_)
ptype_desc_->set_type(type);
if (rtype_desc_)
rtype_desc_->set_type(type);
do_resolve_type(node_func_, type, tm, ptype_desc_, rtype_desc_);
}
return true;
}
private:
// Various helpers.
@ -984,290 +1284,8 @@ private:
return qualifier && strncmp(Char(qualifier), "q(const)", 8) == 0 ? " const" : "";
}
// Replace "resolved_type" occurrences in the string with the value corresponding to the given type.
//
// Note that the node here is the function itself, but type may be either its return type or the type of one of its parameters, so it's passed as a different
// parameter.
//
// Also fills in the start/end wrapper parts of the provided type descriptions if they're not null, with the casts needed to translate from C type to C++ type
// (this is used for the parameters of C++ functions, hence the name) and from C types to C++ types (which is used for the function return values).
static void do_resolve_type(Node* n, String* type, String* s, cxx_ptype_desc* ptype_desc, cxx_rtype_desc* rtype_desc) {
enum TypeKind
{
Type_Ptr,
Type_Ref,
Type_Obj,
Type_Enm,
Type_Max
} typeKind = Type_Max;
// These correspond to the typemaps for SWIGTYPE*, SWIGTYPE&, SWIGTYPE and enum SWIGTYPE, respectively, defined in c.swg.
static const char* typemaps[Type_Max] = {
"$resolved_type*",
"$*resolved_type*",
"$&resolved_type*",
"$resolved_type",
};
for (int i = 0; i < Type_Max; ++i) {
if (Strstr(s, typemaps[i])) {
typeKind = static_cast<TypeKind>(i);
break;
}
}
if (typeKind == Type_Max) {
if (Strstr(s, "resolved_type")) {
Swig_warning(WARN_C_UNSUPPORTTED, input_file, line_number,
"Unsupported typemap \"%s\" used for type \"%s\" of \"%s\"\n",
s, type, Getattr(n, "name")
);
}
return;
}
// The logic here is somewhat messy because we use the same "$resolved_type*" typemap for pointers/references to both enums and classes, but we actually
// need to do quite different things for them. It could probably be simplified by changing the typemaps to be distinct, but this would require also updating
// the code for C wrappers generation in substituteResolvedTypeSpecialVariable().
//
// An even better idea might be to try to define this using cxx{in,out} typemaps for the various types and let the generic SWIG machinery do all the
// matching instead of doing it in the code here.
scoped_dohptr resolved_type(SwigType_typedef_resolve_all(type));
scoped_dohptr base_resolved_type(SwigType_base(resolved_type));
scoped_dohptr typestr;
if (SwigType_isenum(base_resolved_type)) {
String* enumname = NULL;
if (Node* const enum_node = Language::instance()->enumLookup(base_resolved_type)) {
// This is the name of the enum in C wrappers, it should be already set by getEnumName().
enumname = Getattr(enum_node, "enumname");
if (enumname) {
String* const enum_symname = Getattr(enum_node, "sym:name");
if (Checkattr(enum_node, "ismember", "1")) {
Node* const parent_class = parentNode(enum_node);
typestr = NewStringf("%s::%s", Getattr(parent_class, "sym:name"), enum_symname);
} else {
typestr = Copy(enum_symname);
}
}
}
if (!enumname) {
// Unknown enums are mapped to int and no casts are necessary in this case.
typestr = NewString("int");
}
if (SwigType_ispointer(type))
Append(typestr, " *");
else if (SwigType_isreference(type))
Append(typestr, " &");
if (enumname) {
switch (typeKind) {
case Type_Ptr:
if (rtype_desc) {
rtype_desc->apply_out_typemap(NewStringf("(%s)$cresult", typestr.get()));
}
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewStringf("(%s*)$1", enumname));
}
break;
case Type_Ref:
if (rtype_desc) {
rtype_desc->apply_out_typemap(NewStringf("(%s)(*($cresult))", typestr.get()));
}
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewStringf("(%s*)&($1)", enumname));
}
break;
case Type_Enm:
if (rtype_desc) {
rtype_desc->apply_out_typemap(NewStringf("(%s)$cresult", typestr.get()));
}
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewStringf("(%s)$1", enumname));
}
break;
case Type_Obj:
case Type_Max:
// Unreachable, but keep here to avoid -Wswitch warnings.
assert(0);
}
} else {
// This is the only thing we need to do even when we don't have the enum name.
if (typeKind == Type_Ref && ptype_desc)
ptype_desc->apply_in_typemap(NewString("&($1)"));
}
} else {
scoped_dohptr stripped_type(SwigType_strip_qualifiers(resolved_type));
String* classname;
if (Node* const class_node = Language::instance()->classLookup(stripped_type)) {
typestr = SwigType_str(type, 0);
classname = Getattr(class_node, "sym:name");
// We don't use namespaces, but the type may contain them, so get rid of them by replacing the base type name, which is fully qualified, with just the
// class name, which is not.
scoped_dohptr basetype(SwigType_base(type));
scoped_dohptr basetypestr(SwigType_str(basetype, 0));
if (Cmp(basetypestr, classname) != 0) {
Replaceall(typestr, basetypestr, classname);
}
} else {
// This is something unknown, so just use an opaque typedef already declared in C wrappers section for it.
typestr = NewStringf("SWIGTYPE%s*", SwigType_manglestr(stripped_type));
classname = NULL;
}
const char* const owns = GetFlag(n, "feature:new") ? "true" : "false";
switch (typeKind) {
case Type_Ptr:
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewString("$1->swig_self()"));
}
if (rtype_desc) {
if (classname) {
rtype_desc->apply_out_typemap(NewStringf(
"$cresult ? new %s($cresult, %s) : nullptr;",
classname, owns
));
}
}
break;
case Type_Ref:
if (rtype_desc) {
if (classname) {
// We can't return a reference, as this requires an existing object and we don't have any, so we have to return an object instead, and this object
// must be constructed using the special ctor not taking the pointer ownership.
typestr = Copy(classname);
rtype_desc->apply_out_typemap(NewStringf("%s{$cresult, false}", classname));
} else {
// We can't do anything at all in this case.
Swig_error(input_file, line_number, "Unknown reference return type \"%s\"\n", typestr.get());
}
}
if (ptype_desc) {
ptype_desc->apply_in_typemap(NewString("$1.swig_self()"));
}
break;
case Type_Obj:
if (rtype_desc) {
if (classname) {
// The pointer returned by C function wrapping a function returning an object should never be null unless an exception happened, so we don't test
// for it here, unlike in Type_Ptr case.
//
// Also, normally all returned objects should be owned by their wrappers, but there is a special case of objects not being returned by value: this
// seems not to make sense, but can actually happen when typemaps map references or pointers to objects, like they do for e.g. shared_ptr<>.
//
// Note that we must use the type of the function, retrieved from its node, here and not the type passed to us which is the result of typemap
// expansion and so may not be a reference any more.
rtype_desc->apply_out_typemap(NewStringf("%s{$cresult, %s}",
typestr.get(),
SwigType_isreference(Getattr(n, "type")) ? owns : "true"
));
} else {
Swig_error(input_file, line_number, "Unknown object return type \"%s\"\n", typestr.get());
}
}
if (ptype_desc) {
// It doesn't seem like it can ever be useful to pass an object by value to a wrapper function and it can fail if it doesn't have a copy ctor (see
// code related to has_copy_ctor_ in our dtor above), so always pass it by const reference instead.
Append(typestr, " const&");
ptype_desc->apply_in_typemap(NewString("$1.swig_self()"));
}
break;
case Type_Enm:
case Type_Max:
// Unreachable, but keep here to avoid -Wswitch warnings.
assert(0);
}
}
Replaceall(s, typemaps[typeKind], typestr);
}
cxx_ptype_desc lookup_cxx_parm_type(Node* n, Parm* p) {
cxx_ptype_desc ptype_desc;
// Ensure our own replaceSpecialVariables() is used for $typemap() expansion.
temp_ptr_setter<cxx_ptype_desc*> set(&ptype_desc_, &ptype_desc);
bool use_cxxin = true;
String* type = Swig_typemap_lookup("cxxintype", p, "", NULL);
if (!type) {
use_cxxin = false;
type = Swig_typemap_lookup("ctype", p, "", NULL);
}
if (type) {
ptype_desc.set_type(type);
do_resolve_type(n, Getattr(p, "type"), ptype_desc.type(), &ptype_desc, NULL);
}
if (use_cxxin) {
if (String* in_tm = Getattr(p, "tmap:cxxin"))
ptype_desc.apply_in_typemap(Copy(in_tm));
}
return ptype_desc;
}
bool lookup_cxx_ret_type(cxx_rtype_desc& rtype_desc, Node* n) {
String* const func_type = Getattr(n, "type");
if (SwigType_type(func_type) == T_VOID) {
// Nothing to do, rtype_desc is void by default.
return true;
}
// As above, ensure our replaceSpecialVariables() is used.
temp_ptr_setter<cxx_rtype_desc*> set(&rtype_desc_, &rtype_desc);
bool use_cxxout = true;
String* type(Swig_typemap_lookup("cxxouttype", n, "", NULL));
if (!type) {
use_cxxout = false;
type = Swig_typemap_lookup("ctype", n, "", NULL);
}
if (!type) {
Swig_warning(WARN_C_TYPEMAP_CTYPE_UNDEF, Getfile(n), Getline(n),
"No ctype typemap defined for the return type \"%s\" of %s\n",
SwigType_str(func_type, NULL),
Getattr(n, "sym:name")
);
return false;
}
rtype_desc.set_type(type);
do_resolve_type(n, func_type, rtype_desc.type(), NULL, &rtype_desc);
if (use_cxxout) {
if (String* out_tm = Swig_typemap_lookup("cxxout", n, "", NULL))
rtype_desc.apply_out_typemap(out_tm);
}
return true;
}
const cxx_wrappers& cxx_wrappers_;
cxx_wrappers& cxx_wrappers_;
// The class node itself, left null only if we skip generating wrappers for it for whatever reason.
Node* class_node_;
@ -1277,11 +1295,6 @@ private:
// any, this member can also be null).
scoped_dohptr first_base_;
// These pointers are temporarily set to non-null value only while expanding a typemap for C++ wrappers, see replaceSpecialVariables().
Node* node_func_;
cxx_ptype_desc* ptype_desc_;
cxx_rtype_desc* rtype_desc_;
// Name of the C function used for deleting the owned object, if any.
String* dtor_wname_;
@ -1586,7 +1599,7 @@ public:
virtual void replaceSpecialVariables(String *method, String *tm, Parm *parm) {
// This function is called by Swig_typemap_lookup(), which may be called when generating C or C++ wrappers, so delegate to the latter one if necessary.
if (cxx_class_wrapper_ && cxx_class_wrapper_->replaceSpecialVariables(method, tm, parm))
if (cxx_wrappers_.is_initialized() && cxx_wrappers_.replaceSpecialVariables(method, tm, parm))
return;
SwigType *type = Getattr(parm, "type");
@ -2386,8 +2399,10 @@ public:
emit_wrapper_func_decl(n, wname);
if (cxx_class_wrapper_)
cxx_class_wrapper_->emit_member_function(n);
if (cxx_wrappers_.is_initialized()) {
if (cxx_class_wrapper_)
cxx_class_wrapper_->emit_member_function(n);
}
Delete(name);
}