/* Copyright (C) 2001 John W. Eaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Octave; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 2001-11-20 Paul Kienzle * bring various bits of ov-builtin, ov-fcn, symtab, variables together to create this new file containing the new octave_dispatch type. */ #include #include #include #include #include #include #include // using std; // XXX FIXME XXX should be using a map from type_id->name, rather // than type_name->name typedef std::map Table; class octave_dispatch : public octave_function { public: // XXX FIXME XXX need to handle doc strings of dispatched functions, for // example, by appending "for (,...) see " for each // time dispatch(f,type,name) is called. octave_dispatch (symbol_record *s) : octave_function (s->name(), s->help()), map (new Table) { base.alias(s); } // XXX FIXME XXX if we get deleted, we should restore the original // symbol_record from base before dying.df ~octave_dispatch (void) { } octave_value_list do_multi_index_op (int, const octave_value_list&); void add (const std::string t, const std::string n) const { (*map)[t] = n; } void clear (const std::string t) const { map->erase(t); } void print (void) const { // XXX FIXME XXX shouldn't be using cout cout << "dispatched " << base.name() << endl; for (Table::iterator it = map->begin(); it != map->end(); it++) { cout << base.name() << "(" << it->first << ",...)->" << it->second << "(" << it->first << ",...)" << endl; } } private: Table *map; symbol_record base; octave_dispatch (void); octave_dispatch (const octave_dispatch& m); DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA DECLARE_OCTAVE_ALLOCATOR }; DEFINE_OCTAVE_ALLOCATOR (octave_dispatch); DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_dispatch, "dispatched function"); octave_value_list octave_dispatch::do_multi_index_op (int nargout, const octave_value_list& args) { octave_value_list retval; // cout << "entering dispatched do_multi_index_op" << endl; // If more than one argument, check if argument template matches any // dispatched functions. if (args.length() > 0 && map->count (args(0).type_name()) > 0) return feval ((*map)[args(0).type_name()], args, nargout); // No match, so evaluate the base function. Unwrapping feval, it // is just is_valid_function followed by do_multi_index_op(). The // routine is_valid_function() first checks if the function is in // the symbol table with sym_tab->lookup(), and then checks that it // is up to date with lookup(sym_rec). In our case we are // shadowing the base function definition so we don't want to do // the symbol table lookup, but we still need to check that it is // up to date. lookup(&base, false); // Extract the (possibly updated) definition from the symbol record. octave_value& ov = base.def(); octave_function *fcn = ov.function_value(true); if (error_state || fcn == NULL) { // XXX FIXME XXX wrong error("no base function for %s",base.name().c_str()); return retval; } // Apply the function to the arguments and return the results retval = fcn->do_multi_index_op (nargout, args); // Returning return retval; } DEFUN_DLD(dispatch, args, , "\ dispatch('f','type','name')\n\ \n\ Replaces the symbol for the name f with a dispatch\n\ function so that if f is called with the first argument\n\ of the given type, then all arguments are passed to\n\ the named alternative function instead.\n\ \n\ dispatch('f','type')\n\ \n\ Clear dispatch function associated with the given type\n\ \n\ dispatch('f')\n\ \n\ List dispatch functions\n\ ") { octave_value retval; int nargin = args.length(); if (nargin < 1 || nargin > 3) { print_usage("dispatch"); return retval; } std::string f,t,n; if (nargin > 0) f = args(0).string_value(); if (nargin > 1) t = args(1).string_value(); if (nargin > 2) n = args(2).string_value(); if (error_state) return retval; static bool type_loaded = false; // register dispatch function type if you have not already done so if (! type_loaded) { octave_dispatch::register_type (); // cerr << "installing dispatch type at type-id = " // << octave_dispatch::static_type_id () << "\n"; type_loaded = true; } // find the base function in the symbol table, loading it if it // is not already there; if it is already a dispatch, then bonus symbol_record *sr = global_sym_tab->lookup(f,true); if (sr->def().type_id() != octave_dispatch::static_type_id()) { // Not an dispatched name, so if display or clear then we are done if ( nargin < 3) return retval; // finish loading the function // XXX FIXME XXX in principle it is not even necessary for f // to be defined let alone be a function, but for simplicity // we will force it to be a function. Otherwise we will need // to reproduce the functionality of lookup despite the // symbol record for the dispatch function of the same name // as the function we are trying to look up. lookup(sr, false); if (!sr->is_function()) { error("dispatch: %s is not a function", f.c_str()); return retval; } // Build a new dispatch object based on the function definition octave_dispatch *dispatch = new octave_dispatch(sr); // Replace the definition in the symbol record sr->unprotect(); // sr->clear(); sr->define(octave_value(dispatch),symbol_record::BUILTIN_FUNCTION); sr->document("dispatched function"); sr->make_eternal(); // XXX FIXME XXX why?? sr->protect(); } // clear/replace/extend the map with the new type-function pair // XXX FIXME XXX oops . . . need pass by reference semantics const octave_dispatch& rep = (octave_dispatch&)(sr->def().get_rep()); if (nargin == 1) rep . print (); else if (nargin == 2) // XXX FIXME XXX should we eliminate the dispatch function if // there are no more elements? rep . clear (t); else rep . add (t, n); return retval; }