diff options
Diffstat (limited to 'src/lib/netlist/prg/nltool.cpp')
-rw-r--r-- | src/lib/netlist/prg/nltool.cpp | 374 |
1 files changed, 187 insertions, 187 deletions
diff --git a/src/lib/netlist/prg/nltool.cpp b/src/lib/netlist/prg/nltool.cpp index a1d53d6792d..4a1c8ac0315 100644 --- a/src/lib/netlist/prg/nltool.cpp +++ b/src/lib/netlist/prg/nltool.cpp @@ -9,21 +9,23 @@ ****************************************************************************/ #include "netlist/plib/pmain.h" -#include "netlist/nl_setup.h" -#include "netlist/nl_parser.h" #include "netlist/devices/net_lib.h" -#include "netlist/tools/nl_convert.h" +#include "netlist/nl_parser.h" +#include "netlist/nl_setup.h" #include "netlist/solver/nld_solver.h" +#include "netlist/tools/nl_convert.h" #include <cstring> +#define NLTOOL_VERSION 20190202 + class tool_app_t : public plib::app { public: tool_app_t() : plib::app(), opt_grp1(*this, "General options", "The following options apply to all commands."), - opt_cmd (*this, "c", "cmd", "run", "run:convert:listdevices:static:header:docheader", "run|convert|listdevices|static|header"), + opt_cmd (*this, "c", "cmd", 0, std::vector<pstring>({"run","convert","listdevices","static","header","docheader"}), "run|convert|listdevices|static|header|docheader"), opt_file(*this, "f", "file", "-", "file to process (default is stdin)"), opt_defines(*this, "D", "define", "predefine value as macro, e.g. -Dname=value. If '=value' is omitted predefine it as 1. This option may be specified repeatedly."), opt_rfolders(*this, "r", "rom", "where to look for data files"), @@ -31,25 +33,34 @@ public: opt_quiet(*this, "q", "quiet", "be quiet - no warnings"), opt_version(*this, "", "version", "display version and exit"), opt_help(*this, "h", "help", "display help and exit"), + opt_grp2(*this, "Options for run and static commands", "These options apply to run and static commands."), opt_name(*this, "n", "name", "", "the netlist in file specified by ""-f"" option to run; default is first one"), + opt_grp3(*this, "Options for run command", "These options are only used by the run command."), - opt_ttr (*this, "t", "time_to_run", 1.0, "time to run the emulation (seconds)"), + opt_ttr (*this, "t", "time_to_run", 1.0, "time to run the emulation (seconds)\n\n abc def\n\n xyz"), opt_logs(*this, "l", "log" , "define terminal to log. This option may be specified repeatedly."), opt_inp(*this, "i", "input", "", "input file to process (default is none)"), opt_loadstate(*this,"", "loadstate", "", "load state from file and continue from there"), opt_savestate(*this,"", "savestate", "", "save state to file at end of run"), + opt_grp4(*this, "Options for convert command", "These options are only used by the convert command."), - opt_type(*this, "y", "type", "spice", "spice:eagle:rinf", "type of file to be converted: spice,eagle,rinf"), + opt_type(*this, "y", "type", 0, std::vector<pstring>({"spice","eagle","rinf"}), "type of file to be converted: spice,eagle,rinf"), + + opt_grp5(*this, "Options for header command", "These options are only used by the header command."), + opt_tabwidth(*this, "", "tab-width", 4, "Tab width for output."), + opt_linewidth(*this,"", "line-width", 72, "Line width for output."), opt_ex1(*this, "nltool -c run -t 3.5 -f nl_examples/cdelay.c -n cap_delay", "Run netlist \"cap_delay\" from file nl_examples/cdelay.c for 3.5 seconds"), opt_ex2(*this, "nltool --cmd=listdevices", - "List all known devices.") + "List all known devices."), + opt_ex3(*this, "nltool --cmd=header --tab-width=8 --line-width=80", + "Create the header file needed for including netlists as code.") {} plib::option_group opt_grp1; - plib::option_str_limit opt_cmd; + plib::option_str_limit<unsigned> opt_cmd; plib::option_str opt_file; plib::option_vec opt_defines; plib::option_vec opt_rfolders; @@ -60,18 +71,22 @@ public: plib::option_group opt_grp2; plib::option_str opt_name; plib::option_group opt_grp3; - plib::option_double opt_ttr; + plib::option_num<double> opt_ttr; plib::option_vec opt_logs; plib::option_str opt_inp; plib::option_str opt_loadstate; plib::option_str opt_savestate; plib::option_group opt_grp4; - plib::option_str_limit opt_type; + plib::option_str_limit<unsigned> opt_type; + plib::option_group opt_grp5; + plib::option_num<unsigned> opt_tabwidth; + plib::option_num<unsigned> opt_linewidth; plib::option_example opt_ex1; plib::option_example opt_ex2; + plib::option_example opt_ex3; - int execute(); - pstring usage(); + int execute() override; + pstring usage() override; private: void run(); @@ -86,6 +101,8 @@ private: void listdevices(); + std::vector<pstring> m_options; + }; static NETLIST_START(dummy) @@ -103,45 +120,54 @@ NETLIST_END() class netlist_data_folder_t : public netlist::source_t { public: - netlist_data_folder_t(netlist::setup_t &setup, - pstring folder) - : netlist::source_t(setup, netlist::source_t::DATA) + netlist_data_folder_t(const pstring &folder) + : netlist::source_t(netlist::source_t::DATA) , m_folder(folder) { } - virtual std::unique_ptr<plib::pistream> stream(const pstring &file) override; + plib::unique_ptr<plib::pistream> stream(const pstring &file) override; private: pstring m_folder; }; -std::unique_ptr<plib::pistream> netlist_data_folder_t::stream(const pstring &file) +plib::unique_ptr<plib::pistream> netlist_data_folder_t::stream(const pstring &file) { pstring name = m_folder + "/" + file; try { - auto strm = plib::make_unique_base<plib::pistream, plib::pifilestream>(name); - return strm; + auto strm = plib::make_unique<plib::pifilestream>(name); + return std::move(strm); } catch (const plib::pexception &e) { if (dynamic_cast<const plib::file_open_e *>(&e) == nullptr ) throw; } - return std::unique_ptr<plib::pistream>(nullptr); + return plib::unique_ptr<plib::pistream>(nullptr); } +class netlist_tool_callbacks_t : public netlist::callbacks_t +{ +public: + netlist_tool_callbacks_t(tool_app_t &app) + : netlist::callbacks_t() + , m_app(app) + { } + + void vlog(const plib::plog_level &l, const pstring &ls) const override; + +private: + tool_app_t &m_app; +}; + class netlist_tool_t : public netlist::netlist_t { public: netlist_tool_t(tool_app_t &app, const pstring &aname) - : netlist::netlist_t(aname), m_app(app) - { - } - - virtual ~netlist_tool_t() override + : netlist::netlist_t(aname, plib::make_unique<netlist_tool_callbacks_t>(app)) { } @@ -149,6 +175,8 @@ public: { } + netlist::setup_t &setup() { return nlstate().setup(); } + void read_netlist(const pstring &filename, const pstring &name, const std::vector<pstring> &logs, const std::vector<pstring> &defines, @@ -157,23 +185,22 @@ public: // read the netlist ... for (auto & d : defines) - setup().register_define(d); + setup().add_define(d); for (auto & r : roms) - setup().register_source(plib::make_unique_base<netlist::source_t, netlist_data_folder_t>(setup(), r)); + setup().register_source(plib::make_unique<netlist_data_folder_t>(r)); - setup().register_source(plib::make_unique_base<netlist::source_t, - netlist::source_file_t>(setup(), filename)); + setup().register_source(plib::make_unique<netlist::source_file_t>(filename)); setup().include(name); - log_setup(logs); + create_dynamic_logs(logs); // start devices - this->start(); + setup().prepare_to_run(); // reset this->reset(); } - void log_setup(const std::vector<pstring> &logs) + void create_dynamic_logs(const std::vector<pstring> &logs) { log().debug("Creating dynamic logs ...\n"); for (auto & log : logs) @@ -186,15 +213,15 @@ public: std::vector<char> save_state() { - state().pre_save(); + run_state_manager().pre_save(); std::size_t size = 0; - for (auto const & s : state().save_list()) + for (auto const & s : run_state_manager().save_list()) size += s->m_dt.size * s->m_count; std::vector<char> buf(size); char *p = buf.data(); - for (auto const & s : state().save_list()) + for (auto const & s : run_state_manager().save_list()) { std::size_t sz = s->m_dt.size * s->m_count; if (s->m_dt.is_float || s->m_dt.is_integral) @@ -210,7 +237,7 @@ public: void load_state(std::vector<char> &buf) { std::size_t size = 0; - for (auto const & s : state().save_list()) + for (auto const & s : run_state_manager().save_list()) size += s->m_dt.size * s->m_count; if (buf.size() != size) @@ -218,7 +245,7 @@ public: char *p = buf.data(); - for (auto const & s : state().save_list()) + for (auto const & s : run_state_manager().save_list()) { std::size_t sz = s->m_dt.size * s->m_count; if (s->m_dt.is_float || s->m_dt.is_integral) @@ -227,19 +254,16 @@ public: log().fatal("found unsupported save element {1}\n", s->m_name); p += sz; } - state().post_load(); - rebuild_lists(); + run_state_manager().post_load(); + nlstate().rebuild_lists(); } protected: - void vlog(const plib::plog_level &l, const pstring &ls) const override; - private: - tool_app_t &m_app; }; -void netlist_tool_t::vlog(const plib::plog_level &l, const pstring &ls) const +void netlist_tool_callbacks_t::vlog(const plib::plog_level &l, const pstring &ls) const { pstring err = plib::pfmt("{}: {}\n")(l.name())(ls.c_str()); // FIXME: ... @@ -248,18 +272,19 @@ void netlist_tool_t::vlog(const plib::plog_level &l, const pstring &ls) const throw netlist::nl_exception(err); } - struct input_t { input_t(const netlist::setup_t &setup, const pstring &line) + : m_value(0.0) { - char buf[400]; + std::array<char, 400> buf; // NOLINT(cppcoreguidelines-pro-type-member-init) double t; - int e = sscanf(line.c_str(), "%lf,%[^,],%lf", &t, buf, &m_value); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) + int e = sscanf(line.c_str(), "%lf,%[^,],%lf", &t, buf.data(), &m_value); if (e != 3) throw netlist::nl_exception(plib::pfmt("error {1} scanning line {2}\n")(e)(line)); m_time = netlist::netlist_time::from_double(t); - m_param = setup.find_param(pstring(buf, pstring::UTF8), true); + m_param = setup.find_param(pstring(buf.data()), true); } void setparam() @@ -286,13 +311,12 @@ struct input_t double m_value; }; -static std::vector<input_t> read_input(const netlist::setup_t &setup, pstring fname) +static std::vector<input_t> read_input(const netlist::setup_t &setup, const pstring &fname) { std::vector<input_t> ret; if (fname != "") { - plib::pifilestream f(fname); - plib::putf8_reader r(f); + plib::putf8_reader r = plib::putf8_reader(plib::pifilestream(fname)); pstring l; while (r.readline(l)) { @@ -309,78 +333,82 @@ static std::vector<input_t> read_input(const netlist::setup_t &setup, pstring fn void tool_app_t::run() { plib::chrono::timer<plib::chrono::system_ticks> t; - t.start(); - + std::vector<input_t> inps; + netlist::netlist_time ttr; netlist_tool_t nt(*this, "netlist"); - //plib::perftime_t<plib::exact_ticks> t; - nt.init(); + { + auto t_guard(t.guard()); + //plib::perftime_t<plib::exact_ticks> t; - if (!opt_verb()) - nt.log().verbose.set_enabled(false); - if (opt_quiet()) - nt.log().warning.set_enabled(false); + nt.init(); - nt.read_netlist(opt_file(), opt_name(), - opt_logs(), - opt_defines(), opt_rfolders()); + if (!opt_verb()) + nt.log().verbose.set_enabled(false); + if (opt_quiet()) + nt.log().warning.set_enabled(false); - std::vector<input_t> inps = read_input(nt.setup(), opt_inp()); + nt.read_netlist(opt_file(), opt_name(), + opt_logs(), + m_options, opt_rfolders()); + + inps = read_input(nt.setup(), opt_inp()); + ttr = netlist::netlist_time::from_double(opt_ttr()); + } - netlist::netlist_time ttr = netlist::netlist_time::from_double(opt_ttr()); - t.stop(); pout("startup time ==> {1:5.3f}\n", t.as_seconds() ); t.reset(); - t.start(); - // FIXME: error handling - if (opt_loadstate.was_specified()) + netlist::netlist_time nlt = nt.time(); { - plib::pifilestream strm(opt_loadstate()); - plib::pbinary_reader reader(strm); - std::vector<char> loadstate; - reader.read(loadstate); - nt.load_state(loadstate); - pout("Loaded state, run will continue at {1:.6f}\n", nt.time().as_double()); - } + auto t_guard(t.guard()); - unsigned pos = 0; - netlist::netlist_time nlt = nt.time(); + // FIXME: error handling + if (opt_loadstate.was_specified()) + { + plib::pifilestream strm(opt_loadstate()); + plib::pbinary_reader reader(strm); + std::vector<char> loadstate; + reader.read(loadstate); + nt.load_state(loadstate); + pout("Loaded state, run will continue at {1:.6f}\n", nt.time().as_double()); + } + unsigned pos = 0; - while (pos < inps.size() - && inps[pos].m_time < ttr - && inps[pos].m_time >= nlt) - { - nt.process_queue(inps[pos].m_time - nlt); - inps[pos].setparam(); - nlt = inps[pos].m_time; - pos++; - } - pout("runnning ...\n"); + while (pos < inps.size() + && inps[pos].m_time < ttr + && inps[pos].m_time >= nlt) + { + nt.process_queue(inps[pos].m_time - nlt); + inps[pos].setparam(); + nlt = inps[pos].m_time; + pos++; + } - if (ttr > nlt) - nt.process_queue(ttr - nlt); - else - { - pout("end time {1:.6f} less than saved time {2:.6f}\n", - ttr.as_double(), nlt.as_double()); - ttr = nlt; - } + pout("runnning ...\n"); - if (opt_savestate.was_specified()) - { - auto savestate = nt.save_state(); - plib::pofilestream strm(opt_savestate()); - plib::pbinary_writer writer(strm); - writer.write(savestate); - } - nt.stop(); + if (ttr > nlt) + nt.process_queue(ttr - nlt); + else + { + pout("end time {1:.6f} less than saved time {2:.6f}\n", + ttr.as_double(), nlt.as_double()); + ttr = nlt; + } - t.stop(); + if (opt_savestate.was_specified()) + { + auto savestate = nt.save_state(); + plib::pofilestream strm(opt_savestate()); + plib::pbinary_writer writer(strm); + writer.write(savestate); + } + nt.stop(); + } double emutime = t.as_seconds(); pout("{1:f} seconds emulation took {2:f} real time ==> {3:5.2f}%\n", @@ -399,9 +427,9 @@ void tool_app_t::static_compile() nt.read_netlist(opt_file(), opt_name(), opt_logs(), - opt_defines(), opt_rfolders()); + m_options, opt_rfolders()); - plib::putf8_writer w(pout_strm); + plib::putf8_writer w(&pout_strm); std::map<pstring, pstring> mp; nt.solver()->create_solver_code(mp); @@ -417,13 +445,26 @@ void tool_app_t::static_compile() void tool_app_t::mac_out(const pstring &s, const bool cont) { - static constexpr unsigned RIGHT = 72; if (cont) { - unsigned adj = 0; + unsigned pos = 0; + pstring r; for (const auto &x : s) - adj += (x == '\t' ? 3 : 0); - pout("{1}\\\n", s.rpad(" ", RIGHT-1-adj)); + { + if (x == '\t') + { + auto pos_mod_4 = pos % opt_tabwidth(); + auto tab_adj = opt_tabwidth() - pos_mod_4; + r += plib::rpad(pstring(""), pstring(" "), tab_adj); + pos += tab_adj; + } + else + { + r += x; + pos++; + } + } + pout("{1}\\\n", plib::rpad(r, pstring(" "), opt_linewidth()-1)); } else pout("{1}\n", s); @@ -433,15 +474,15 @@ void tool_app_t::cmac(const netlist::factory::element_t *e) { auto v = plib::psplit(e->param_desc(), ","); pstring vs; - for (auto s : v) - vs += ", p" + s.replace_all("+", "").replace_all(".", "_"); + for (const auto &s : v) + vs += ", p" + plib::replace_all(plib::replace_all(s, "+", ""), ".", "_"); mac_out("#define " + e->name() + "(name" + vs + ")"); mac_out("\tNET_REGISTER_DEV(" + e->name() +", name)"); - for (auto s : v) + for (const auto &s : v) { - pstring r(s.replace_all("+", "").replace_all(".", "_")); - if (s.startsWith("+")) + pstring r(plib::replace_all(plib::replace_all(s, "+", ""), ".", "_")); + if (plib::startsWith(s, "+")) mac_out("\tNET_CONNECT(name, " + r + ", p" + r + ")"); else mac_out("\tNETDEV_PARAMI(name, " + r + ", p" + r + ")"); @@ -453,18 +494,18 @@ void tool_app_t::mac(const netlist::factory::element_t *e) { auto v = plib::psplit(e->param_desc(), ","); pstring vs; - for (auto s : v) + for (const auto &s : v) { - vs += ", " + s.replace_all("+", "").replace_all(".", "_"); + vs += ", " + plib::replace_all(plib::replace_all(s, "+", ""), ".", "_"); } pout("{1}(name{2})\n", e->name(), vs); if (v.size() > 0) { pout("/*\n"); - for (auto s : v) + for (const auto &s : v) { - pstring r(s.replace_all("+", "").replace_all(".", "_")); - if (s.startsWith("+")) + pstring r(plib::replace_all(plib::replace_all(s, "+", ""), ".", "_")); + if (plib::startsWith(s, "+")) pout("{1:10}: Terminal\n",r); else pout("{1:10}: Parameter\n", r); @@ -482,8 +523,7 @@ void tool_app_t::create_header() nt.log().verbose.set_enabled(false); nt.log().warning.set_enabled(false); - nt.setup().register_source(plib::make_unique_base<netlist::source_t, - netlist::source_proc_t>(nt.setup(), "dummy", &netlist_dummy)); + nt.setup().register_source(plib::make_unique<netlist::source_proc_t>("dummy", &netlist_dummy)); nt.setup().include("dummy"); pout("// license:GPL-2.0+\n"); @@ -506,9 +546,9 @@ void tool_app_t::create_header() if (last_source != e->sourcefile()) { last_source = e->sourcefile(); - pout("{1}\n", pstring("// ").rpad("-", 72)); - pout("{1}{2}\n", pstring("// Source: "), e->sourcefile().replace_all("../", "")); - pout("{1}\n", pstring("// ").rpad("-", 72)); + pout("{1}\n", plib::rpad(pstring("// "), pstring("-"), opt_linewidth())); + pout("{1}{2}\n", pstring("// Source: "), plib::replace_all(e->sourcefile(), "../", "")); + pout("{1}\n", plib::rpad(pstring("// "), pstring("-"), opt_linewidth())); } cmac(e.get()); } @@ -527,8 +567,7 @@ void tool_app_t::create_docheader() nt.log().verbose.set_enabled(false); nt.log().warning.set_enabled(false); - nt.setup().register_source(plib::make_unique_base<netlist::source_t, - netlist::source_proc_t>(nt.setup(), "dummy", &netlist_dummy)); + nt.setup().register_source(plib::make_unique<netlist::source_proc_t>("dummy", &netlist_dummy)); nt.setup().include("dummy"); std::vector<pstring> devs; @@ -580,73 +619,42 @@ void tool_app_t::listdevices() netlist::factory::list_t &list = nt.setup().factory(); - nt.setup().register_source(plib::make_unique_base<netlist::source_t, - netlist::source_proc_t>(nt.setup(), "dummy", &netlist_dummy)); + nt.setup().register_source(plib::make_unique<netlist::source_proc_t>("dummy", &netlist_dummy)); nt.setup().include("dummy"); - nt.start(); + nt.setup().prepare_to_run(); - std::vector<plib::owned_ptr<netlist::core_device_t>> devs; + std::vector<netlist::pool_owned_ptr<netlist::core_device_t>> devs; for (auto & f : list) { pstring out = plib::pfmt("{1:-20} {2}(<id>")(f->classname())(f->name()); - std::vector<pstring> terms; - f->macro_actions(nt.setup().netlist(), f->name() + "_lc"); - auto d = f->Create(nt.setup().netlist(), f->name() + "_lc"); + f->macro_actions(nt.setup(), f->name() + "_lc"); + auto d = f->Create(nt.nlstate(), f->name() + "_lc"); // get the list of terminals ... - for (auto & t : nt.setup().m_terminals) - { - if (t.second->name().startsWith(d->name())) - { - pstring tn(t.second->name().substr(d->name().length()+1)); - if (tn.find(".") == pstring::npos) - terms.push_back(tn); - } - } - - for (auto & t : nt.setup().m_alias) - { - if (t.first.startsWith(d->name())) - { - pstring tn(t.first.substr(d->name().length()+1)); - //printf("\t%s %s %s\n", t.first.c_str(), t.second.c_str(), tn.c_str()); - if (tn.find(".") == pstring::npos) - { - terms.push_back(tn); - pstring resolved = nt.setup().resolve_alias(t.first); - //printf("\t%s %s %s\n", t.first.c_str(), t.second.c_str(), resolved.c_str()); - if (resolved != t.first) - { - auto found = std::find(terms.begin(), terms.end(), resolved.substr(d->name().length()+1)); - if (found!=terms.end()) - terms.erase(found); - } - } - } - } + std::vector<pstring> terms(nt.setup().get_terminals_for_device_name(d->name())); out += "," + f->param_desc(); - for (auto p : plib::psplit(f->param_desc(),",") ) + for (const auto &p : plib::psplit(f->param_desc(),",") ) { - if (p.startsWith("+")) + if (plib::startsWith(p, "+")) { plib::container::remove(terms, p.substr(1)); } } out += ")"; - printf("%s\n", out.c_str()); + pout("{}\n", out); if (terms.size() > 0) { pstring t = ""; for (auto & j : terms) t += "," + j; - printf("\tTerminals: %s\n", t.substr(1).c_str()); + pout("\tTerminals: {}\n", t.substr(1)); } - devs.push_back(std::move(d)); + devs.emplace_back(std::move(d)); } } @@ -681,10 +689,6 @@ int tool_app_t::execute() plib::fpsignalenabler::global_enable(true); plib::fpsignalenabler sigen(plib::FP_ALL & ~plib::FP_INEXACT & ~plib::FP_UNDERFLOW); - //perr("{}", "WARNING: This is Work In Progress! - It may fail anytime\n"); - //perr("Update dispatching using method {}\n", pmf_verbose[NL_PMF_TYPE]); - //printf("test2 %f\n", std::exp(-14362.38064713)); - if (opt_help()) { pout(usage()); @@ -694,8 +698,8 @@ int tool_app_t::execute() if (opt_version()) { pout( - "nltool (netlist) 0.1\n" - "Copyright (C) 2018 Couriersud\n" + "nltool (netlist) " PSTRINGIFY(NLTOOL_VERSION) "\n" + "Copyright (C) 2019 Couriersud\n" "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>.\n" "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n\n" @@ -703,9 +707,12 @@ int tool_app_t::execute() return 0; } + m_options = opt_defines(); + m_options.emplace_back("NLTOOL_VERSION=" PSTRINGIFY(NLTOOL_VERSION)); + try { - pstring cmd = opt_cmd(); + pstring cmd = opt_cmd.as_string(); if (cmd == "listdevices") listdevices(); else if (cmd == "run") @@ -723,29 +730,29 @@ int tool_app_t::execute() if (opt_file() == "-") { plib::pstdin f; - ostrm.write(f); + plib::copystream(ostrm, f); } else { plib::pifilestream f(opt_file()); - ostrm.write(f); + plib::copystream(ostrm, f); } contents = ostrm.str(); pstring result; - if (opt_type().equals("spice")) + if (opt_type.as_string() == "spice") { nl_convert_spice_t c; c.convert(contents); result = c.result(); } - else if (opt_type().equals("eagle")) + else if (opt_type.as_string() == "eagle") { nl_convert_eagle_t c; c.convert(contents); result = c.result(); } - else if (opt_type().equals("rinf")) + else if (opt_type.as_string() == "rinf") { nl_convert_rinf_t c; c.convert(contents); @@ -771,13 +778,6 @@ int tool_app_t::execute() perr("plib exception caught: {}\n", e.text()); } -#if 0 -#define str(x) # x -#define strx(x) str(x) -#define ttt strx(__cplusplus) - printf("%s\n", ttt); -#endif - return 0; } |