当前位置:首页 > 技术 > 正文内容

嵌入式脚本语言中的对象模型设计与实现

访客 技术 2026年7月22日 4

对象模型的设计理念

在构建嵌入式脚本语言时,核心之一是设计一个灵活且高效的数据表示结构。本文描述的对象模型采用树形节点结构,支持四种基本类型:普通对象、指针、属性值和数组。这种设计允许通过路径表达式(如 user.profile[0].name)访问深层数据,适用于代码生成、配置解析等场景。

该模型的核心思想是将所有数据抽象为可嵌套的节点,每个节点根据其类型决定如何解释其内容。这使得系统能够统一处理复杂数据结构,同时保持接口简洁。

核心数据结构定义

使用 C++ 实现的主结构体 CNode 如下:


struct CNode {
    enum NodeType { Object, Pointer, Value, Array };

    NodeType type;
    CNode* target;           // 用于指针类型
    std::string valueData;   // 用于存储字符串或数字的值
    std::map<std::string, CNode> children;     // 子节点集合
    std::vector<CNode> arrayItems;           // 数组元素

    CNode() : type(Object), target(nullptr) {}
    
    // 各种操作方法见下文
};

该结构通过枚举区分不同类型,并仅在运行时依据 type 字段判断当前应使用哪个成员变量。若需优化内存占用,可改用 union 包裹非共享字段。

数据读取与写入封装

为避免直接暴露内部状态,提供了若干安全的访问方法:


std::string getValue() const {
    switch (type) {
        case Value:
            return valueData;
        case Object:
            auto it = children.find("default");
            if (it != children.end()) return it->second.getValue();
            return "";
        case Pointer:
            return target ? target->getValue() : "";
        case Array:
            return "[array]";
        default:
            return "[unknown]";
    }
}

void setValue(const std::string& val) {
    type = Value;
    valueData = val;
}

void setValue(int num) {
    type = Value;
    valueData = std::to_string(num);
}

此外提供向容器添加子项的方法:


void addObjectChild(const std::string& key, const CNode& node) {
    type = Object;
    children[key] = node;
}

void addReference(const std::string& key, CNode* ref) {
    type = Object;
    CNode ptrNode;
    ptrNode.type = Pointer;
    ptrNode.target = ref;
    children[key] = ptrNode;
}

void createAndAppendToArray(const std::string& key) {
    type = Object;
    if (children.find(key) == children.end()) {
        CNode arrNode;
        arrNode.type = Array;
        children[key] = arrNode;
    }
}

void appendToArray(const std::string& key, const CNode& item) {
    createAndAppendToArray(key);
    children[key].arrayItems.push_back(item);
}

void appendValueToArray(const std::string& key, const std::string& val) {
    CNode temp;
    temp.setValue(val);
    appendToArray(key, temp);
}

基于路径的对象查找机制

支持以点号分隔并包含数组索引的路径查询,例如 config.servers[2].ip。实现如下:


CNode* findNode(const std::string& path) {
    std::vector<std::string> segments;
    size_t start = 0;
    while (start < path.size()) {
        size_t end = path.find('.', start);
        if (end == std::string::npos) end = path.size();
        segments.push_back(path.substr(start, end - start));
        start = end + 1;
    }

    CNode* current = this;
    for (const auto& segment : segments) {
        // 解析是否带索引:name[index]
        size_t bracket = segment.find('[');
        std::string name = segment;
        int index = -1;
        if (bracket != std::string::npos) {
            name = segment.substr(0, bracket);
            std::string idxStr = segment.substr(bracket + 1, segment.size() - bracket - 2);
            index = std::stoi(idxStr);
        }

        // 跳转指针
        while (current && current->type == Pointer) {
            current = current->target;
        }

        if (!current) return nullptr;

        if (current->type == Object) {
            auto it = current->children.find(name);
            if (it == current->children.end()) return nullptr;
            current = &it->second;
        } else {
            return nullptr; // 非对象无法继续遍历
        }

        // 处理数组索引
        if (index != -1) {
            if (current->type != Array || index < 0 || index >= (int)current->arrayItems.size())
                return nullptr;
            current = &current->arrayItems[index];
        }
    }
    return current;
}

结构化输出与调试显示

为了便于调试和日志输出,实现了带缩进的递归打印功能:


std::string toStringWithIndent(int depth = 0, int maxDepth = -1) const {
    if (maxDepth >= 0 && depth >= maxDepth) {
        return "\t" * depth + "[depth limit]\n";
    }

    std::string indent(depth * 4, ' ');
    std::ostringstream oss;

    switch (type) {
        case Value:
            oss << indent << "VALUE: \"" << valueData << "\"\n";
            break;
        case Pointer:
            oss << indent << "-> POINTER:\n";
            if (target) {
                oss << target->toStringWithIndent(depth + 1, maxDepth);
            } else {
                oss << indent << "  [null target]\n";
            }
            break;
        case Array:
            oss << indent << "ARRAY[" << arrayItems.size() << "]:\n";
            for (size_t i = 0; i < arrayItems.size(); ++i) {
                oss << indent << "  [" << i << "]:\n";
                oss << arrayItems[i].toStringWithIndent(depth + 2, maxDepth);
            }
            break;
        case Object:
            if (children.empty()) {
                oss << indent << "{empty}\n";
            } else {
                oss << indent << "{\n";
                for (const auto& pair : children) {
                    oss << indent << "  " << pair.first << ":\n";
                    oss << pair.second.toStringWithIndent(depth + 2, maxDepth);
                }
                oss << indent << "}\n";
            }
            break;
        default:
            oss << indent << "[invalid type]\n";
            break;
    }
    return oss.str();
}

此方法按层级缩进输出,支持深度限制,适合用于大型结构的概览展示。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。