feat: C++ 阶段 2 - 模板函数基础

- template <class T> 函数: 捕获定义, 调用时按首参类型推断实例化
- 实例化命名: max(3,5)->max_i, max(1.5,2.5)->max_d, bigger(p1,p2)->bigger_Point
- 输出三段式布局: [声明/类] [模板实例化定义] [顶层函数]
- 类类型模板参数自动加 struct 前缀
- 多参数模板/模板类: 干净跳过并注释
- 修复: Cls a(3), b(7) 多声明符带构造参数
- 输出改为内存缓冲 (8MB) 支持重排
这个提交包含在:
Paze AI
2026-08-16 18:12:15 +08:00
父节点 7d6ca9130c
当前提交 68d5bae1ee
修改 2 个文件,包含 344 行新增8 行删除
二进制
查看文件
二进制文件未显示。
+344 -8
查看文件
@@ -36,6 +36,33 @@ static Token g_tok;
static int g_line = 1;
static int g_ns_open = 0; /* namespace block depth (flattened) */
/* ---------------- templates ---------------- */
typedef struct TplFn {
char name[64]; /* template function name */
char tparam[32]; /* type parameter name (e.g. "T") */
char body[4096]; /* captured function text with T placeholder */
struct TplFn *next;
} TplFn;
static TplFn *g_tplfns = NULL;
typedef struct TplInst {
char name[128]; /* mangled instantiated name, e.g. max_i */
struct TplInst *next;
} TplInst;
static TplInst *g_tplinsts = NULL;
static int g_capture = 0; /* capture emit() into g_cap */
static char g_cap[4096];
static int g_cap_len;
static int g_emit_sect = 0; /* 0 = decl buffer, 1 = function buffer */
static char g_outbuf[8 * 1024 * 1024]; /* declarations (classes, globals) */
static int g_out_len;
static char g_fn_buf[8 * 1024 * 1024]; /* top-level function bodies */
static int g_fn_len;
static char g_tpl_defs[16384]; /* instantiated definitions (flushed at end) */
static int g_tpl_defs_len;
typedef struct ClassInfo {
char name[128];
char base[128];
@@ -355,8 +382,23 @@ static int g_out_is_word = 0; /* last emitted char was word char */
static void emit(const char *fmt, ...)
{
va_list ap;
int n;
va_start(ap, fmt);
vfprintf(g_out, fmt, ap);
if (g_capture) {
n = vsnprintf(g_cap + g_cap_len, sizeof g_cap - g_cap_len, fmt, ap);
if (n > 0) g_cap_len += n;
if (g_cap_len >= (int)sizeof g_cap - 1) g_cap_len = sizeof g_cap - 1;
} else if (g_emit_sect == 1) {
if (g_fn_len < (int)sizeof g_fn_buf - 1) {
n = vsnprintf(g_fn_buf + g_fn_len,
sizeof g_fn_buf - g_fn_len, fmt, ap);
if (n > 0) g_fn_len += n;
}
} else if (g_out_len < (int)sizeof g_outbuf - 1) {
n = vsnprintf(g_outbuf + g_out_len,
sizeof g_outbuf - g_out_len, fmt, ap);
if (n > 0) g_out_len += n;
}
va_end(ap);
}
@@ -413,6 +455,7 @@ static void proto_emit(const char *fmt, ...)
/* ---------------- class parsing ---------------- */
static void gather_decl(char *type, int typesz, char *name, int namesz);
static int tpl_call_check(char varcls[][128], char varname[][64], int nvars);
static void parse_member_decl(const char *cls, const char *ret_type, const char *mname_in)
{
@@ -555,6 +598,9 @@ static void parse_member_decl(const char *cls, const char *ret_type, const char
}
unread_tok(&nxt);
}
/* template function call inside a method body */
if (tpl_call_check(NULL, NULL, 0))
continue;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) { bd++; proto_emit(" {"); prev_dot = 0; next_tok(); continue; }
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) { bd--; if (bd > 0) proto_emit("}"); next_tok(); continue; }
if (g_tok.type == T_THIS) { proto_emit("this"); prev_dot = 0; next_tok(); continue; }
@@ -978,6 +1024,9 @@ static void parse_member_definition(const char *ret_type)
}
unread_tok(&nxt);
}
/* template function call inside a method body */
if (tpl_call_check(NULL, NULL, 0))
continue;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) { depth++; emit(" {"); prev_dot = 0; next_tok(); continue; }
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
if (depth > 0) { depth--; emit("}"); next_tok(); continue; }
@@ -1258,6 +1307,9 @@ static void parse_free_operator(const char *ret_type, const char *name)
}
unread_tok(&nxt);
}
/* template function call inside an operator body */
if (tpl_call_check(NULL, NULL, 0))
continue;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) {
bdepth++;
emit(" {");
@@ -1362,6 +1414,135 @@ static void parse_free_operator(const char *ret_type, const char *name)
}
}
/* ---------------- templates ---------------- */
/* replace standalone identifiers 'from' with 'to' in src */
static void tpl_subst(char *dst, int dsz, const char *src,
const char *from, const char *to)
{
int d = 0, i = 0, fl = (int)strlen(from), tl = (int)strlen(to);
while (src[i] && d < dsz - 1) {
if (!strncmp(src + i, from, fl) &&
(i == 0 || !(isalnum((unsigned char)src[i-1]) || src[i-1] == '_')) &&
!(isalnum((unsigned char)src[i+fl]) || src[i+fl] == '_')) {
if (d + tl < dsz - 1) {
memcpy(dst + d, to, tl);
d += tl;
}
i += fl;
} else {
dst[d++] = src[i++];
}
}
dst[d] = 0;
}
static TplFn *find_tplfn(const char *name)
{
TplFn *p;
for (p = g_tplfns; p; p = p->next)
if (!strcmp(p->name, name)) return p;
return NULL;
}
static const char *type_suffix(const char *t)
{
if (!strcmp(t, "int")) return "i";
if (!strcmp(t, "double")) return "d";
if (!strcmp(t, "float")) return "f";
if (!strcmp(t, "char")) return "c";
if (!strcmp(t, "long")) return "l";
if (!strcmp(t, "short")) return "s";
if (!strcmp(t, "unsigned int")) return "u";
return t; /* class name */
}
/* emit (once) the instantiated definition of a template function */
static void instantiate_tpl_fn(TplFn *tf, const char *ttype, const char *mname)
{
TplInst *p;
for (p = g_tplinsts; p; p = p->next)
if (!strcmp(p->name, mname)) return;
p = (TplInst*)calloc(1, sizeof *p);
strncpy(p->name, mname, sizeof p->name - 1);
p->name[sizeof p->name - 1] = 0;
p->next = g_tplinsts;
g_tplinsts = p;
{
char tmp[4096], def[4096];
char stype[96];
const char *subst_type = ttype;
if (find_class(ttype)) {
snprintf(stype, sizeof stype, "struct %s", ttype);
subst_type = stype;
}
tpl_subst(tmp, sizeof tmp, tf->body, tf->tparam, subst_type);
tpl_subst(def, sizeof def, tmp, tf->name, mname);
{
int l = (int)strlen(def);
if (g_tpl_defs_len + l + 1 < (int)sizeof g_tpl_defs) {
memcpy(g_tpl_defs + g_tpl_defs_len, def, l);
g_tpl_defs_len += l;
g_tpl_defs[g_tpl_defs_len++] = '\n';
}
}
}
}
/* If g_tok is a template function name followed by '(', infer the
concrete type from the first argument and rewrite g_tok to the
mangled instantiation name (the def is queued in g_tpl_defs).
Returns 1 if handled. */
static int tpl_call_check(char varcls[][128], char varname[][64], int nvars)
{
TplFn *tf;
Token nxt, a1;
const char *ttype = "int";
char suffix[64];
char mname[128];
if (g_tok.type != T_IDENT) return 0;
tf = find_tplfn(g_tok.text);
if (!tf) return 0;
read_tok(&nxt);
if (!(nxt.type == T_PUNCT && !strcmp(nxt.text, "("))) {
unread_tok(&nxt);
return 0;
}
/* stream is now at the first argument; read it to infer the type */
read_tok(&a1);
if (a1.type == T_NUMBER) {
if (strchr(a1.text, '.') || strchr(a1.text, 'e') ||
strchr(a1.text, 'E') || strchr(a1.text, 'f') ||
strchr(a1.text, 'F') || strchr(a1.text, 'l'))
ttype = "double";
else
ttype = "int";
} else if (a1.type == T_CHAR) {
ttype = "char";
} else if (a1.type == T_IDENT && nvars > 0) {
int k;
ttype = "int";
for (k = 0; k < nvars; k++)
if (!strcmp(a1.text, varname[k])) {
ttype = varcls[k];
break;
}
}
/* restore the stream: arg back, then '(' back */
unread_tok(&a1);
unread_tok(&nxt);
/* g_tok is still the template name; rewrite it to the mangle */
strncpy(suffix, type_suffix(ttype), sizeof suffix - 1);
suffix[sizeof suffix - 1] = 0;
snprintf(mname, sizeof mname, "%s_%s", tf->name, suffix);
instantiate_tpl_fn(tf, ttype, mname);
g_tok.type = T_IDENT;
strncpy(g_tok.text, mname, sizeof g_tok.text - 1);
g_tok.text[sizeof g_tok.text - 1] = 0;
return 1;
}
static void parse_function(const char *ret_type, const char *name)
{
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "::")) {
@@ -1405,6 +1586,9 @@ static void parse_function(const char *ret_type, const char *name)
}
unread_tok(&nxt);
}
/* template function call: max(a, b) -> max_i(a, b) */
if (tpl_call_check(varcls, varname, nvars))
continue;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) { depth++; emit(" {"); next_tok(); continue; }
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
if (depth > 0) { depth--; emit("}"); next_tok(); continue; }
@@ -1499,7 +1683,7 @@ static void parse_function(const char *ret_type, const char *name)
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) next_tok();
}
continue;
goto add_decls;
}
/* plain declaration: Cls var; -> default ctor */
if (g_tok.type == T_PUNCT &&
@@ -1510,16 +1694,53 @@ static void parse_function(const char *ret_type, const char *name)
}
}
}
/* additional declarators: Cls a, b, c; */
/* additional declarators: Cls a, b(2), c; */
add_decls:
while (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",")) {
emit(", ");
next_tok();
if (g_tok.type == T_IDENT && nvars < 64) {
char vn[64];
strncpy(varcls[nvars], clsname, 127);
strncpy(varname[nvars], g_tok.text, 63);
strncpy(vn, g_tok.text, sizeof vn - 1);
vn[sizeof vn - 1] = 0;
nvars++;
emit(" %s", g_tok.text);
next_tok();
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
ClassInfo *cci2 = find_class(clsname);
if (cci2 && cci2->has_ctor) {
/* declarator with ctor args: a(3), b(7) */
emit("; struct %s %s; %s_ctor(&%s",
clsname, vn, clsname, vn);
next_tok();
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) {
emit(")");
next_tok();
} else {
emit(", ");
int adepth = 0;
while (!(g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")") &&
adepth == 0)) {
if (g_tok.type == T_EOF) break;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, "(")) adepth++;
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) adepth--;
emit_tok();
next_tok();
}
emit(")");
if (g_tok.type == T_PUNCT &&
!strcmp(g_tok.text, ")")) next_tok();
}
continue; /* look for more declarators */
}
emit(", %s(", vn);
continue;
}
emit(", %s", vn);
}
}
continue;
@@ -1727,6 +1948,100 @@ static void gather_decl(char *type, int typesz, char *name, int namesz)
/* no name found; caller handles */
}
/* skip a declaration up to ';' or a balanced '}' */
static void skip_decl(void)
{
int depth = 0;
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "{")) depth++;
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "}")) {
if (depth == 0) { next_tok(); return; }
depth--;
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ";") && depth == 0) {
next_tok();
return;
}
next_tok();
}
}
/* template <class T> function/class */
static void parse_template(void)
{
char tparam[32] = "T";
char tybuf[512], nmbuf[256];
int multi = 0;
next_tok(); /* '<' */
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "<")) {
next_tok();
/* class T | typename T | struct T */
if (g_tok.type == T_CLASS || g_tok.type == T_STRUCT ||
(g_tok.type == T_IDENT && !strcmp(g_tok.text, "typename")))
next_tok();
if (g_tok.type == T_IDENT) {
strncpy(tparam, g_tok.text, sizeof tparam - 1);
tparam[sizeof tparam - 1] = 0;
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ",")) multi = 1;
/* skip to '>' */
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ">")) break;
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ">")) next_tok();
} else if (g_tok.type == T_CLASS) {
/* template<class T> */
next_tok();
if (g_tok.type == T_IDENT) {
strncpy(tparam, g_tok.text, sizeof tparam - 1);
tparam[sizeof tparam - 1] = 0;
next_tok();
}
if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, ">")) next_tok();
}
if (multi) {
/* multi-parameter templates: not supported, skip declaration */
skip_decl();
emit("/* skipped multi-param template */\n");
return;
}
if (g_tok.type == T_CLASS || g_tok.type == T_STRUCT) {
/* template class: not yet supported, skip it cleanly */
skip_decl();
emit("/* skipped template class */\n");
return;
}
/* template function */
gather_decl(tybuf, sizeof tybuf, nmbuf, sizeof nmbuf);
if (nmbuf[0]) {
g_capture = 1;
g_cap_len = 0;
parse_function(tybuf[0] ? tybuf : "int", nmbuf);
g_capture = 0;
g_cap[g_cap_len] = 0;
{
TplFn *tf = (TplFn*)calloc(1, sizeof *tf);
strncpy(tf->name, nmbuf, sizeof tf->name - 1);
tf->name[sizeof tf->name - 1] = 0;
strncpy(tf->tparam, tparam, sizeof tf->tparam - 1);
tf->tparam[sizeof tf->tparam - 1] = 0;
strncpy(tf->body, g_cap, sizeof tf->body - 1);
tf->body[sizeof tf->body - 1] = 0;
tf->next = g_tplfns;
g_tplfns = tf;
}
} else {
skip_decl();
emit("/* skipped unknown template */\n");
}
}
static void parse_program(void)
{
for (;;) {
@@ -1811,9 +2126,14 @@ static void parse_program(void)
continue;
}
if (g_tok.type == T_USING || g_tok.type == T_TEMPLATE ||
g_tok.type == T_INLINE || g_tok.type == T_CONSTEXPR ||
g_tok.type == T_FRIEND || g_tok.type == T_VIRTUAL) {
if (g_tok.type == T_TEMPLATE) {
parse_template();
continue;
}
if (g_tok.type == T_USING || g_tok.type == T_INLINE ||
g_tok.type == T_CONSTEXPR || g_tok.type == T_FRIEND ||
g_tok.type == T_VIRTUAL) {
next_tok();
while (!(g_tok.type == T_EOF)) {
if (g_tok.type == T_PUNCT &&
@@ -1837,13 +2157,19 @@ static void parse_program(void)
char tybuf[512], nmbuf[256];
gather_decl(tybuf, sizeof tybuf, nmbuf, sizeof nmbuf);
if (nmbuf[0]) {
int was = g_emit_sect;
g_emit_sect = 1;
parse_function(tybuf[0] ? tybuf : "int", nmbuf);
g_emit_sect = was;
} else if (g_tok.type == T_EOF) {
break;
} else if (g_tok.type == T_PUNCT && !strcmp(g_tok.text, "(")) {
/* function without return type (C style) */
int was = g_emit_sect;
g_emit_sect = 1;
emit("int ");
parse_function("int", "f");
g_emit_sect = was;
} else {
/* global variable: type + rest */
if (tybuf[0]) emit("%s", tybuf);
@@ -1883,6 +2209,16 @@ int main(int argc, char **argv)
next_tok();
parse_program();
/* layout: [declarations/classes] [template instantiations]
[top-level functions] */
fwrite(g_outbuf, 1, g_out_len, g_out);
if (g_tpl_defs_len) {
fputc('\n', g_out);
fwrite(g_tpl_defs, 1, g_tpl_defs_len, g_out);
}
fputc('\n', g_out);
fwrite(g_fn_buf, 1, g_fn_len, g_out);
fclose(g_in);
if (g_out != stdout) fclose(g_out);
return 0;