new function class
Hi, here is a short example code to demonstrate the changes I plan to make to GiNaC's function system (like discussed in November on this list). - every function is now a C++ class derived from class function (here still called newfunction to avoid name clashes). - the 14 functions already present in the standard C library have a special naming convention (here _ginac is appended to the class name) and supporting C functions for creation. - the definition of the functions is rather long, but will be made more comfortable with the help of new macros like GINAC_DECLARE_FUNCTION ... (not included here yet). Comments? Regards, Jens #include <iostream> #include <fstream> #include <stdexcept> #include <cmath> #include <ginac/ginac.h> namespace GiNaC { //////////////////////////////////////////////////////////////////////////////// class newfunction : public exprseq { GINAC_DECLARE_REGISTERED_CLASS_NO_CTORS(newfunction, exprseq) virtual newfunction* duplicate() const { return new newfunction(*this); } virtual void accept(GiNaC::visitor& v) const { if (visitor* p = dynamic_cast<visitor*>(&v)) { p->visit(*this); } else { inherited::accept(v); } } protected: virtual int compare_same_type(const basic& other) const { return exprseq::compare_same_type(other); } virtual bool is_equal_same_type(const basic& other) const { return exprseq::compare_same_type(other); } public: virtual void print(const print_context& c, unsigned level = 0) const; public: newfunction(tinfo_t ti) { tinfo_key = ti; } newfunction(tinfo_t ti, const ex& x1) : inherited(x1) { tinfo_key = ti; } newfunction(tinfo_t ti, const ex& x1, const ex& x2) : inherited(x1, x2) { tinfo_key = ti; } newfunction(tinfo_t ti, const ex& x1, const ex& x2, const ex& x3) : inherited(x1, x2, x3) { tinfo_key = ti; } // ... }; GINAC_IMPLEMENT_REGISTERED_CLASS(newfunction, exprseq) newfunction::newfunction(const GiNaC::archive_node& n, GiNaC::lst& sym_lst) : inherited(n, sym_lst) { } void newfunction::archive(GiNaC::archive_node& n) const { inherited::archive(n); } GiNaC::ex newfunction::unarchive(const GiNaC::archive_node& n, GiNaC::lst& sym_lst) { return (new newfunction(n, sym_lst))->setflag(status_flags::dynallocated); } void newfunction::print(const print_context& c, unsigned level) const { const std::vector<print_functor>& pdt = get_class_info().options.get_print_dispatch_table(); unsigned id = c.get_class_info().options.get_id(); if (id >= pdt.size() || !(pdt[id].is_valid())) { if (is_a<print_tree>(c)) { c.s << std::string(level, ' ') << class_name() << " @" << this << std::hex << ", hash=0x" << hashvalue << ", flags=0x" << flags << std::dec << ", nops=" << nops() << std::endl; unsigned delta_indent = static_cast<const print_tree&>(c).delta_indent; for (size_t i=0; i<seq.size(); ++i) { seq[i].print(c, level + delta_indent); } c.s << std::string(level + delta_indent, ' ') << "=====" << std::endl; } else if (is_a<print_latex>(c)) { c.s << "\\mbox{" << class_name() << "}"; inherited::do_print(c,level); } else { c.s << class_name(); inherited::do_print(c,level); } } else { pdt[id](*this, c, level); } } //////////////////////////////////////////////////////////////////////////////// class polylog : public newfunction { // a new GINAC_DECLARE_FUNCTION macro will replace this GINAC_DECLARE_REGISTERED_CLASS_NO_CTORS(polylog, newfunction) // will be part of GINAC_DECLARE_FUNCTION macro virtual polylog* duplicate() const { return new polylog(*this); } // will be part of GINAC_DECLARE_FUNCTION macro virtual void accept(GiNaC::visitor& v) const { if (visitor* p = dynamic_cast<visitor*>(&v)) { p->visit(*this); } else { inherited::accept(v); } } public: polylog(const ex& x1, const ex& x2) : inherited(&polylog::tinfo_static, x1, x2) { } protected: void do_print_latex(const print_context& c, unsigned level) const; public: virtual ex eval(int level = 0) const; }; GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(polylog, exprseq, print_func<print_latex>(&polylog::do_print_latex)) // will be part of GINAC_IMPLEMENT_FUNCTION macro polylog::polylog(const GiNaC::archive_node& n, GiNaC::lst& sym_lst) : inherited(n, sym_lst) { } // will be part of GINAC_IMPLEMENT_FUNCTION macro void polylog::archive(GiNaC::archive_node& n) const { inherited::archive(n); } // will be part of GINAC_IMPLEMENT_FUNCTION macro GiNaC::ex polylog::unarchive(const GiNaC::archive_node& n, GiNaC::lst& sym_lst) { return (new polylog(n, sym_lst))->setflag(status_flags::dynallocated); } void polylog::do_print_latex(const print_context& c, unsigned level) const { c.s << "\\mbox{Li}"; inherited::do_print(c,level); } ex polylog::eval(int level) const { if (op(0).is_equal(1)) { return zeta(op(1)); } else { return this->hold(); } } //////////////////////////////////////////////////////////////////////////////// class log10_ginac : public newfunction { GINAC_DECLARE_REGISTERED_CLASS_NO_CTORS(log10_ginac, newfunction) virtual log10_ginac* duplicate() const { return new log10_ginac(*this); } virtual void accept(GiNaC::visitor& v) const { if (visitor* p = dynamic_cast<visitor*>(&v)) { p->visit(*this); } else { inherited::accept(v); } } public: log10_ginac(const ex& x) : inherited(&log10_ginac::tinfo_static, x) { } protected: void do_print(const print_context& c, unsigned level) const; void do_print_latex(const print_context& c, unsigned level) const; public: virtual ex eval(int level = 0) const; }; GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(log10_ginac, newfunction, print_func<print_dflt>(&log10_ginac::do_print). print_func<print_latex>(&log10_ginac::do_print_latex)) log10_ginac::log10_ginac(const GiNaC::archive_node& n, GiNaC::lst& sym_lst) : inherited(n, sym_lst) { } void log10_ginac::archive(GiNaC::archive_node& n) const { inherited::archive(n); } GiNaC::ex log10_ginac::unarchive(const GiNaC::archive_node& n, GiNaC::lst& sym_lst) { return (new log10_ginac(n, sym_lst))->setflag(status_flags::dynallocated); } void log10_ginac::do_print(const print_context& c, unsigned level) const { std::string classname(class_name()); c.s << classname.erase(classname.find("_ginac",0),6); inherited::do_print(c,level); } void log10_ginac::do_print_latex(const print_context& c, unsigned level) const { c.s << "\\log"; inherited::do_print(c,level); } ex log10_ginac::eval(int level) const { if (op(0).is_equal(0)) { return 666; } else { return this->hold(); } } template<typename T1> const GiNaC::log10_ginac log10(const T1& x) { return log10_ginac(x); } const GiNaC::log10_ginac log10(double x) { return log10_ginac(x); } const GiNaC::log10_ginac log10(float x) { return log10_ginac(x); } //////////////////////////////////////////////////////////////////////////////// } // namespace GiNaC //using namespace std; using namespace GiNaC; int main() { try { symbol x("x"); ex r1 = polylog(x,3) + polylog(1,3) + 6*GiNaC::log10(3.0f) - log10(x); std::cout << dflt << r1 << std::endl; std::cout << tree << r1 << std::endl; std::cout << latex << r1 << std::endl; } catch (const std::exception& e) { std::cout << e.what() << std::endl; } return 0; }
Hi! Jens Vollinga wrote:
ex log10_ginac::eval(int level) const { if (op(0).is_equal(0)) { return 666; } else { return this->hold(); } }
template<typename T1> const GiNaC::log10_ginac log10(const T1& x) { return log10_ginac(x); } const GiNaC::log10_ginac log10(double x) { return log10_ginac(x); } const GiNaC::log10_ginac log10(float x) { return log10_ginac(x); }
Oh, I so much prefer return-value error signaling over throwing up^W exceptions! The only thing that confuses me are the specializations introduced for built-in floating point types. The real conflicts are when the function argument is a built-in integral type, as then there is a template function in namespace std conflicting with the template function up there. Maybe you really meant to specialize for built-in integral types because when using namespace GiNaC one would expect log10(2) to hold() but log10(2.0) to evalf()? Or did I miss something? Cheers -richy. -- Richard B. Kreckel <http://www.ginac.de/~kreckel/>
Hi, Richard B. Kreckel schrieb:
Oh, I so much prefer return-value error signaling over throwing up^W exceptions!
:-) But seriously, I'm almost finished with implementing the new system. There are still some compile/link issues and then some checks/timings to be done. One big task remains, though: choosing nice/convienent/powerful/new-but-similar macros for the definition of functions. To this end I will send the patches then (next week?), so that we can discuss it further.
The only thing that confuses me are the specializations introduced for built-in floating point types. The real conflicts are when the function argument is a built-in integral type, as then there is a template function in namespace std conflicting with the template function up there. Maybe you really meant to specialize for built-in integral types because when using namespace GiNaC one would expect log10(2) to hold() but log10(2.0) to evalf()? Or did I miss something?
I don't have log10(numeric) yet. So writing using namespace GiNaC; ex r = log10(1.0); would not call GiNaC::log10 but ::log10() [-> std::log10()] without giving any hint to the user. I learned (after years of programming C++ ... ;-)) that gcc puts all the math functions not only in namespace std but also in the global namespace. While this behavior is wrong with respect to the ISO standard, I learned from Gabriel Dos Reis and others that this will probably never be fixed. Regards, Jens
Jens Vollinga wrote:
I don't have log10(numeric) yet. So writing
using namespace GiNaC; ex r = log10(1.0);
would not call GiNaC::log10 but ::log10() [-> std::log10()] without giving any hint to the user.
So what about ex r = log10(1)? -richy. -- Richard B. Kreckel <http://www.ginac.de/~kreckel/>
Hi, Richard B. Kreckel schrieb:
using namespace GiNaC; ex r = log10(1.0);
would not call GiNaC::log10 but ::log10() [-> std::log10()] without giving any hint to the user.
So what about ex r = log10(1)?
it calls GiNaC::log10 and therefore r contains GiNaC::log(1). But as soon as I say using namespace std; the compiler will complain and forces the user to disambiguate the call. Then you would have to write either std::log10 or GiNaC::log10, which makes sense to me. Regards, Jens
Hi, Jens Vollinga schrieb:
the compiler will complain and forces the user to disambiguate the call. Then you would have to write either std::log10 or GiNaC::log10, which makes sense to me.
one additional remark: #include <cmath> #include <ginac/ginac.h> int main() { GiNaC::symbol x("x"); GiNaC::ex r = sin(x); return 0; } compiles without errors (gcc 4.0.2) !?! Note that it doesn't say GiNaC::sin. Even ... { GiNaC::symbol x("x"); sin(x); return 0; } ... compiles! Now I am confused again ... Regards, Jens
Hi, Jens Vollinga schrieb:
Now I am confused again ...
not anymore :-) ... I read the ISO standard again and the last code example is valid. But still, maybe we should agree on some kind of policy: when to disambiguate automatically and when to force the user to do it. Regards, Jens
participants (2)
-
Jens Vollinga
-
Richard B. Kreckel