📄 templates.cpp
字号:
// These are represented by dummy types, except for (3), which just pass through.
// (See the end of parser.y)
Type Template::dummy(Type t, string name)
{
if (t != t_null) {
if (name == "") { // e.g. just plain 'int' passes through (3)
return t;
} else { // *fix 1.2.9 force the const type
t.make_const();
// 'int n' -> unbound variable dummy (2)
return Type::make_dummy(name,t,NULL);
}
} else
// 'class T' -> classic type dummy (1)
return Type::make_dummy(name,t_null,NULL);
}
Type Template::dummy(PEntry pe)
{
return Type::make_dummy("",pe->type,pe); // constant entry dummy (4)
}
char *get_method_name(char *line);
const int MAX_LINE_SIZE = 256;
void Template::grab()
{
char buff[MAX_LINE_SIZE];
// in general stuff will already be written into the buffer; may (or not) include the ':' or '{'
int already_written = strlen(mIBuffer);
int brace_count = already_written > 0 ? count(mIBuffer,'{') : 0;
char *ptr = mIBuffer + already_written; // - 1; *NOTE*
strcpy(ptr,"\n"); // seems to make a difference...
ptr ++;
do {
Input::grab_next_line(buff);
brace_count += count(buff,'{') - count(buff,'}');
if (*buff != '\0') strcpy(ptr,buff);
ptr += strlen(buff);
strcpy(ptr,"\n"); //*I suspect we need this because we cannot handle very long lines!
ptr ++;
//brace_count += count(buff,'{') - count(buff,'}');
} while (brace_count > 0);
if (Parser::debug.verbose) cout << "*buff: " << mIBuffer << endl;
m_buffer = strdup(mIBuffer);
*mIBuffer = '\0';
}
// *add 1.1.0 This is obviously very similar to the above; next time I want to combine
// the functionality, but for now I just want to know that (a) it works and (b) doesn't
// interfere with stuff that already works.
// *fix 1.2.4 If this is a ctor with an init list, handle the special case by always
// grabbing everything up to '{'
void Template::grab_function_body(bool plain_method, char* body)
{
int brace_count = plain_method ? 1 : 0;
bool grabbing_init = ! plain_method;
char* ptr;
char buff[MAX_LINE_SIZE];
int open_kount, close_kount;
if (body) ptr = body + strlen(body);
do {
Input::grab_next_line(buff);
open_kount = count(buff,'{');
close_kount = count(buff,'}');
brace_count += open_kount - close_kount;
if (open_kount) grabbing_init = false;
if (body) {
if (*buff != '\0') strcpy(ptr,buff);
ptr += strlen(buff);
strcpy(ptr,"\n"); //*I suspect we need this because we cannot handle very long lines!
ptr ++;
}
} while (brace_count > 0 || grabbing_init);
}
Template *Template::as_template(Type t)
{
if (t.is_template_class()) // peculiar expr gets the actual template,
// not just the particular instance!
return t.as_class()->get_template()->get_template();
else return NULL; //*NOTE* until we can sort this out!
}
static string mErrStr;
int err(const string& s)
{ mErrStr = s; return 0; }
int Template::match(const TypeList& tl)
{
TLCI tlif, tlia;
if (tl == m_formal_args) return 999; // *fix 1.0.0 full specialization
int effort = 0;
for(tlif = m_formal_args.begin(), tlia = tl.begin(); tlia != tl.end(); ++tlif, ++tlia) {
Type tf = *tlif, ta = *tlia;
if (! tf.is_dummy()) {
// *fix 1.1.1 must be able to match all plain args of template functions
if (::match(tf,ta)==NO_MATCH) return 0;
continue; // NB rest is not for plain arguments!!
}
if (tf.as_dummy() != NULL) tf.as_dummy()->unbind();
if (tf.is_const()) { // Quite safe to remove const even if the actual arg isn't...
ta.strip_const(); tf.strip_const(); effort++;
}
if (tf.is_reference()) {
/*if (ta.is_reference())*/
{ ta.strip_reference(); tf.strip_reference(); effort++; }
}
while (tf.is_pointer()) {
if (ta.is_pointer()) { ta.decr_pointer(); tf.decr_pointer(); effort++; }
else break;
}
// at this point, the formal arg type is either 'bare'
// or a template class.
if (!tf.is_bare()) return err("cannot match");
if (ta.is_const() && ta.is_ref_or_ptr()) return err("parameter is not const");
effort++;
if (tf.is_template_class()) {
if(ta.is_template_class()) {
TemplateInstance * titf = tf.as_class()->get_template();
TemplateInstance * tita = ta.as_class()->get_template();
// but do they belong to the same template?
if (titf->get_template() == tita->get_template()) {
effort++;
TypeList& tlf = titf->type_parms();
TypeList& tla = tita->type_parms();
TLI tlif,tlia;
for(tlif=tlf.begin(),tlia=tla.begin(); tlif != tlf.end(); tlif++,tlia++) {
if (!tlif->as_dummy()->bind_to(*tlia)) return err("already bound to a diff. type");
}
} else return 0; // no way we can match different templates!
} else return 0; // cannot match template w/ a non-template class!
} else {
if (! tf.as_dummy()->bind_to(ta)) return err("already bound");
}
}
return effort;
}
Type TemplateInstance::type()
{
if (m_template->is_class()) return Type((Class *)data());
else return Type(((Function *)data())->signature());
}
string TemplateInstance::name()
{ return m_template->name(); }
TemplateInstance::TemplateInstance(Template *templ, const TypeList& tl)
: m_template(templ), m_type_parms(templ->formal_parms()), m_type_args(tl),m_instantiated(false),
m_data(NULL)
{ }
bool TemplateEntry::instantiate_method(Function *fn)
{
if (fn->get_template()) return true; // fine - already instantiated!
// This function doesn't have a TemplateInstance initially
// *hack 1.1.0 The hack here is to find the template corresponding to this function
// in the overloaded set. The assumption of course is that they have been
// defined in the same order as they were declared!
FunctionEntry *pfe = fn->fun_entry();
FunctionEntry::iterator fei;
TemplateList::iterator tli;
Template *templ;
for(fei = pfe->begin(), tli = m_templates.begin(); fei != pfe->end(); ++fei,++tli)
if (*fei == fn) templ = *tli;
PClass pc = fn->class_context();
TemplateInstance *c_inst = pc->get_template();
TemplateInstance *tinst = new TemplateInstance(templ,c_inst->type_list());
try {
copy_type_list(tinst->type_parms(),c_inst->type_parms());
TLCI tlif = tinst->get_template()->formal_parms().begin(), tlia = c_inst->type_parms().begin();
for(; tlia != c_inst->type_parms().end(); ++tlif, ++tlia)
(*tlif).as_dummy()->bind_to(*tlia);
templ->instantiate(tinst);
tinst->instantiated(true);
// and mark as instantiated...
fn->set_template(tinst);
tinst->data(fn);
return true;
} catch(string s) {
error(s);
return false;
} catch(...) {
error("unknown error");
return false;
}
}
int TemplateEntry::simple_match(const TypeList& tl, bool use_args)
{
TemplateInstanceList::iterator tili;
int i = 0;
FORALL(tili,m_instances) {
TemplateInstance *ti = *tili;
const TypeList& tlinst = use_args ? ti->type_list() : ti->type_parms();
if (Parser::debug.verbose) cout << "*cmp " << tl << ' ' << tlinst << endl;
if (tl == tlinst) return i;
i++;
}
return -1;
}
int TemplateEntry::no_instances()
// returns the number of _instantiated_ instances
{
TemplateInstanceList::iterator tili;
int i = 0;
FORALL(tili,m_instances) if ((*tili)->instantiated()) i++;
return i;
}
TemplateInstance *TemplateEntry::match_instance()
{
if (m_match_idx == -1) return NULL;
return utils::list_item(m_instances,m_match_idx);
}
string TemplateEntry::last_error()
{
string ret = mErrStr;
mErrStr = "";
return ret;
}
string TemplateEntry::name()
{
return m_entry->name;
}
void TemplateEntry::add_template(Template *templ)
{
utils::add_unique(m_templates,templ);
}
Template* TemplateEntry::templates(int i)
{
return utils::list_item(m_templates,i);
}
void TemplateEntry::add_method_entry(PEntry pe)
{
if (m_method_entries == NULL) m_method_entries = new EntryList();
m_method_entries->push_back(pe);
}
const EntryList *TemplateEntry::method_entries()
{
return m_method_entries;
}
void copy_type_list(TypeList& tlp, const TypeList& tlt)
{
TLCI ti;
TLI tip;
for(tip=tlp.begin(),ti=tlt.begin(); ti != tlt.end(); ++ti, ++tip) {
if (ti->is_dummy()) {
DummyType *dt = ti->as_dummy();
*tip = dt->type();
} else *tip = *ti;
}
}
bool TemplateEntry::match(const TypeList& actual_parms)
{
if (is_method()) return false;
// ensure that variables lose their implicit referenceness, etc.
TypeList tl = actual_parms;
massage_type_list(tl);
set_index(-1);
using Parser::state;
// Do we have an existing instance which will match exactly?
int idx = simple_match(tl);
if (idx != -1) {
set_index(idx);
return true;
}
// Build up a candidate list of templates which could theoretically match this list
TemplateList tel;
int no_args = tl.size();
TemplateList::iterator teli;
FORALL(teli, m_templates)
if ((*teli)->formal_args().size() == no_args) tel.push_back(*teli);
if (tel.size()==0) return err("Wrong number of type arguments");
// now find the template that scores the highest! Even if there is only one
// candidate, we go through this to see if we can actually do the match and
// (most importantly) to do the type binding
Template *templ;
int score,max_score = 0;
FORALL(teli,tel) {
score = (*teli)->match(tl);
if (score > max_score) { templ = *teli; max_score = score; }
}
if (max_score == 0) {
string error = last_error();
return err(error != "" ? error : "Cannot match any template");
}
// at this point the template parameters should be successfully bound
// and a new instance of this template can officially exist (although not yet
// instantiated, of course.)
TemplateInstance *instance = new TemplateInstance(templ,tl);
// Meanwhile, we fill in the instance's type parameters
copy_type_list(instance->type_parms(),templ->formal_parms());
if (m_is_class) {
TemplateInstance *old_inst = state.in_template;
// Generate an 'empty' class for the template instance (like one declared forwardly)
string qualified_name = build_qualified_name(name(),tl);
state.push_context(templ->context());
state.in_template = instance;
state.add_class(CLASS,qualified_name,ForwardClass,t_void);
state.in_template = old_inst; // *fix 0.9.7 restore this for nested instantiations
state.pop_context();
//*fix 1.2.9 this is the actual parent context of this template
NamedTable* tbl = static_cast<NamedTable*>(templ->context()->parent_context());
PEntry pe = tbl->lookup(qualified_name); // wuz state.context().
Class *pc = pe->type.as_class();
instance->data(pc);
instance->entry(pe);
pc->set_template(instance);
} else { // strictly speaking, this is not correct in _all cases_ but this will do for now.
try {
templ->instantiate(instance);
} catch(string msg) {
return err(msg);
}
}
add_instance(instance);
return true;
}
void TemplateEntry::add_instance(TemplateInstance* ti)
{
int ni = m_instances.size();
m_instances.push_back(ti);
set_index(ni);
}
int count(char *p, char ch)
{
int k = 0;
while (*p != '\0') if (*p++ == ch) k++;
return k;
}
char *get_method_name(char *line)
{
char buff[MAX_LINE_SIZE];
strcpy(buff,line);
char *p = strstr(buff,"(");
if (p==NULL) return NULL;
p--;
while(isspace(*p)) p--;
*(p+1) = '\0';
while(!isspace(*p)) p--;
p++;
if (strncmp(p,"operator",8)==0) p+=8;
return p;
}
ostream& operator<< (ostream& outs, const TypeList& tl)
{
outs << '<';
TLCI tli, tl_last = tl.end();
--tl_last;
FORALL(tli,tl) {
outs << *tli;
if (tli != tl_last) outs << ',';
}
outs << '>';
return outs;
}
string build_qualified_name(const string& name, const TypeList& tl)
{
char buff[512];
ostrstream outs(buff,512);
outs << name << tl;
outs << ends;
return buff;
}
char *sh_tlistc(const TypeList& tl) /*DEBUG*/
{
static char buff[128];
ostrstream outs(buff,128);
outs << tl << ends;
return buff;
}
char *sh_tlist(TypeList& tl)
{
return sh_tlistc((const TypeList&)tl);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -