/* * mwrap-cgen.cc * Generate C MEX file from MWrap AST. * * Copyright (c) 2007 David Bindel * See the file COPYING for copying permissions */ #include #include #include #include #include "mwrap-ast.h" #include "mwrap-support.h" /* -- General utility functions -- */ /* * Check whether a tinfo is an array type */ bool is_array(int tinfo) { return (tinfo == VT_array || tinfo == VT_carray || tinfo == VT_zarray); } /* * Check whether a tinfo is an array type */ bool is_obj(int tinfo) { return (tinfo == VT_obj || tinfo == VT_p_obj || tinfo == VT_r_obj); } /* * A function has a nullable return value if it returns a non-object * type for which a NULL pointer value makes sense. */ bool nullable_return(Func* f) { return (f->ret && (f->ret->tinfo == VT_string || is_array(f->ret->tinfo) || f->ret->tinfo == VT_p_scalar || f->ret->tinfo == VT_p_cscalar || f->ret->tinfo == VT_p_zscalar)); } /* * Get the name of a variable into the provided buffer. */ const char* vname(Var* v, char* buf) { if (v->iospec == 'o') sprintf(buf, "out%d_", v->output_label); else sprintf(buf, "in%d_", v->input_label); return buf; } /* * Check whether there are any FORTRAN routines in the list. */ bool has_fortran(Func* f) { for (; f; f=f->next) if (f->fort) return true; return false; } /* * Check whether the specified variable is complex */ bool complex_tinfo(Var* v) { return (v->tinfo == VT_carray || v->tinfo == VT_zarray || v->tinfo == VT_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_r_zscalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar); } /* * Count routines in the list. */ int max_routine_id(Func* f) { int maxid = 0; for (; f; f=f->next) if (f->id > maxid) maxid = f->id; return maxid; } /* -- Generate standard complex type definitions -- */ void mex_cpp_complex(FILE* fp) { fprintf(fp, "#include \n" "\n" "typedef std::complex dcomplex;\n" "#define real_dcomplex(z) std::real(z)\n" "#define imag_dcomplex(z) std::imag(z)\n" "#define setz_dcomplex(z,r,i) *z = dcomplex(r,i)\n" "\n" "typedef std::complex fcomplex;\n" "#define real_fcomplex(z) std::real(z)\n" "#define imag_fcomplex(z) std::imag(z)\n" "#define setz_fcomplex(z,r,i) *z = fcomplex(r,i)\n\n"); } void mex_c99_complex(FILE* fp) { fprintf(fp, "#include \n" "\n" "typedef _Complex double dcomplex;\n" "#define real_dcomplex(z) creal(z)\n" "#define imag_dcomplex(z) cimag(z)\n" "#define setz_dcomplex(z,r,i) *z = r + i*_Complex_I\n" "\n" "typedef _Complex float fcomplex;\n" "#define real_fcomplex(z) crealf(z)\n" "#define imag_fcomplex(z) cimagf(z)\n" "#define setz_fcomplex(z,r,i) *z = r + i*_Complex_I\n\n"); } /* -- Generate scalar conversion info -- */ /* * For each scalar type T, we define methods to convert a double array * to/from a T array, and to handle return of a pointer to a single T * (which might be NULL). */ void mex_define_copiers(FILE* fp, const char* name) { fprintf(fp, "mxWrapGetArrayDef(mxWrapGetArray_%s, %s)\n", name, name); fprintf(fp, "mxWrapCopyDef (mxWrapCopy_%s, %s)\n", name, name); fprintf(fp, "mxWrapReturnDef (mxWrapReturn_%s, %s)\n", name, name); } void mex_define_zcopiers(FILE* fp, const char* name, const char* ztype) { fprintf(fp, "mxWrapGetScalarZDef(mxWrapGetScalar_%s, %s,\n" " %s, setz_%s)\n", name, name, ztype, name); fprintf(fp, "mxWrapGetArrayZDef (mxWrapGetArray_%s, %s,\n" " %s, setz_%s)\n", name, name, ztype, name); fprintf(fp, "mxWrapCopyZDef (mxWrapCopy_%s, %s,\n" " real_%s, imag_%s)\n", name, name, name, name); fprintf(fp, "mxWrapReturnZDef (mxWrapReturn_%s, %s,\n" " real_%s, imag_%s)\n", name, name, name, name); } void mex_define_copiers(FILE* fp) { fprintf(fp, "/* Array copier definitions */\n"); for( set::iterator iter = scalar_decls.begin(); iter != scalar_decls.end(); ++iter) mex_define_copiers(fp, iter->c_str()); for( set::iterator iter = cscalar_decls.begin(); iter != cscalar_decls.end(); ++iter) mex_define_zcopiers(fp, iter->c_str(), "float"); for( set::iterator iter = zscalar_decls.begin(); iter != zscalar_decls.end(); ++iter) mex_define_zcopiers(fp, iter->c_str(), "double"); fprintf(fp, "\n"); } /* -- Handle FORTRAN name mangling -- */ /* * For each FORTRAN function, we define a macro version of the name * starting with MWF77_, which will handle the various name-mangling * issues that come up. We support three name mangling conventions for * now: all upper (no underscore mangling); all lower, single underscore; * and all lower, single underscore, double underscore for names that * contain an underscore (f2c). */ void mex_define_caps_fname(FILE* fp, Func* f) { fprintf(fp, "#define MWF77_%s ", f->funcv); for (char* s = f->funcv; *s; ++s) fputc(toupper(*s), fp); fprintf(fp, "\n"); } void mex_define_caps_fnames(FILE* fp, Func* f) { set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_define_caps_fname(fp, f); fnames.insert(f->funcv); } } } void mex_define_underscore1_fname(FILE* fp, Func* f) { fprintf(fp, "#define MWF77_%s ", f->funcv); for (char* s = f->funcv; *s; ++s) fputc(tolower(*s), fp); fprintf(fp, "_\n"); } void mex_define_underscore1_fnames(FILE* fp, Func* f) { set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_define_underscore1_fname(fp, f); fnames.insert(f->funcv); } } } void mex_define_f2c_fname(FILE* fp, Func* f) { fprintf(fp, "#define MWF77_%s ", f->funcv); bool has_underscore = false; for (char* s = f->funcv; *s; ++s) { has_underscore = (has_underscore || (*s == '_')); fputc(tolower(*s), fp); } if (has_underscore) fprintf(fp, "__\n"); else fprintf(fp, "_\n"); } void mex_define_f2c_fnames(FILE* fp, Func* f) { set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_define_f2c_fname(fp, f); fnames.insert(f->funcv); } } } void mex_define_fnames(FILE* fp, Func* f) { fprintf(fp, "#if defined(MWF77_CAPS)\n"); mex_define_caps_fnames(fp, f); fprintf(fp, "#elif defined(MWF77_UNDERSCORE1)\n"); mex_define_underscore1_fnames(fp, f); fprintf(fp, "#else /* f2c convention */\n"); mex_define_f2c_fnames(fp, f); fprintf(fp, "#endif\n\n"); } /* -- Handle FORTRAN declarations -- */ /* * We assume unless told otherwise that all FORTRAN functions need * prototypes, which we will generate. */ void mex_fortran_arg(FILE* fp, Var* v) { if (!v) return; if (v->tinfo == VT_mx) fprintf(fp, "const mxArray*", v->name); else fprintf(fp, "%s*", v->basetype, v->name); if (v->next) { fprintf(fp, ", "); mex_fortran_arg(fp, v->next); } } void mex_fortran_decl(FILE* fp, Func* f) { if (f->ret) fprintf(fp, "%s ", f->ret->basetype); else fprintf(fp, "MWF77_RETURN "); fprintf(fp, "MWF77_%s(", f->funcv); mex_fortran_arg(fp, f->args); fprintf(fp, ");\n"); } void mex_fortran_decls(FILE* fp, Func* f) { fprintf(fp, "#ifdef __cplusplus\n" "extern \"C\" { /* Prevent C++ name mangling */\n" "#endif\n\n" "#ifndef MWF77_RETURN\n" "#define MWF77_RETURN int\n" "#endif\n\n"); set fnames; for (; f; f = f->next) { if (f->fort && fnames.find(f->funcv) == fnames.end()) { mex_fortran_decl(fp, f); fnames.insert(f->funcv); } } fprintf(fp, "\n#ifdef __cplusplus\n" "} /* end extern C */\n" "#endif\n\n"); } /* -- Generate class conversion info -- */ /* * For every parent class, we define a getter method that can take a * string of the form "T:value" and interpret the value as a pointer * to T for every child class T. We want to get an *exact* match with * the child pointer type and then cast that pointer to the parent type * in the C++ code, because otherwise we don't get the right behavior * with multiple inheritance (i.e. when a cast actually changes the pointer * value). */ void mex_casting_getter_type(FILE* fp, const char* name) { fprintf(fp, " %s* p_%s = NULL;\n" " sscanf(pbuf, \"%s:%%p\", &p_%s);\n" " if (p_%s)\n" " return p_%s;\n\n", name, name, name, name, name, name); } void mex_casting_getter(FILE* fp, const char* cname, InheritsDecl* inherits) { fprintf(fp, "\n%s* mxWrapGetP_%s(const mxArray* a, const char** e)\n", cname, cname); fprintf(fp, "{\n" " char pbuf[128];\n" " if (mxGetClassID(a) == mxDOUBLE_CLASS &&\n" " mxGetM(a)*mxGetN(a) == 1 && *mxGetPr(a) == 0)\n" " return NULL;\n" " if (!mxIsChar(a)) {\n" "#ifdef R2008OO\n" " mxArray* ap = mxGetProperty(a, 0, \"mwptr\");\n" " if (ap)\n" " return mxWrapGetP_%s(ap, e);\n" "#endif\n" " *e = \"Invalid pointer\";\n" " return NULL;\n" " }\n" " mxGetString(a, pbuf, sizeof(pbuf));\n\n", cname); mex_casting_getter_type(fp, cname); for (InheritsDecl* i = inherits; i; i = i->next) mex_casting_getter_type(fp, i->name); fprintf(fp, " *e = \"Invalid pointer to %s\";\n" " return NULL;\n" "}\n\n", cname); } void mex_casting_getters(FILE* fp) { map::iterator i = class_decls.begin(); for (; i != class_decls.end(); ++i) mex_casting_getter(fp, i->first.c_str(), i->second); } void mex_cast_get_p(FILE* fp, const char* basetype, int input_label) { fprintf(fp, " in%d_ = ", input_label); if (is_mxarray_type(basetype)) fprintf(fp, "mxWrapGet_%s(prhs[%d], &mw_err_txt_);\n", basetype, input_label); else if (class_decls.find(basetype) == class_decls.end()) fprintf(fp, "(%s*) mxWrapGetP(prhs[%d], \"%s:%%p\", &mw_err_txt_);\n", basetype, input_label, basetype); else fprintf(fp, "mxWrapGetP_%s(prhs[%d], &mw_err_txt_);\n", basetype, input_label); fprintf(fp, " if (mw_err_txt_)\n" " goto mw_err_label;\n"); } /* -- Mex stub variable declarations -- */ /* * In each stub function, we declare local variables corresponding to * arguments (input and output), return values, and dimension parameters. * We use the names inX_, outX_, and dimX_ to represent input/inout, output * and dimension parameters, where X is the index in the prhs array or * plhs array. */ void mex_declare_type(char* typebuf, Var* v) { if (is_obj(v->tinfo) || is_array(v->tinfo)) sprintf(typebuf, "%s*", v->basetype); else if (v->tinfo == VT_rarray) sprintf(typebuf, "const %s*", v->basetype); else if (v->tinfo == VT_scalar || v->tinfo == VT_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_scalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_r_zscalar || v->tinfo == VT_p_scalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) sprintf(typebuf, "%s", v->basetype); else if (v->tinfo == VT_string) sprintf(typebuf, "char*"); else if (v->tinfo == VT_mx && v->iospec == 'i') sprintf(typebuf, "const mxArray*"); else if (v->tinfo == VT_mx && v->iospec == 'o') sprintf(typebuf, "mxArray*"); else { fprintf(stderr, "v->tinfo == %d; v->name = %s\n", v->tinfo, v->name); assert(0); } } void mex_declare_in_args(FILE* fp, Var* v) { if (!v) return; if ((v->iospec == 'i' || v->iospec == 'b') && v->tinfo != VT_const) { char typebuf[128]; mex_declare_type(typebuf, v); if (is_array(v->tinfo) || is_obj(v->tinfo) || v->tinfo == VT_string) { fprintf(fp, " %-10s in%d_ =0; /* %-10s */\n", typebuf, v->input_label, v->name); } else { fprintf(fp, " %-10s in%d_; /* %-10s */\n", typebuf, v->input_label, v->name); } } mex_declare_in_args(fp, v->next); } void mex_declare_out_args(FILE* fp, Var* v) { if (!v) return; if (v->iospec == 'o' && v->tinfo != VT_mx) { char typebuf[128]; mex_declare_type(typebuf, v); if (is_array(v->tinfo) || is_obj(v->tinfo) || v->tinfo == VT_string) { fprintf(fp, " %-10s out%d_=0; /* %-10s */\n", typebuf, v->output_label, v->name); } else { fprintf(fp, " %-10s out%d_; /* %-10s */\n", typebuf, v->output_label, v->name); } } mex_declare_out_args(fp, v->next); } void mex_declare_dim_args(FILE* fp, Expr* e) { if (!e) return; fprintf(fp, " %-10s dim%d_; /* %-10s */\n", "mwSize", e->input_label, e->value); mex_declare_dim_args(fp, e->next); } void mex_declare_dim_args(FILE* fp, Var* v) { if (!v) return; if (v->qual) mex_declare_dim_args(fp, v->qual->args); mex_declare_dim_args(fp, v->next); } void mex_declare_return(FILE* fp, Var* v) { mex_declare_out_args(fp, v); } void mex_declare_args(FILE* fp, Func* f) { if (f->thisv) { char typebuf[128]; strcpy(typebuf, f->classv); strcat(typebuf, "*"); fprintf(fp, " %-10s in%d_ =0; /* %-10s */\n", typebuf, 0, f->thisv); } mex_declare_in_args(fp, f->args); if (!nullable_return(f)) mex_declare_return(fp, f->ret); mex_declare_out_args(fp, f->args); mex_declare_dim_args(fp, f->ret); mex_declare_dim_args(fp, f->args); if (f->ret || f->args || f->thisv) fprintf(fp, "\n"); } /* -- Mex stub dimension retrieval -- */ /* * After declaring local variables, we copy in the dimension arguments. */ int mex_unpack_dims(FILE* fp, Expr* e) { if (!e) return 0; fprintf(fp, " dim%d_ = (mwSize) mxWrapGetScalar(prhs[%d], &mw_err_txt_);\n", e->input_label, e->input_label); return mex_unpack_dims(fp, e->next)+1; } int mex_unpack_dims(FILE* fp, Var* v) { if (!v) return 0; if (v->qual) return mex_unpack_dims(fp, v->qual->args) + mex_unpack_dims(fp, v->next); return mex_unpack_dims(fp, v->next); } void mex_unpack_dims(FILE* fp, Func* f) { if (mex_unpack_dims(fp, f->ret) || mex_unpack_dims(fp, f->args)) fprintf(fp, "\n"); } /* -- Mex stub dimension checks -- */ /* * For input and inout arrays where the dimensions are given explicitly * in the call, we check that the dimensions of the MATLAB array passed * in agree with the dimensions in the dimension arguments. */ void mex_check_dims(FILE* fp, Var* v) { if (!v) return; if (v->iospec != 'o' && is_array(v->tinfo) && v->qual && v->qual->args) { Expr* a = v->qual->args; if (a->next) { fprintf(fp, " if (mxGetM(prhs[%d]) != dim%d_ ||\n" " mxGetN(prhs[%d]) != dim%d_) {\n" " mw_err_txt_ = \"Bad argument size: %s\";\n" " goto mw_err_label;\n" " }\n\n", v->input_label, a->input_label, v->input_label, a->next->input_label, v->name); } else { fprintf(fp, " if (mxGetM(prhs[%d])*mxGetN(prhs[%d]) != dim%d_) {\n" " mw_err_txt_ = \"Bad argument size: %s\";" " goto mw_err_label;\n" " }\n\n", v->input_label, v->input_label, a->input_label, v->name); } } mex_check_dims(fp, v->next); } void mex_check_dims(FILE* fp, Func* f) { if (!fp) return; mex_check_dims(fp, f->args); } /* -- Output size data -- */ /* * Generate code to compute the size of an array from dim arguments. */ void mex_alloc_size1(FILE* fp, Expr* e) { if (!e) return; fprintf(fp, "dim%d_", e->input_label); if (e->next) { fprintf(fp, "*"); mex_alloc_size1(fp, e->next); } } void mex_alloc_size(FILE* fp, Expr* e) { if (!e) fprintf(fp, "1"); else mex_alloc_size1(fp, e); } /* -- Unpack input data -- */ /* * Generate code to copy data from the prhs array into the inX_ * local variables in a stub function. Note that for input strings, * we take any explicit dimension information as what the user wants -- * if the string is too long to fit in that explicit dimension, our * generated code will return with an error message. */ void mex_unpack_input_array(FILE* fp, Var* v) { fprintf(fp, " if (mxGetM(prhs[%d])*mxGetN(prhs[%d]) != 0) {\n", v->input_label, v->input_label); if (strcmp(v->basetype, "double") == 0 && v->iospec == 'i') fprintf(fp, " in%d_ = mxGetPr(prhs[%d]);\n", v->input_label, v->input_label); else fprintf(fp, " in%d_ = mxWrapGetArray_%s(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->basetype, v->input_label); fprintf(fp, " } else\n" " in%d_ = NULL;\n", v->input_label, v->basetype); } void mex_unpack_input_string(FILE* fp, Var* v) { if (!(v->qual && v->qual->args)) fprintf(fp, " in%d_ = mxWrapGetString(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->input_label); else { fprintf(fp, " in%d_ = (char*) mxMalloc(", v->input_label); mex_alloc_size(fp, v->qual->args); fprintf(fp, "*sizeof(char));\n"); fprintf(fp, " if (mxGetString(prhs[%d], in%d_, ", v->input_label, v->input_label); mex_alloc_size(fp, v->qual->args); fprintf(fp, ") != 0) {\n" " mw_err_txt_ = \"Invalid string argument\";\n" " goto mw_err_label;\n" " }\n"); } } void mex_unpack_inputs(FILE* fp, Var* v) { if (!v) return; if (v->iospec == 'o') { mex_unpack_inputs(fp, v->next); return; } if (is_obj(v->tinfo)) mex_cast_get_p(fp, v->basetype, v->input_label); else if (is_array(v->tinfo)) mex_unpack_input_array(fp, v); else if (v->tinfo == VT_scalar || v->tinfo == VT_r_scalar || v->tinfo == VT_p_scalar) fprintf(fp, " in%d_ = (%s) mxWrapGetScalar(prhs[%d], &mw_err_txt_);\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", v->input_label, v->basetype, v->input_label); else if (v->tinfo == VT_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_r_zscalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) fprintf(fp, " mxWrapGetScalar_%s(&in%d_, prhs[%d]);\n", v->basetype, v->input_label, v->input_label); else if (v->tinfo == VT_string) mex_unpack_input_string(fp, v); else if (v->tinfo == VT_mx) fprintf(fp, " in%d_ = prhs[%d];\n", v->input_label, v->input_label); mex_unpack_inputs(fp, v->next); } void mex_unpack_inputs(FILE* fp, Func* f) { if (f->thisv) mex_cast_get_p(fp, f->classv, 0); mex_unpack_inputs(fp, f->args); } /* -- Check input data -- */ /* * If an input variable is declared to be an object or a reference to * an object, we check to make sure the pointer passed in is not NULL. */ void mex_check_inputs(FILE* fp, Var* v) { if (!v) return; if (v->iospec != 'o' && (v->tinfo == VT_obj || v->tinfo == VT_r_obj)) fprintf(fp, " if (!in%d_) {\n" " mw_err_txt_ = \"Argument %s cannot be null\";\n" " goto mw_err_label;\n" " }\n", v->input_label, v->name); mex_check_inputs(fp, v->next); } void mex_check_inputs(FILE* fp, Func* f) { mex_check_inputs(fp, f->args); } /* -- Allocate output data -- */ /* * When we allocate data for output variables, we only allocate data * for *pure* output variables. For input/output variables, we'll use * the scratch copy of the input as the scratch copy of the output, too. */ void mex_alloc_output(FILE* fp, Var* v, bool return_flag) { if (!v) return; if (v->iospec == 'o') { if (!return_flag && is_obj(v->tinfo) && is_mxarray_type(v->basetype)) { fprintf(fp, " out%d_ = mxWrapAlloc_%s();\n", v->output_label, v->basetype); } else if (is_array(v->tinfo)) { fprintf(fp, " out%d_ = (%s*) mxMalloc(", v->output_label, v->basetype); mex_alloc_size(fp, v->qual->args); fprintf(fp, "*sizeof(%s));\n", v->basetype); } else if (v->tinfo == VT_rarray) { fprintf(fp, " out%d_ = (%s*) NULL;\n", v->output_label, v->basetype); } else if (v->tinfo == VT_string) { fprintf(fp, " out%d_ = (char*) mxMalloc(", v->output_label); mex_alloc_size(fp, v->qual->args); fprintf(fp, "*sizeof(char));\n"); } } mex_alloc_output(fp, v->next, return_flag); } void mex_alloc_output(FILE* fp, Func* f) { if (!nullable_return(f)) mex_alloc_output(fp, f->ret, true); mex_alloc_output(fp, f->args, false); } /* -- Record that this routine was called -- */ /* * For coverage testing, we keep a counter of the number of times each * stub is called. */ void mex_record_call(FILE* fp, Func* f) { fprintf(fp, " if (mexprofrecord_)\n" " mexprofrecord_[%d]++;\n", f->id); } /* -- Make the call -- */ /* * The only non-obvious aspect of the call line is the way that return * values are treated. The biggest issue is that functions can return * NULL, and we need to present that to the user in some reasonable * way that won't be confused with an ordinary return -- this is why * we use 0 to represent a null return for a string or an object, but * we use an empty array to represent a null return for a numeric * object. A secondary issue has to do with strings -- we won't know the * size of a return string until after we have it in hand. Both because * of the possibility of NULL returns and because of the indeterminate size * of strings, we allocate space to hold return values *with* the call, * rather than setting up space before the call. */ void mex_make_call(FILE* fp, Var* v, int first) { if (!v) return; if (!first) fprintf(fp, ", "); char namebuf[128]; if (v->tinfo == VT_obj || v->tinfo == VT_r_obj) fprintf(fp, "*%s", vname(v, namebuf)); else if (v->tinfo == VT_mx && v->iospec == 'o') fprintf(fp, "plhs+%d", v->output_label); else if (v->tinfo == VT_p_scalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) fprintf(fp, "&%s", vname(v, namebuf)); else if (v->tinfo == VT_const) fprintf(fp, "%s", v->name); else fprintf(fp, "%s", vname(v, namebuf)); mex_make_call(fp, v->next, 0); } void mex_make_call(FILE* fp, Func* f) { if (f->thisv) fprintf(fp, "in0_->"); if (strcmp(f->funcv, "new") == 0) fprintf(fp, "new %s(", f->classv); else { if (f->fort) fprintf(fp, "MWF77_"); fprintf(fp, "%s(", f->funcv); } mex_make_call(fp, f->args, 1); fprintf(fp, ")"); } void mex_make_stmt(FILE* fp, Func* f) { if (!f) return; if (f->thisv) fprintf(fp, " if (!in0_) {\n" " mw_err_txt_ = \"Cannot dispatch to NULL\";\n" " goto mw_err_label;\n" " }\n"); if (mw_generate_catch) fprintf(fp, " try {\n "); if (f->ret) { Var* v = f->ret; if (v->tinfo == VT_obj) { if (is_mxarray_type(v->basetype)) { fprintf(fp, " plhs[0] = mxWrapSet_%s(&(", v->basetype); mex_make_call(fp, f); fprintf(fp ,"));\n"); } else { fprintf(fp, " out0_ = new %s(", v->basetype); mex_make_call(fp, f); fprintf(fp ,");\n"); } } else if (is_array(v->tinfo)) { fprintf(fp, " plhs[0] = mxWrapReturn_%s(", v->basetype); mex_make_call(fp, f); fprintf(fp, ", "); Expr* e = v->qual->args; if (e && e->next && !e->next->next) { fprintf(fp, " dim%d_, dim%d_);\n", e->input_label, e->next->input_label); } else { mex_alloc_size(fp, v->qual->args); fprintf(fp, ", 1);\n"); } } else if (v->tinfo == VT_scalar || v->tinfo == VT_r_scalar || v->tinfo == VT_cscalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_zscalar) { fprintf(fp, " out0_ = "); mex_make_call(fp, f); fprintf(fp, ";\n"); } else if (v->tinfo == VT_string) { fprintf(fp, " plhs[0] = mxWrapStrncpy("); mex_make_call(fp, f); fprintf(fp, ");\n"); } else if (v->tinfo == VT_mx) { fprintf(fp, " plhs[0] = "); mex_make_call(fp, f); fprintf(fp, ";\n"); } else if (v->tinfo == VT_p_obj) { if (is_mxarray_type(v->basetype)) { fprintf(fp, " plhs[0] = mxWrapSet_%s(", v->basetype); mex_make_call(fp, f); fprintf(fp, ");\n"); } else { fprintf(fp, " out0_ = "); mex_make_call(fp, f); fprintf(fp, ";\n"); } } else if (v->tinfo == VT_p_scalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) { fprintf(fp, " plhs[0] = mxWrapReturn_%s(", v->basetype); mex_make_call(fp, f); fprintf(fp, ", 1, 1);\n"); } else if (v->tinfo == VT_r_obj) { if (is_mxarray_type(v->basetype)) { fprintf(fp, " plhs[0] = mxWrapSet_%s(&(", v->basetype); mex_make_call(fp, f); fprintf(fp, "));\n"); } else { fprintf(fp, " out0_ = &("); mex_make_call(fp, f); fprintf(fp, ");\n"); } } } else { fprintf(fp, " "); mex_make_call(fp, f); fprintf(fp, ";\n"); } if (mw_generate_catch) fprintf(fp, " } catch(...) {\n" " mw_err_txt_ = \"Caught C++ exception from %s\";\n" " }\n" " if (mw_err_txt_)\n" " goto mw_err_label;\n", f->funcv); } /* -- Marshal the results -- */ /* * Copy inout arguments, output arguments, and any simple return * arguments into the plhs array. */ void mex_marshal_array(FILE* fp, Var* v) { Expr* e = v->qual->args; char namebuf[128]; const char* mtype = complex_tinfo(v) ? "mxCOMPLEX" : "mxREAL"; const char* ws = " "; if (v->tinfo == VT_rarray) { ws = " "; fprintf(fp, " if (out%d_ == NULL) {\n", v->output_label); fprintf(fp, " plhs[%d] = mxCreateDoubleMatrix(0,0, mxREAL);\n", v->output_label); fprintf(fp, " } else {\n"); } if (!e) { // No dimension info -- must be an inout array fprintf(fp, "%splhs[%d] = mxCreateDoubleMatrix(" "mxGetM(prhs[%d]), mxGetN(prhs[%d]), %s);\n", ws, v->output_label, v->input_label, v->input_label, mtype); fprintf(fp, "%smxWrapCopy_%s(plhs[%d], in%d_, ", ws, v->basetype, v->output_label, v->input_label); fprintf(fp, "mxGetM(prhs[%d])*mxGetN(prhs[%d])", v->input_label, v->input_label); fprintf(fp, ");\n"); } else if (!(e->next)) { // Only size given fprintf(fp, "%splhs[%d] = mxCreateDoubleMatrix(dim%d_, 1, %s);\n", ws, v->output_label, e->input_label, mtype); fprintf(fp, "%smxWrapCopy_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); fprintf(fp, "dim%d_", e->input_label); fprintf(fp, ");\n"); } else if (!(e->next->next)) { // Two dimensions given fprintf(fp, "%splhs[%d] = mxCreateDoubleMatrix(" "dim%d_, dim%d_, %s);\n", ws, v->output_label, e->input_label, e->next->input_label, mtype); fprintf(fp, "%smxWrapCopy_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); fprintf(fp, "dim%d_*dim%d_", e->input_label, e->next->input_label); fprintf(fp, ");\n"); } else { // Three or more dimensions given -- punt and make it 1D fprintf(fp, "%splhs[%d] = mxCreateDoubleMatrix(", ws, v->output_label); mex_alloc_size(fp, e); fprintf(fp, ", 1, %s);\n", mtype); fprintf(fp, "%smxWrapCopy_%s(plhs[%d], %s, ", ws, v->basetype, v->output_label, vname(v, namebuf)); mex_alloc_size(fp, e); fprintf(fp, ");\n"); } if (v->tinfo == VT_rarray) { fprintf(fp, " }\n"); } } void mex_marshal_result(FILE* fp, Var* v, bool return_flag) { char namebuf[128]; if (is_obj(v->tinfo) && is_mxarray_type(v->basetype)) { if (!return_flag) fprintf(fp, " plhs[%d] = mxWrapSet_%s(%s);\n", v->output_label, v->basetype, vname(v,namebuf)); } else if (is_obj(v->tinfo)) fprintf(fp, " plhs[%d] = mxWrapCreateP(out%d_, \"%s:%%p\");\n", v->output_label, v->output_label, v->basetype); else if (is_array(v->tinfo) || v->tinfo == VT_rarray) mex_marshal_array(fp, v); else if (v->tinfo == VT_scalar || v->tinfo == VT_r_scalar || v->tinfo == VT_p_scalar) fprintf(fp, " plhs[%d] = mxCreateDoubleMatrix(1, 1, mxREAL);\n" " *mxGetPr(plhs[%d]) = %s;\n", v->output_label, v->output_label, vname(v, namebuf)); else if (v->tinfo == VT_cscalar || v->tinfo == VT_zscalar || v->tinfo == VT_r_cscalar || v->tinfo == VT_r_zscalar || v->tinfo == VT_p_cscalar || v->tinfo == VT_p_zscalar) fprintf(fp, " plhs[%d] = mxCreateDoubleMatrix(1, 1, mxCOMPLEX);\n" " *mxGetPr(plhs[%d]) = real_%s(%s);\n" " *mxGetPi(plhs[%d]) = imag_%s(%s);\n", v->output_label, v->output_label, v->basetype, vname(v, namebuf), v->output_label, v->basetype, vname(v, namebuf)); else if (v->tinfo == VT_string) fprintf(fp, " plhs[%d] = mxCreateString(%s);\n", v->output_label, vname(v, namebuf)); } void mex_marshal_results(FILE* fp, Var* v, bool return_flag) { if (!v) return; if (v->iospec != 'i') mex_marshal_result(fp, v, return_flag); mex_marshal_results(fp, v->next, return_flag); } void mex_marshal_results(FILE* fp, Func* f) { if (!nullable_return(f)) mex_marshal_results(fp, f->ret, true); mex_marshal_results(fp, f->args, false); } /* -- Deallocate temporary data -- */ /* * Free scratch arrays allocated to hold input copies and output * storage. */ void mex_dealloc(FILE* fp, Var* v, bool return_flag) { if (!v) return; if (is_array(v->tinfo) || v->tinfo == VT_string) { if (v->iospec == 'o') fprintf(fp, " if (out%d_) mxFree(out%d_);\n", v->output_label, v->output_label); else if (v->iospec == 'b' || strcmp(v->basetype, "double") != 0) fprintf(fp, " if (in%d_) mxFree(in%d_);\n", v->input_label, v->input_label); } else if (is_obj(v->tinfo) && is_mxarray_type(v->basetype)) { if (v->iospec == 'i' || v->iospec == 'b') fprintf(fp, " if (in%d_) mxWrapFree_%s(in%d_);\n", v->input_label, v->basetype, v->input_label); else if (v->iospec == 'o' && !return_flag) fprintf(fp, " if (out%d_) mxWrapFree_%s(out%d_);\n", v->output_label, v->basetype, v->output_label); } mex_dealloc(fp, v->next, return_flag); } void mex_dealloc(FILE* fp, Func* f) { if (!nullable_return(f)) mex_dealloc(fp, f->ret, true); mex_dealloc(fp, f->args, false); } /* -- Generate mex stub -- */ /* * Print a MEX stub function. We generate one of these for each * distinct call line in the input files. */ void print_c_comment(FILE* fp, Func* f) { fprintf(fp, "/* ---- %s: %d ----\n * ", f->fname.c_str(), f->line); print(fp, f); for (Func* fsame = f->same_next; fsame; fsame = fsame->next) fprintf(fp, " * Also at %s: %d\n", fsame->fname.c_str(), fsame->line); fprintf(fp, " */\n"); } void print_mex_stub(FILE* fp, Func* f) { print_c_comment(fp, f); fprintf(fp, "const char* stubids%d_ = \"%s\";\n\n" "void mexStub%d(int nlhs, mxArray* plhs[],\n" " int nrhs, const mxArray* prhs[])\n" "{\n" " const char* mw_err_txt_ = 0;\n", f->id, id_string(f).c_str(), f->id); mex_declare_args(fp, f); mex_unpack_dims(fp, f); mex_check_dims(fp, f); mex_unpack_inputs(fp, f); mex_check_inputs(fp, f); mex_alloc_output(fp, f); mex_record_call(fp, f); mex_make_stmt(fp, f); mex_marshal_results(fp, f); fprintf(fp, "\nmw_err_label:\n"); mex_dealloc(fp, f); fprintf(fp, " if (mw_err_txt_)\n" " mexErrMsgTxt(mw_err_txt_);\n" "}\n\n"); } /* -- Generate profiler output routine -- */ /* * The profiler / coverage testing routines include starting, * ending, and reporting. Starting the profiler locks the MEX file * in memory. */ void make_profile_output(FILE* fp, Func* f, const char* printfunc) { fprintf(fp, " if (!mexprofrecord_)\n" " %s\"Profiler inactive\\n\");\n", printfunc); for (; f; f = f->next) { fprintf(fp, " %s\"%%d calls to %s:%d", printfunc, f->fname.c_str(), f->line); for (Func* fsame = f->same_next; fsame; fsame = fsame->next) fprintf(fp, " (%s:%d)", fsame->fname.c_str(), fsame->line); fprintf(fp, "\\n\", mexprofrecord_[%d]);\n", f->id); } } /* -- Generate MEX file -- */ /* * Generate the overall C file to be fed into MEX. This consists of * basic support code (from mwrap-support.c), type conversion * routines, all the call stubs, and a main dispatcher that decides * which stub to call. */ void print_mex_stubs(FILE* fp, Func* f) { for (; f; f = f->next) print_mex_stub(fp, f); } void print_mex_else_cases(FILE* fp, Func* f) { for (Func* fcall = f; fcall; fcall = fcall->next) fprintf(fp, " else if (strcmp(id, stubids%d_) == 0)\n" " mexStub%d(nlhs,plhs, nrhs-1,prhs+1);\n", fcall->id, fcall->id); int maxid = max_routine_id(f); fprintf(fp, " else if (strcmp(id, \"*profile on*\") == 0) {\n" " if (!mexprofrecord_) {\n" " mexprofrecord_ = (int*) malloc(%d * sizeof(int));\n" " mexLock();\n" " }\n" " memset(mexprofrecord_, 0, %d * sizeof(int));\n" " } else if (strcmp(id, \"*profile off*\") == 0) {\n" " if (mexprofrecord_) {\n" " free(mexprofrecord_);\n" " mexUnlock();\n" " }\n" " mexprofrecord_ = NULL;\n" " } else if (strcmp(id, \"*profile report*\") == 0) {\n", maxid+1, maxid+1); make_profile_output(fp, f, "mexPrintf("); fprintf(fp, " } else if (strcmp(id, \"*profile log*\") == 0) {\n" " FILE* logfp;\n" " if (nrhs != 2 || mxGetString(prhs[1], id, sizeof(id)) != 0)\n" " mexErrMsgTxt(\"Must have two string arguments\");\n" " logfp = fopen(id, \"w+\");\n" " if (!logfp)\n" " mexErrMsgTxt(\"Cannot open log for output\");\n"); make_profile_output(fp, f, "fprintf(logfp, "); fprintf(fp, " fclose(logfp);\n"); fprintf(fp, " } else\n" " mexErrMsgTxt(\"Unknown identifier\");\n"); } const char* mwrap_banner = "/* --------------------------------------------------- */\n" "/* Automatically generated by mwrap */\n" "/* --------------------------------------------------- */\n\n"; const char* mexBase = "/* ----\n" " */\n" "void mexFunction(int nlhs, mxArray* plhs[],\n" " int nrhs, const mxArray* prhs[])\n" "{\n" " char id[512];\n" " if (nrhs == 0) {\n" " mexPrintf(\"Mex function installed\\n\");\n" " return;\n" " }\n\n" " if (mxGetString(prhs[0], id, sizeof(id)) != 0)\n" " mexErrMsgTxt(\"Identifier should be a string\");\n"; void print_mex_file(FILE* fp, Func* f) { fprintf(fp, "%s", mwrap_banner); fprintf(fp, "%s", mex_header); if (mw_use_c99_complex) mex_c99_complex(fp); else if (mw_use_cpp_complex) mex_cpp_complex(fp); mex_define_copiers(fp); mex_casting_getters(fp); if (has_fortran(f)) { mex_define_fnames(fp, f); mex_fortran_decls(fp, f); } print_mex_stubs(fp, f); fprintf(fp, "%s", mexBase); print_mex_else_cases(fp, f); fprintf(fp, "}\n\n"); }