You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Yacc解析器与外部Libpq程序之间共享变量?

Yacc解析器与Libpq程序共享数据的最佳方案

首先要明确你的场景是将两者合并为同一个进程还是保持两个独立进程,不同场景的最优方案差异很大:

场景一:合并为同一个进程(推荐)

如果解析和后续Libpq处理是流水线式的逻辑,最好将Yacc生成的解析器代码与Libpq程序编译链接成一个可执行文件,这比跨进程通信高效得多,也更易维护。

最优实现:用结构体封装解析结果,通过函数传递

不要用全局变量,而是将解析得到的所有数据封装到一个自定义结构体中,在解析过程中填充,解析完成后直接将结构体实例传递给Libpq处理逻辑。示例如下:

  1. 在共享头文件中定义解析结果结构体:
// parse_result.h
#ifndef PARSE_RESULT_H
#define PARSE_RESULT_H

typedef struct {
    char* target_table;
    int filter_count;
    char** filter_conditions;
    // 根据你的需求添加其他字段
} ParseResult;

// 初始化和销毁函数,避免内存泄漏
ParseResult* parse_result_init();
void parse_result_destroy(ParseResult* res);

#endif
  1. 在Yacc的语法规则动作中填充结构体:
// 假设YYSTYPE中包含ParseResult指针
%union {
    char* str;
    ParseResult* res;
}

// 语法规则示例
%%
query: SELECT target_table WHERE filter_list {
    // $2是target_table字符串,$4是filter条件数组
    $$->target_table = $2;
    $$->filter_count = get_filter_count($4);
    $$->filter_conditions = copy_filter_array($4);
};
%%

// 解析入口函数,返回填充好的结构体
ParseResult* parse_input(const char* input) {
    ParseResult* res = parse_result_init();
    // 将res关联到YYSTYPE,让语法动作可以访问
    yylval.res = res;
    yy_scan_string(input);
    yyparse();
    yylex_destroy();
    return res;
}
  1. 在Libpq主程序中调用解析函数并处理:
#include "parse_result.h"
#include <libpq-fe.h>

int main() {
    const char* input = "SELECT users WHERE age > 18 AND active = true";
    ParseResult* res = parse_input(input);

    // 用res中的数据构造SQL查询,调用Libpq接口
    PGconn* conn = PQconnectdb("dbname=mydb user=postgres");
    if (PQstatus(conn) != CONNECTION_OK) {
        // 错误处理
        PQfinish(conn);
        parse_result_destroy(res);
        return 1;
    }

    // 示例:用解析结果构造查询
    char* query = construct_query(res);
    PGresult* pg_res = PQexec(conn, query);

    // 处理Libpq结果...

    // 清理资源
    PQclear(pg_res);
    PQfinish(conn);
    free(query);
    parse_result_destroy(res);
    return 0;
}

这种方式比全局变量更模块化,避免了全局状态带来的命名冲突、并发安全问题,也更易调试和维护。

场景二:必须保持两个独立进程

如果因为架构限制必须分成两个独立进程,最优的进程间通信方式取决于数据量和性能需求:

  • 匿名管道:如果是父子进程(比如解析器fork出Libpq进程),匿名管道是最简单高效的方式,直接通过标准输入输出传递序列化后的解析结果。
  • 命名管道(FIFO):如果是无亲缘关系的进程,用FIFO可以实现双向通信,比写入临时文件更高效,不需要磁盘持久化。
  • 共享内存:如果数据量很大,共享内存是性能最优的选择,但需要配合信号量或互斥锁来解决同步问题,避免竞态条件。

对你提出的三个方案的分析

  1. 共享头文件全局变量:如果是两个独立进程,全局变量属于各自的进程地址空间,根本无法共享;即使是同一个进程,全局变量也容易引发状态混乱,不推荐。
  2. 文件/管道:管道是可行的,但临时文件会带来磁盘IO开销,优先选管道类IPC。
  3. 链接在一起访问全局变量:链接成同一个进程是可行的,但用全局变量不如结构体传递规范,维护性差。

内容的提问来源于stack exchange,提问作者AmrShams07

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 10:33:29