[Go] Add goargout typemap.
This commit is contained in:
parent
0dd7b61c57
commit
acaaa0f31f
6 changed files with 286 additions and 44 deletions
|
|
@ -5,6 +5,9 @@ See the RELEASENOTES file for a summary of changes in each release.
|
|||
Version 3.0.3 (in progress)
|
||||
===========================
|
||||
|
||||
2014-09-09: ianlancetaylor
|
||||
[Go] Add goargout typemap.
|
||||
|
||||
2014-09-09: olly
|
||||
[PHP] Fix segmentation faults with directors in PHP >= 5.4, and
|
||||
reenable runme tests for director_basic testcase. Fix from
|
||||
|
|
|
|||
|
|
@ -773,6 +773,27 @@ to gotype. If this is not defined no conversion is done.
|
|||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>argout</td>
|
||||
<td>
|
||||
C/C++ code to adjust an argument value when returning from a function.
|
||||
This is called after the real C/C++ function has run. This uses the
|
||||
internally generated C/C++ type, based on imtype. This is only useful
|
||||
for a pointer type of some sort. If this is not defined nothing will
|
||||
be done.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>goargout</td>
|
||||
<td>
|
||||
Go code to adjust an argument value when returning from a function.
|
||||
This is called after the real C/C++ function has run. The value will
|
||||
be in imtype. This is only useful for a pointer type of some sort.
|
||||
If this is not defined nothing will be done.
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>directorin</td>
|
||||
<td>
|
||||
|
|
|
|||
|
|
@ -32,4 +32,12 @@ func main() {
|
|||
fmt.Println("got", m, "want", want)
|
||||
panic(m)
|
||||
}
|
||||
|
||||
a := []string{"a", "bc", "def"}
|
||||
go_inout.DoubleArray(&a)
|
||||
dwant := []string{"a", "bc", "def", "aa", "bcbc", "defdef"}
|
||||
if !reflect.DeepEqual(a, dwant) {
|
||||
fmt.Println("got", a, "want", dwant)
|
||||
panic(a)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
// Test the goin and goout typemaps.
|
||||
// Test the goin, goout, and goargout typemaps.
|
||||
|
||||
%module go_inout
|
||||
|
||||
%{
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
%}
|
||||
|
||||
%inline
|
||||
|
|
@ -21,7 +23,7 @@ struct RetStruct {
|
|||
|
||||
// Write a typemap that calls C++ by converting in and out of JSON.
|
||||
|
||||
%go_import("encoding/json")
|
||||
%go_import("encoding/json", "bytes", "encoding/binary")
|
||||
|
||||
%insert(go_header)
|
||||
%{
|
||||
|
|
@ -77,3 +79,121 @@ RetStruct Same(MyStruct s)
|
|||
}
|
||||
|
||||
%}
|
||||
|
||||
%inline
|
||||
%{
|
||||
|
||||
struct MyArray {
|
||||
std::vector<std::string> strings;
|
||||
};
|
||||
|
||||
static uint64_t getuint64(const char* s) {
|
||||
uint64_t ret = 0;
|
||||
for (int i = 0; i < 8; i++, s++) {
|
||||
ret |= static_cast<uint64_t>(*s) << i * 8;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void putuint64(std::string *s, size_t off, uint64_t v) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
(*s)[off + i] = (v >> (i * 8)) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%typemap(gotype) MyArray* "*[]string"
|
||||
|
||||
%typemap(imtype) MyArray* "*string"
|
||||
|
||||
// Encode the slice as a single string, with length prefixes.
|
||||
%typemap(goin) MyArray*
|
||||
%{
|
||||
{
|
||||
var buf bytes.Buffer
|
||||
bin := binary.LittleEndian
|
||||
var b [8]byte
|
||||
bin.PutUint64(b[:], uint64(len(*$input)))
|
||||
buf.Write(b[:])
|
||||
for _, s := range *$input {
|
||||
bin.PutUint64(b[:], uint64(len(s)))
|
||||
buf.Write(b[:])
|
||||
buf.WriteString(s)
|
||||
}
|
||||
str := buf.String()
|
||||
$result = &str
|
||||
}
|
||||
%}
|
||||
|
||||
// Unpack the string holding the packed slice.
|
||||
%typemap(in) MyArray* (MyArray t)
|
||||
%{
|
||||
{
|
||||
_gostring_ *s = $input;
|
||||
const char *p = static_cast<const char *>(s->p);
|
||||
uint64_t len = getuint64(p);
|
||||
p += 8;
|
||||
t.strings.resize(len);
|
||||
for (uint64_t i = 0; i < len; i++) {
|
||||
uint64_t slen = getuint64(p);
|
||||
p += 8;
|
||||
t.strings[i].assign(p, slen);
|
||||
p += slen;
|
||||
}
|
||||
$1 = &t;
|
||||
}
|
||||
%}
|
||||
|
||||
// Pack the vector into a string.
|
||||
%typemap(argout) MyArray*
|
||||
%{
|
||||
{
|
||||
size_t tot = 8;
|
||||
std::vector<std::string>::const_iterator p;
|
||||
for (p = $1->strings.begin(); p != $1->strings.end(); ++p) {
|
||||
tot += 8 + p->size();
|
||||
}
|
||||
std::string str;
|
||||
str.resize(tot);
|
||||
putuint64(&str, 0, $1->strings.size());
|
||||
size_t off = 8;
|
||||
for (p = $1->strings.begin(); p != $1->strings.end(); ++p) {
|
||||
putuint64(&str, off, p->size());
|
||||
off += 8;
|
||||
str.replace(off, p->size(), *p);
|
||||
off += p->size();
|
||||
}
|
||||
*$input = _swig_makegostring(str.data(), str.size());
|
||||
}
|
||||
%}
|
||||
|
||||
// Unpack the string into a []string.
|
||||
%typemap(goargout) MyArray*
|
||||
%{
|
||||
{
|
||||
str := *$input
|
||||
bin := binary.LittleEndian
|
||||
size := bin.Uint64([]byte(str[:8]))
|
||||
str = str[8:]
|
||||
r := make([]string, size)
|
||||
for i := range r {
|
||||
len := bin.Uint64([]byte(str[:8]))
|
||||
str = str[8:]
|
||||
r[i] = str[:len]
|
||||
str = str[len:]
|
||||
}
|
||||
*$1 = r
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%inline
|
||||
%{
|
||||
void DoubleArray(MyArray* v) {
|
||||
size_t size = v->strings.size();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
v->strings.push_back(v->strings[i] + v->strings[i]);
|
||||
}
|
||||
}
|
||||
%}
|
||||
|
|
|
|||
|
|
@ -225,13 +225,13 @@
|
|||
%{ $1 = *($&1_ltype)&$input; %}
|
||||
|
||||
%typemap(out) SWIGTYPE *
|
||||
%{ *($&1_ltype)&$result = $1; %}
|
||||
%{ *($&1_ltype)&$result = ($1_ltype)$1; %}
|
||||
|
||||
%typemap(directorin) SWIGTYPE *
|
||||
%{ $input = ($1_ltype)$1; %}
|
||||
%{ *($&1_ltype)&$input = ($1_ltype)$1; %}
|
||||
|
||||
%typemap(directorout) SWIGTYPE *
|
||||
%{ $result = ($1_ltype)$input; %}
|
||||
%{ $result = *($&1_ltype)&$input; %}
|
||||
|
||||
%apply SWIGTYPE * { SWIGTYPE *const }
|
||||
|
||||
|
|
|
|||
|
|
@ -868,6 +868,7 @@ private:
|
|||
Swig_typemap_attach_parms("default", parms, dummy);
|
||||
Swig_typemap_attach_parms("gotype", parms, dummy);
|
||||
Swig_typemap_attach_parms("goin", parms, dummy);
|
||||
Swig_typemap_attach_parms("goargout", parms, dummy);
|
||||
Swig_typemap_attach_parms("imtype", parms, dummy);
|
||||
int parm_count = emit_num_arguments(parms);
|
||||
int required_count = emit_num_required(parms);
|
||||
|
|
@ -892,20 +893,25 @@ private:
|
|||
|
||||
bool needs_wrapper = (gccgo_flag || receiver || is_constructor || is_destructor || parm_count > required_count);
|
||||
|
||||
bool has_goout = false;
|
||||
if (goout) {
|
||||
has_goout = true;
|
||||
}
|
||||
|
||||
// See whether any of the function parameters are represented by
|
||||
// interface values. When calling the C++ code, we need to convert
|
||||
// back to a uintptr.
|
||||
if (!needs_wrapper) {
|
||||
Parm *p = parms;
|
||||
for (int i = 0; i < parm_count; ++i) {
|
||||
p = getParm(p);
|
||||
String *ty = Getattr(p, "type");
|
||||
if (goTypeIsInterface(p, ty) || Getattr(p, "tmap:goin") != NULL) {
|
||||
needs_wrapper = true;
|
||||
break;
|
||||
}
|
||||
p = nextParm(p);
|
||||
Parm *p = parms;
|
||||
for (int i = 0; i < parm_count; ++i) {
|
||||
p = getParm(p);
|
||||
String *ty = Getattr(p, "type");
|
||||
if (Getattr(p, "tmap:goargout")) {
|
||||
has_goout = true;
|
||||
needs_wrapper = true;
|
||||
} else if (goTypeIsInterface(p, ty) || Getattr(p, "tmap:goin")) {
|
||||
needs_wrapper = true;
|
||||
}
|
||||
p = nextParm(p);
|
||||
}
|
||||
if (goTypeIsInterface(n, result) || goout != NULL) {
|
||||
needs_wrapper = true;
|
||||
|
|
@ -982,7 +988,7 @@ private:
|
|||
|
||||
Printv(f_go_wrappers, "func ", NULL);
|
||||
|
||||
Parm *p = parms;
|
||||
p = parms;
|
||||
int pi = 0;
|
||||
|
||||
// Add the receiver if this is a method.
|
||||
|
|
@ -1097,7 +1103,7 @@ private:
|
|||
}
|
||||
|
||||
if (gccgo_flag) {
|
||||
if (goout != NULL || is_constructor) {
|
||||
if (has_goout || is_constructor) {
|
||||
Printv(call, "\tfunc() {\n", NULL);
|
||||
}
|
||||
Printv(call, "\tdefer SwigCgocallDone()\n", NULL);
|
||||
|
|
@ -1143,6 +1149,7 @@ private:
|
|||
if ((i == 0 && is_destructor) || ((i > 0 || !receiver || base || is_constructor) && goTypeIsInterface(p, pt))) {
|
||||
Printv(call, ".Swigcptr()", NULL);
|
||||
}
|
||||
Setattr(p, "emit:goinput", ln);
|
||||
} else {
|
||||
String *ivar = NewString("");
|
||||
Printf(ivar, "_swig_i_%d", i);
|
||||
|
|
@ -1154,19 +1161,22 @@ private:
|
|||
Printv(f_go_wrappers, goin, NULL);
|
||||
Delete(goin);
|
||||
Printv(call, ivar, NULL);
|
||||
Setattr(p, "emit:goinput", ivar);
|
||||
}
|
||||
|
||||
p = nextParm(p);
|
||||
}
|
||||
Printv(call, ")\n", NULL);
|
||||
|
||||
if (gccgo_flag && (goout != NULL || is_constructor)) {
|
||||
if (gccgo_flag && (has_goout || is_constructor)) {
|
||||
Printv(call, "\t}()\n", NULL);
|
||||
}
|
||||
|
||||
Printv(f_go_wrappers, call, NULL);
|
||||
Delete(call);
|
||||
|
||||
goargout(parms, parm_count);
|
||||
|
||||
if (need_return_var) {
|
||||
if (goout == NULL) {
|
||||
Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
|
||||
|
|
@ -1676,6 +1686,32 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* goargout()
|
||||
*
|
||||
* Handle Go argument output code if any. This is used for the Go
|
||||
* function. This assumes that each parameter has an "emit:goinput"
|
||||
* property with the name to use to refer to that parameter.
|
||||
* ----------------------------------------------------------------------- */
|
||||
|
||||
void goargout(ParmList *parms, int parm_count) {
|
||||
Parm *p = parms;
|
||||
for (int i = 0; i < parm_count; ++i) {
|
||||
p = getParm(p);
|
||||
String *tm = Getattr(p, "tmap:goargout");
|
||||
if (!tm) {
|
||||
p = nextSibling(p);
|
||||
} else {
|
||||
tm = Copy(tm);
|
||||
Replaceall(tm, "$result", "swig_r");
|
||||
Replaceall(tm, "$input", Getattr(p, "emit:goinput"));
|
||||
Printv(f_go_wrappers, tm, NULL);
|
||||
Delete(tm);
|
||||
p = Getattr(p, "tmap:goargout:next");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------
|
||||
* freearg()
|
||||
*
|
||||
|
|
@ -3170,6 +3206,7 @@ private:
|
|||
Swig_typemap_attach_parms("directorargout", parms, w);
|
||||
Swig_typemap_attach_parms("godirectorin", parms, w);
|
||||
Swig_typemap_attach_parms("goin", parms, dummy);
|
||||
Swig_typemap_attach_parms("goargout", parms, dummy);
|
||||
|
||||
if (!is_ignored) {
|
||||
// We use an interface to see if this method is defined in Go.
|
||||
|
|
@ -3282,16 +3319,29 @@ private:
|
|||
if (GetFlag(n, "abstract")) {
|
||||
Printv(f_go_wrappers, "\tpanic(\"call to pure virtual method\")\n", NULL);
|
||||
} else {
|
||||
bool has_goout = false;
|
||||
String *goout = NULL;
|
||||
if (SwigType_type(result) != T_VOID) {
|
||||
Printv(f_go_wrappers, "\tvar swig_r ", goImType(n, result), "\n", NULL);
|
||||
goout = Swig_typemap_lookup("goout", n, "swig_r", NULL);
|
||||
if (goout) {
|
||||
has_goout = true;
|
||||
}
|
||||
}
|
||||
|
||||
p = parms;
|
||||
for (int i = 0; i < parm_count; ++i) {
|
||||
p = getParm(p);
|
||||
if (Getattr(p, "tmap:goargout")) {
|
||||
has_goout = true;
|
||||
}
|
||||
p = nextParm(p);
|
||||
}
|
||||
|
||||
String *call = NewString("");
|
||||
|
||||
if (gccgo_flag) {
|
||||
if (goout != NULL) {
|
||||
if (has_goout) {
|
||||
Printv(call, "\tfunc() {\n", NULL);
|
||||
}
|
||||
Printv(call, "\tdefer SwigCgocallDone()\n", NULL);
|
||||
|
|
@ -3320,6 +3370,7 @@ private:
|
|||
if (goTypeIsInterface(p, pt)) {
|
||||
Printv(call, ".Swigcptr()", NULL);
|
||||
}
|
||||
Setattr(p, "emit:goinput", ln);
|
||||
} else {
|
||||
String *ivar = NewString("");
|
||||
Printf(ivar, "_swig_i_%d", i);
|
||||
|
|
@ -3331,7 +3382,7 @@ private:
|
|||
Printv(f_go_wrappers, goin, NULL);
|
||||
Delete(goin);
|
||||
Printv(call, ivar, NULL);
|
||||
Delete(ivar);
|
||||
Setattr(p, "emit:goinput", ivar);
|
||||
}
|
||||
|
||||
p = nextParm(p);
|
||||
|
|
@ -3339,13 +3390,15 @@ private:
|
|||
|
||||
Printv(call, ")\n", NULL);
|
||||
|
||||
if (gccgo_flag && goout != NULL) {
|
||||
if (gccgo_flag && has_goout) {
|
||||
Printv(call, "\t}()\n", NULL);
|
||||
}
|
||||
|
||||
Printv(f_go_wrappers, call, NULL);
|
||||
Delete(call);
|
||||
|
||||
goargout(parms, parm_count);
|
||||
|
||||
if (SwigType_type(result) != T_VOID) {
|
||||
if (goout == NULL) {
|
||||
Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
|
||||
|
|
@ -3526,6 +3579,7 @@ private:
|
|||
String *goin = Getattr(p, "tmap:goin");
|
||||
if (goin == NULL) {
|
||||
Printv(call, ln, NULL);
|
||||
Setattr(p, "emit:goinput", ln);
|
||||
} else {
|
||||
String *ivar = NewString("");
|
||||
Printf(ivar, "_swig_i_%d", i);
|
||||
|
|
@ -3537,7 +3591,7 @@ private:
|
|||
Printv(f_go_wrappers, goin, NULL);
|
||||
Delete(goin);
|
||||
Printv(call, ivar, NULL);
|
||||
Delete(ivar);
|
||||
Setattr(p, "emit:goinput", ivar);
|
||||
}
|
||||
|
||||
Delete(ln);
|
||||
|
|
@ -3554,6 +3608,8 @@ private:
|
|||
Printv(f_go_wrappers, call, NULL);
|
||||
Delete(call);
|
||||
|
||||
goargout(parms, parm_count);
|
||||
|
||||
if (SwigType_type(result) != T_VOID) {
|
||||
if (goout == NULL) {
|
||||
Printv(f_go_wrappers, "\treturn swig_r\n", NULL);
|
||||
|
|
@ -3838,6 +3894,21 @@ private:
|
|||
|
||||
Printv(w->code, " crosscall2(", callback_wname, ", &swig_a, (int) sizeof swig_a);\n", NULL);
|
||||
|
||||
/* Marshal outputs */
|
||||
for (p = parms; p;) {
|
||||
String *tm;
|
||||
if ((tm = Getattr(p, "tmap:directorargout"))) {
|
||||
tm = Copy(tm);
|
||||
Replaceall(tm, "$result", "jresult");
|
||||
Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
|
||||
Printv(w->code, tm, "\n", NIL);
|
||||
Delete(tm);
|
||||
p = Getattr(p, "tmap:directorargout:next");
|
||||
} else {
|
||||
p = nextSibling(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (SwigType_type(result) != T_VOID) {
|
||||
String *result_str = NewString("c_result");
|
||||
String *tm = Swig_typemap_lookup("directorout", n, result_str, NULL);
|
||||
|
|
@ -3886,7 +3957,7 @@ private:
|
|||
|
||||
String *pn = NewString("g");
|
||||
Append(pn, Getattr(p, "lname"));
|
||||
Setattr(p, "emit:input", pn);
|
||||
Setattr(p, "emit:directorinput", pn);
|
||||
|
||||
String *tm = gccgoCTypeForGoValue(n, Getattr(p, "type"), pn);
|
||||
Wrapper_add_local(w, pn, tm);
|
||||
|
|
@ -3915,6 +3986,21 @@ private:
|
|||
}
|
||||
Printv(w->code, callback_wname, "(go_val", args, ");\n", NULL);
|
||||
|
||||
/* Marshal outputs */
|
||||
for (p = parms; p;) {
|
||||
String *tm;
|
||||
if ((tm = Getattr(p, "tmap:directorargout"))) {
|
||||
tm = Copy(tm);
|
||||
Replaceall(tm, "$result", "jresult");
|
||||
Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
|
||||
Printv(w->code, tm, "\n", NIL);
|
||||
Delete(tm);
|
||||
p = Getattr(p, "tmap:directorargout:next");
|
||||
} else {
|
||||
p = nextSibling(p);
|
||||
}
|
||||
}
|
||||
|
||||
if (SwigType_type(result) != T_VOID) {
|
||||
String *result_str = NewString("c_result");
|
||||
String *tm = Swig_typemap_lookup("directorout", n, result_str, NULL);
|
||||
|
|
@ -3933,21 +4019,6 @@ private:
|
|||
Delete(result_str);
|
||||
}
|
||||
}
|
||||
|
||||
/* Marshal outputs */
|
||||
for (p = parms; p;) {
|
||||
String *tm;
|
||||
if ((tm = Getattr(p, "tmap:directorargout"))) {
|
||||
tm = Copy(tm);
|
||||
Replaceall(tm, "$result", "jresult");
|
||||
Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
|
||||
Printv(w->code, tm, "\n", NIL);
|
||||
Delete(tm);
|
||||
p = Getattr(p, "tmap:directorargout:next");
|
||||
} else {
|
||||
p = nextSibling(p);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(is_pure_virtual);
|
||||
Printv(w->code, " _swig_gopanic(\"call to pure virtual function ", Getattr(parent, "sym:name"), name, "\");\n", NULL);
|
||||
|
|
@ -4990,6 +5061,17 @@ private:
|
|||
String *gcCTypeForGoValue(Node *n, SwigType *type, String *name) {
|
||||
bool is_interface;
|
||||
String *gt = goTypeWithInfo(n, type, true, &is_interface);
|
||||
|
||||
String *tail = NewString("");
|
||||
SwigType *t = SwigType_typedef_resolve_all(type);
|
||||
if (!SwigType_isreference(t)) {
|
||||
while (Strncmp(gt, "*", 1) == 0) {
|
||||
Replace(gt, "*", "", DOH_REPLACE_FIRST);
|
||||
Printv(tail, "*", NULL);
|
||||
}
|
||||
}
|
||||
Delete(t);
|
||||
|
||||
bool is_string = Strcmp(gt, "string") == 0;
|
||||
bool is_slice = Strncmp(gt, "[]", 2) == 0;
|
||||
bool is_function = Strcmp(gt, "_swig_fnptr") == 0;
|
||||
|
|
@ -5017,18 +5099,21 @@ private:
|
|||
if (is_string) {
|
||||
// Note that we don't turn a reference to a string into a
|
||||
// pointer to a string. Strings are immutable anyhow.
|
||||
ret = NewString("_gostring_ ");
|
||||
Append(ret, name);
|
||||
ret = NewString("");
|
||||
Printv(ret, "_gostring_", tail, " ", name, NULL);
|
||||
Delete(tail);
|
||||
return ret;
|
||||
} else if (is_slice) {
|
||||
// Slices are always passed as a _goslice_, whether or not references
|
||||
// are involved.
|
||||
ret = NewString("_goslice_ ");
|
||||
Append(ret, name);
|
||||
ret = NewString("");
|
||||
Printv(ret, "_goslice_", tail, " ", name, NULL);
|
||||
Delete(tail);
|
||||
return ret;
|
||||
} else if (is_function || is_member) {
|
||||
ret = NewString("void *");
|
||||
Append(ret, name);
|
||||
ret = NewString("");
|
||||
Printv(ret, "void*", tail, " ", name, NULL);
|
||||
Delete(tail);
|
||||
return ret;
|
||||
} else if (is_complex64) {
|
||||
ret = NewString("_Complex float ");
|
||||
|
|
@ -5045,6 +5130,7 @@ private:
|
|||
SwigType_add_pointer(t);
|
||||
ret = SwigType_lstr(t, name);
|
||||
Delete(t);
|
||||
Delete(tail);
|
||||
return ret;
|
||||
} else {
|
||||
SwigType *t = SwigType_typedef_resolve_all(type);
|
||||
|
|
@ -5068,6 +5154,7 @@ private:
|
|||
}
|
||||
Delete(q);
|
||||
Delete(t);
|
||||
Delete(tail);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -5100,14 +5187,17 @@ private:
|
|||
} else if (is_float64) {
|
||||
ret = NewString("double ");
|
||||
} else {
|
||||
Delete(tail);
|
||||
return SwigType_lstr(type, name);
|
||||
}
|
||||
}
|
||||
|
||||
Append(ret, tail);
|
||||
if (SwigType_isreference(type)) {
|
||||
Append(ret, "* ");
|
||||
}
|
||||
Append(ret, name);
|
||||
Delete(tail);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue