📄 templates.cpp
字号:
/* TEMPLATES.CPP
* Support for C++ templates
* UnderC C++ interpreter
* Steve Donovan, 2001
* This is GPL'd software, and the usual disclaimers apply.
* See LICENCE
*/
#include "common.h"
#include "templates.h"
#include "std_utils.h"
#include "tparser.h" // for CLASS token
#include <ctype.h>
#include "input.h"
#include "directcall.h"
const int TEMPL_BUFFSIZE = 20000;
// in main.cpp
int uc_eval(char *expr, bool append_semicolon, bool synchronous, char *name, int lineno);
#define YYEMPTY (-2)
extern int yychar;
int save_yychar;
YYSTYPE save_yylval;
void save_parser_state()
{
save_yychar = yychar;
save_yylval = yylval;
yychar = YYEMPTY;
}
void restore_parser_state()
{
yychar = save_yychar;
yylval = save_yylval;
}
int count(char *, char); // at end of this file.
string build_qualified_name(const string& name, const TypeList& tl);
ostream& operator<< (ostream& outs, const TypeList& tl);
void massage_type_list(TypeList& tl);
void copy_type_list(TypeList& tlp, const TypeList& tlt);
typedef TypeList::iterator TLI;
typedef TypeList::const_iterator TLCI;
static char mIBuffer[TEMPL_BUFFSIZE];
bool DummyType::bind_to(Type t)
{
// Plain dummies bind to types in a straightforward manner; they only complain when
// they are already bound and an attempt is made to bind them to a different type
// Binding a constant dummy to a variable dummy involves some copying, which will
// only work for simple types at the mo.
PEntry pe = entry() != NULL && t.is_dummy() ? t.as_dummy()->entry() : NULL;
if (t == t_null) m_type = t_null; // that is, unbound us (NB for us to _reuse_ this)
else if (pe != NULL && pe->name == "") { // constant dummy...
memcpy(entry()->global_ptr(),pe->global_ptr(),pe->type.size());
// *fix 1.2.9 indicates that we are properly bound!
m_type = entry()->type;
// *change 1.2.4 no longer function_trace
if (Parser::debug.verbose) cout << "*bound (const) " << name() << ' ' << *(int *)pe->global_ptr() << endl;
} else {
if (! unbound() && t != m_type) return false;
m_type = t;
if (entry()) entry()->type = t;
if (Parser::debug.verbose) cout << "*bound " << name() << ' ' << t << endl;
}
return true;
}
string DummyType::value_as_string()
{
string s;
if (name() != "") s += name();
else {
if (entry()==NULL) s += "unbound";
else s += type().value_as_string(entry()->global_ptr());
}
return s;
}
IContext::IContext(Table *parent)
: Table(parent,DIRECT,0)
{ m_type = INSTANTIATION_CONTEXT; }
Template::Template(TemplateEntry *te, const TypeList& tl, IContext *cntxt)
: m_context(cntxt), m_templ_entry(te), m_formal_args(tl)
{
m_file = Input::filename();
m_lineno = Input::lineno();
}
bool Template::is_class()
{ return m_templ_entry->is_class(); }
string Template::name()
{ return m_templ_entry->name(); }
void Template::instantiate(TemplateInstance *instance)
{
using Parser::state;
const TypeList& tl = instance->get_template()->formal_parms();
TLCI ti;
// don't try to instantiate templates using dummy types!
int unbound_count = 0;
FORALL(ti,tl) if (ti->is_dummy()){
Type t = *ti;
if (t.is_dummy() && (t.is_unbound() || t.as_dummy()->type().is_dummy()) )
unbound_count++;
}
string inst_err = "";
bool success = true;
if (unbound_count > 0) success = false;
else {
Parser::ParserState old_state = state;
// *hack 1.2.7 Can't use defered method compilation for instantiating template classes.
bool old_skip = Parser::debug.skip_method_bodies;
Parser::debug.skip_method_bodies = false;
state.reset();
state.push_context(m_context);
state.in_template = instance;
dcl_set(false,false); // save the DCL & ALS stacks...
save_parser_state(); // save the BISON state...
// *fix 1.1.0 Don't try to instantiate while in importing mode...
void *slib = Builtin::get_dll_handle();
Builtin::set_dll_handle(NULL);
try {
success = uc_eval(m_buffer,false,true,m_file.c_str(),m_lineno) == 0;
} catch(string s) {
inst_err = s;
} catch(...) {
//inst_err = "unknown instantiation error";
success = false;
}
Builtin::set_dll_handle(slib);
state.in_template = NULL;
state.pop_context();
restore_parser_state();
// *fix 1.2.7 lines swopped around...
state = old_state;
// dcl_reset();
Parser::debug.skip_method_bodies = old_skip;
// NB to _unbind_ the dummy parameters, ready for the next instantiation!
FORALL(ti,tl) if (ti->is_dummy()) ti->as_dummy()->unbind();
}
if (! success) {
instance->instantiated(false);
fail(inst_err);
} else {
instance->instantiated(true);
// can't call type() until the data is set!
if (Parser::debug.verbose) { //*change 1.2.4 no longer function_trace
cout << "instantiated: ";
Signature::set_fun_name(instance->name()); // only for function templates, of course!
try {
if (instance->data() != NULL) cout << instance->type();
} catch(...) {
cout << "<unknown>";
}
cout << endl;
}
}
}
TypeList& Template::formal_parms()
{ return (TypeList&)m_context->formal_parms(); }
const TypeList& Template::formal_args()
{ return m_formal_args; }
// static support routines for the parser
Type Template::get_template_type(PEntry te, TypeList *ptl)
{
// resolving the actual type, given an expression like Type <int,20> etc.
TemplateEntry *pte = (TemplateEntry *)te->data;
if(pte->match(*ptl)) {
return pte->match_instance()->type();
} else error("Cannot match this template");
return t_null;
}
bool in_template = false;
void Template::do_template_header(TypeList *ptl)
{
using Parser::state;
IContext *ic = new IContext(&state.context());
state.push_context(ic);
TLI tli;
FORALL(tli,*ptl) {
Type t = *tli;
DummyType *pd = tli->as_dummy();
string name = pd->name();
bool is_dummy = tli->is_unbound();
Type type;
// the 'true' dummy types are put in as is; the constants are put in as
// their type value.
if (is_dummy) type = *tli;
else type = pd->type();
PEntry pe;
if (is_dummy) pe = state.add_typedef(type,name);
else pe = state.add_variable(type,name,NULL,None);
// We bind the actual entry to any dummies
tli->as_dummy()->entry(pe);
}
ic->set_formal_parms(*ptl);
delete ptl;
// *hack 1.1.4 lexical tie-in
in_template = true;
}
void Template::do_function_template()
{
// appears immediately after the function prototype;
// *add 1.1.0 special case of method template; keep track of any entries created.
// *fix 1.1.3 exclude genuine member function templates!
// *hack 1.1.4 lexical tie-in
in_template = false;
try {
bool was_outside_class;
Signature *sig = Parser::get_prototype(was_outside_class);
string name = Signature::get_fun_name();
TypeList formal_args = sig->type_list();
IContext *ic = (IContext *)Parser::state.pop_context();
PEntry pe;
Table *cntxt;
bool was_method = false;
if (sig->class_ptr() != NULL && was_outside_class) {
cntxt = sig->class_ptr();
if (PClass(cntxt)->get_template()==NULL) fail("must be a class template here");
was_method = true;
} else cntxt = &Parser::state.context();
pe = cntxt->lookup(name);
FunctionEntry *pfun;
TemplateEntry *ptmpl;
if (pe == NULL) {
// Create a new FunctionEntry with a TemplateEntry
pe = cntxt->add(name);
pfun = Parser::create_function_entry(sig,name,pe);
ptmpl = new TemplateEntry(pe,false);
if (was_method) ptmpl->set_method();
pfun->set_template(ptmpl);
pe->type = Type(sig);
pe->data = (int)pfun;
if (was_method)
PClass(cntxt)->get_template()->get_template()->get_entry()->add_method_entry(pe);
} else if (! pe->type.is_function()) {
fail("already defined as a non-function");
} else {
// was previously defined as a function (which might not have been a template!)
pfun = (FunctionEntry *)pe->data;
ptmpl = pfun->get_template();
// Not really an error...but watch this carefully! (especially w/ methods!)
if (ptmpl == NULL) { // fail("previously defined as a function");
ptmpl = new TemplateEntry(pe,false);
pfun->set_template(ptmpl);
}
}
// add a new template to the TemplateEntry
Template *pnt = new Template(ptmpl,formal_args,ic);
ptmpl->add_template(pnt);
// Lippman 183, 2nd ed. formal args must contain all formal template type parms.
// (Not applicable to class template members, which don't need matching)
if (! was_method) {
bool all_present = true;
TLCI tlip, tlia;
const TypeList& formal_parms = pnt->formal_parms();
if (!ptmpl->is_method()) {
FORALL(tlip,formal_parms) {
int times_present = 0;
FORALL(tlia,formal_args) if (type_contains(*tlia,*tlip)) times_present++;
all_present = all_present && times_present > 0;
}
if (!all_present) { fail("All formal template type parms must be present at least once"); return; }
}
}
// and grab the function!!
generate_function_header(name,sig,true);
pnt->grab();
//*fix 1.2.0L Should not have deleted the signature!
// delete sig;
} catch(string msg) {
error(msg);
}
}
char* Template::generate_function_header(Function* pf, bool plain_method, bool qualified_names)
{
*mIBuffer = 0;
ostrstream outs(mIBuffer,TEMPL_BUFFSIZE);
Signature::write_qualified_names(qualified_names);
pf->dump(outs);
outs << ' ' << (plain_method ? '{' : ':') << ends;
Signature::write_qualified_names(true); // maintain the default setting!
return mIBuffer;
}
char* Template::generate_function_header(string name, Signature* sig, bool plain_method)
{
*mIBuffer = 0;
ostrstream outs(mIBuffer,TEMPL_BUFFSIZE);
Signature::set_fun_name(name);
outs << *sig << " {" << ends;
return mIBuffer;
}
void Template::do_class_template(int s_or_c, string name, int look_ahead, TypeList *ptl)
{
using Parser::state;
IContext *ic = (IContext *)state.pop_context();
// *hack 1.1.4 lexical tie-in
in_template = false;
// *fix 0.9.8 Doing class template specializations properly
TypeList formal_args;
PEntry pe = state.context().lookup(name);
if (pe && pe->type != t_template_type) { error("Not a template class " + Parser::quotes(name)); return; }
if (ptl != NULL) { // formal_args will only be non-trivial if this is a specialization;
if (pe == NULL) { error("Can only specialize a class template if it already exists"); return; }
formal_args = *ptl;
} else
formal_args = ic->formal_parms();
TemplateEntry *pte;
if (pe == NULL) {
pe = state.context().add(name);
pe->type = t_template_type;
pte = new TemplateEntry(pe,true);
pe->data = (int)pte;
pe->m_typename = true;
} else pte = (TemplateEntry *)pe->data;
Template *pct = new Template(pte,formal_args,ic);
pte->add_template(pct);
*mIBuffer = 0;
ostrstream outs(mIBuffer,TEMPL_BUFFSIZE);
outs << (s_or_c == CLASS_X ? "class " : "struct ") << name;
//* -2 (Obscure Bison number) means that there's no lookahead token...
//* in this case the look-ahead can be either '{' or ':'
if (look_ahead != -2) outs << (char)look_ahead;
outs << endl;
outs << ends;
pct->grab();
}
bool Template::type_contains(Type t1, Type t2)
// does the dummy type t1 contain t2? Two cases:
// (a) t1 is a qualified form of t2, e.g const T&
// (b) t1 is a template containing t2, e.g. list<T,A>&
{
if (t1.is_dummy() && t2.is_dummy()) {
DummyType *d1 = t1.as_dummy(), *d2 = t2.as_dummy();
if (d1 == d2 || d1 && d2 && d1->name() == d2->name()) return true; // case (a)
if (t1.is_class()) {
Class *pc = t1.as_class();
const TypeList& tl = pc->get_template()->type_parms();
TLCI tli;
FORALL(tli,tl) {
Type tp = *tli;
if (tp == t2) // || d2 && tp.is_dummy() && d2->name() == tp.as_dummy()->name())
return true;
}
return false;
//return utils::find(tl,t2); // case (b)
}
}
return false;
}
void massage_type_list(TypeList& tl)
{
TLI tli;
FORALL(tli,tl) {
Type t = *tli;
if (t.is_const() && ! t.is_ref_or_ptr()) t.strip_const(); // e.g. 'const int' -> 'int'
// 'variable references'! (see Expressions::entry_op())
else if (t.is_reference()) t.strip_reference(); //t.is_variable()
// the appearance of a function name means an _implied_ function pointer
if (t.is_signature() && !t.is_pointer()) t.incr_pointer();
*tli = t;
}
}
// support for things like Stack<int,20> and template<class T, int n>
// these guys will grab the four possibilities in such lists.
// For example , these would be:
// 1) class T - a named dummy
// 2) int N - a variable parameter
// 3) int - a type
// 4) 20 - a constant
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -