From 052d42fd9eb8b7e59eff497da98e1f58cde413a4 Mon Sep 17 00:00:00 2001 From: Christian Bauer Date: Fri, 25 Jul 2003 20:44:49 +0000 Subject: [PATCH] implemented double dispatch for print(); methods are specified with print_func() options in the GINAC_IMPLEMENT_REGISTERED_CLASS_OPT macro (see basic.cpp for an example); what remains is to convert all existing classes to this new scheme... --- ginac/basic.cpp | 79 ++++++++++++++++++++++++++++++++++------- ginac/basic.h | 5 +++ ginac/power.cpp | 2 +- ginac/print.h | 90 +++++++++++++++++++++++++++++++++++++++++++++++ ginac/registrar.h | 32 +++++++++++++++++ 5 files changed, 194 insertions(+), 14 deletions(-) diff --git a/ginac/basic.cpp b/ginac/basic.cpp index daac0856..66e6c901 100644 --- a/ginac/basic.cpp +++ b/ginac/basic.cpp @@ -36,13 +36,15 @@ #include "relational.h" #include "operators.h" #include "wildcard.h" -#include "print.h" #include "archive.h" #include "utils.h" namespace GiNaC { -GINAC_IMPLEMENT_REGISTERED_CLASS(basic, void) +GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(basic, void, + print_func(&basic::do_print). + print_func(&basic::do_print_tree). + print_func(&basic::do_print_python_repr)) ////////// // default constructor, destructor, copy constructor and assignment operator @@ -116,23 +118,74 @@ void basic::archive(archive_node &n) const // public -/** Output to stream. +/** Output to stream. This performs double dispatch on the dynamic type of + * *this and the dynamic type of the supplied print context. * @param c print context object that describes the output formatting * @param level value that is used to identify the precedence or indentation * level for placing parentheses and formatting */ void basic::print(const print_context & c, unsigned level) const { - if (is_a(c)) { + // Double dispatch on object type and print_context type + const registered_class_info * reg_info = &get_class_info(); + const print_context_class_info * pc_info = &c.get_class_info(); - c.s << std::string(level, ' ') << class_name() - << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec - << ", nops=" << nops() - << std::endl; - for (size_t i=0; i(c).delta_indent); +next_class: + const std::vector & pdt = reg_info->options.get_print_dispatch_table(); - } else - c.s << "[" << class_name() << " object]"; +next_context: + unsigned id = pc_info->options.get_id(); + if (id >= pdt.size() || !(pdt[id].is_valid())) { + + // Method not found, try parent print_context class + const print_context_class_info * parent_pc_info = pc_info->get_parent(); + if (parent_pc_info) { + pc_info = parent_pc_info; + goto next_context; + } + + // Method still not found, try parent class + const registered_class_info * parent_reg_info = reg_info->get_parent(); + if (parent_reg_info) { + reg_info = parent_reg_info; + pc_info = &c.get_class_info(); + goto next_class; + } + + // Method still not found. This shouldn't happen because basic (the + // base class of the algebraic hierarchy) registers a method for + // print_context (the base class of the print context hierarchy), + // so if we end up here, there's something wrong with the class + // registry. + throw (std::runtime_error(std::string("basic::print(): method for ") + class_name() + "/" + c.class_name() + " not found")); + + } else { + + // Call method + pdt[id](*this, c, level); + } +} + +/** Default output to stream. */ +void basic::do_print(const print_context & c, unsigned level) const +{ + c.s << "[" << class_name() << " object]"; +} + +/** Tree output to stream. */ +void basic::do_print_tree(const print_tree & c, unsigned level) const +{ + c.s << std::string(level, ' ') << class_name() + << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec + << ", nops=" << nops() + << std::endl; + for (size_t i=0; iprint(print_tree(std::cerr)); } -/** Return relative operator precedence (for parenthizing output). */ +/** Return relative operator precedence (for parenthezing output). */ unsigned basic::precedence() const { return 70; diff --git a/ginac/basic.h b/ginac/basic.h index 90e3faff..1bd89cac 100644 --- a/ginac/basic.h +++ b/ginac/basic.h @@ -208,6 +208,11 @@ public: protected: void ensure_if_modifiable() const; + +private: + void do_print(const print_context & c, unsigned level) const; + void do_print_tree(const print_tree & c, unsigned level) const; + void do_print_python_repr(const print_python_repr & c, unsigned level) const; // member variables protected: diff --git a/ginac/power.cpp b/ginac/power.cpp index 64ebc717..fab41d83 100644 --- a/ginac/power.cpp +++ b/ginac/power.cpp @@ -89,7 +89,7 @@ static void print_sym_pow(const print_context & c, const symbol &x, int exp) { // Optimal output of integer powers of symbols to aid compiler CSE. // C.f. ISO/IEC 14882:1998, section 1.9 [intro execution], paragraph 15 - // to learn why such a parenthisation is really necessary. + // to learn why such a parenthesation is really necessary. if (exp == 1) { x.print(c); } else if (exp == 2) { diff --git a/ginac/print.h b/ginac/print.h index 54b6ca71..6b80d488 100644 --- a/ginac/print.h +++ b/ginac/print.h @@ -25,6 +25,7 @@ #include #include +#include #include "class_info.h" @@ -67,6 +68,7 @@ public: public: \ typedef supername inherited; \ friend class function_options; \ + friend class registered_class_options; \ private: \ static GiNaC::print_context_class_info reg_info; \ public: \ @@ -176,6 +178,94 @@ template inline bool is_a(const print_context & obj) { return dynamic_cast(&obj) != 0; } + +class basic; + +/** Base class for print_functor handlers */ +class print_functor_impl { +public: + virtual ~print_functor_impl() {} + virtual print_functor_impl *duplicate() const = 0; + virtual void operator()(const basic & obj, const print_context & c, unsigned level) const = 0; +}; + +/** print_functor handler for pointer-to-functions of class T, context type C */ +template +class print_ptrfun_handler : public print_functor_impl { +public: + typedef void (*F)(const T &, const C &, unsigned); + + print_ptrfun_handler(F f_) : f(f_) {} + print_ptrfun_handler *duplicate() const { return new print_ptrfun_handler(*this); } + + void operator()(const basic & obj, const print_context & c, unsigned level) const + { + // Call the supplied function + f(dynamic_cast(obj), dynamic_cast(c), level); + } + +private: + F f; +}; + +/** print_functor handler for member functions of class T, context type C */ +template +class print_memfun_handler : public print_functor_impl { +public: + typedef void (T::*F)(const C & c, unsigned level); + + print_memfun_handler(F f_) : f(f_) {} + print_memfun_handler *duplicate() const { return new print_memfun_handler(*this); } + + void operator()(const basic & obj, const print_context & c, unsigned level) const + { + // Call the supplied member function + return (dynamic_cast(obj).*f)(dynamic_cast(c), level); + } + +private: + F f; +}; + +/** This class represents a print method for a certain algebraic class and + * print_context type. Its main purpose is to hide the difference between + * member functions and nonmember functions behind one unified operator() + * interface. print_functor has value semantics and acts as a smart pointer + * (with deep copy) to a class derived from print_functor_impl which + * implements the actual function call. */ +class print_functor { +public: + print_functor() : impl(0) {} + print_functor(const print_functor & other) : impl(other.impl.get() ? other.impl->duplicate() : 0) {} + print_functor(std::auto_ptr impl_) : impl(impl_) {} + + template + print_functor(void f(const T &, const C &, unsigned)) : impl(new print_ptrfun_handler(f)) {} + + template + print_functor(void (T::*f)(const C &, unsigned)) : impl(new print_memfun_handler(f)) {} + + print_functor & operator=(const print_functor & other) + { + if (this != &other) { + print_functor_impl *p = other.impl.get(); + impl.reset(p ? other.impl->duplicate() : 0); + } + return *this; + } + + void operator()(const basic & obj, const print_context & c, unsigned level) const + { + (*impl)(obj, c, level); + } + + bool is_valid() const { return impl.get(); } + +private: + std::auto_ptr impl; +}; + + } // namespace GiNaC #endif // ndef __GINAC_BASIC_H__ diff --git a/ginac/registrar.h b/ginac/registrar.h index 9dc3f24d..9610f6c0 100644 --- a/ginac/registrar.h +++ b/ginac/registrar.h @@ -25,8 +25,10 @@ #include #include +#include #include "class_info.h" +#include "print.h" namespace GiNaC { @@ -51,12 +53,42 @@ public: const char *get_parent_name() const { return parent_name; } unsigned get_id() const { return tinfo_key; } unarch_func get_unarch_func() const { return unarchive; } + const std::vector &get_print_dispatch_table() const { return print_dispatch_table; } + + template + registered_class_options & print_func(void f(const T &, const C & c, unsigned)) + { + set_print_func(Ctx::reg_info.options.get_id(), f); + return *this; + } + + template + registered_class_options & print_func(void (T::*f)(const C &, unsigned)) + { + set_print_func(Ctx::reg_info.options.get_id(), f); + return *this; + } + + template + registered_class_options & print_func(const print_functor & f) + { + set_print_func(Ctx::reg_info.options.get_id(), f); + return *this; + } private: + void set_print_func(unsigned id, const print_functor & f) + { + if (id >= print_dispatch_table.size()) + print_dispatch_table.resize(id + 1); + print_dispatch_table[id] = f; + } + const char *name; /**< Class name. */ const char *parent_name; /**< Name of superclass. */ unsigned tinfo_key; /**< TINFO_* key. */ unarch_func unarchive; /**< Pointer to unarchiving function. */ + std::vector print_dispatch_table; /**< Method table for print() dispatch */ }; typedef class_info registered_class_info; -- 2.49.0