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

如何将不同类的assign成员函数传递给通用解析函数以复用代码?

复用Component与Shader的命令解析逻辑

问题背景

开发命令系统时,需要处理两类对象的赋值操作:

  • 继承自Component的对象:通过静态容器管理指针,调用重载的assign(string name, T value)修改数据
  • Shader对象:无法继承Component,但拥有签名完全一致的assign重载,用于修改uniform变量

当前解析命令的逻辑(如判断布尔值、整数、浮点数、向量等)在两条代码路径中完全重复,新增同类命令时会产生大量冗余代码,需要一个灵活的通用方案来消除重复。

解决方案

方案1:通用模板解析函数(最简洁)

利用C++模板的鸭子类型特性,将解析逻辑封装成一个模板函数,只要对象拥有符合要求的assign重载,就能直接复用该函数。

通用解析函数实现

#include <glm/glm.hpp> // 假设使用glm向量库

// 通用解析赋值函数,支持任意拥有对应assign重载的对象
template <typename ObjType>
int parseAndAssign(ObjType* obj, const std::string& assignName, const std::string& valueStr) {
    int success = 0;

    // 布尔值判断
    if (valueStr == "true" || valueStr == "false") {
        success = obj->assign(assignName, valueStr == "true");
    }
    // 数字/向量判断
    else if (!valueStr.empty() && (isdigit(valueStr[0]) || valueStr[0] == '-')) {
        if (valueStr.find(' ') == std::string::npos) {
            // 单个数字:区分整数与浮点数
            float f = std::stof(valueStr);
            int i = std::stoi(valueStr);
            if (f != static_cast<float>(i)) {
                success = obj->assign(assignName, f);
            } else {
                success = obj->assign(assignName, i);
            }
        } else {
            // 向量解析(支持vec2/vec3/vec4)
            int count = 0;
            float num;
            std::vector<float> nums;
            std::istringstream vecIss(valueStr);
            while (vecIss >> num) {
                nums.push_back(num);
                count++;
            }
            if (count == 2) {
                success = obj->assign(assignName, glm::vec2(nums[0], nums[1]));
            } else if (count == 3) {
                success = obj->assign(assignName, glm::vec3(nums[0], nums[1], nums[2]));
            } else if (count == 4) {
                success = obj->assign(assignName, glm::vec4(nums[0], nums[1], nums[2], nums[3]));
            }
        }
    }
    // 字符串类型
    else {
        success = obj->assign(assignName, valueStr);
    }

    return success;
}

调用示例

在原有的命令分支中,直接调用通用函数即可:

int main() {
    // ... 初始化对象代码不变 ...

    if (type == "SHADER") {
        Shader *shader = Shader::shaders[name];
        parseAndAssign(shader, valueName, value);
    }
    else if (type == "COMPONENT") {
        Component *component = Component::components[name];
        parseAndAssign(component, valueName, value);
    }

    return 0;
}

优点:实现简单,无额外运行时开销,新增可赋值类型时只需保证对象有对应assign重载即可,无需修改解析逻辑。


方案2:类型擦除封装统一接口

如果需要将不同类型的赋值操作统一管理(比如存储到容器中延迟执行),可以用类型擦除封装一个通用的Assignable接口。

定义统一接口与包装器

#include <glm/glm.hpp>

class Assignable {
public:
    virtual ~Assignable() = default;
    virtual int assign(const std::string& name, bool value) = 0;
    virtual int assign(const std::string& name, int value) = 0;
    virtual int assign(const std::string& name, float value) = 0;
    virtual int assign(const std::string& name, const glm::vec2& value) = 0;
    virtual int assign(const std::string& name, const glm::vec3& value) = 0;
    virtual int assign(const std::string& name, const glm::vec4& value) = 0;
    virtual int assign(const std::string& name, const std::string& value) = 0;
};

// 针对任意对象的包装器,实现Assignable接口
template <typename T>
class AssignableWrapper : public Assignable {
public:
    AssignableWrapper(T* obj) : m_obj(obj) {}
    
    int assign(const std::string& name, bool value) override { return m_obj->assign(name, value); }
    int assign(const std::string& name, int value) override { return m_obj->assign(name, value); }
    int assign(const std::string& name, float value) override { return m_obj->assign(name, value); }
    int assign(const std::string& name, const glm::vec2& value) override { return m_obj->assign(name, value); }
    int assign(const std::string& name, const glm::vec3& value) override { return m_obj->assign(name, value); }
    int assign(const std::string& name, const glm::vec4& value) override { return m_obj->assign(name, value); }
    int assign(const std::string& name, const std::string& value) override { return m_obj->assign(name, value); }

private:
    T* m_obj;
};

通用解析函数与调用

// 接受Assignable接口指针的解析函数
int parseAndAssign(Assignable* assignable, const std::string& assignName, const std::string& valueStr) {
    // 逻辑与方案1完全一致,只需将obj->assign替换为assignable->assign
    int success = 0;

    if (valueStr == "true" || valueStr == "false") {
        success = assignable->assign(assignName, valueStr == "true");
    } else if (!valueStr.empty() && (isdigit(valueStr[0]) || valueStr[0] == '-')) {
        if (valueStr.find(' ') == std::string::npos) {
            float f = std::stof(valueStr);
            int i = std::stoi(valueStr);
            if (f != static_cast<float>(i)) {
                success = assignable->assign(assignName, f);
            } else {
                success = assignable->assign(assignName, i);
            }
        } else {
            int count = 0;
            float num;
            std::vector<float> nums;
            std::istringstream vecIss(valueStr);
            while (vecIss >> num) {
                nums.push_back(num);
                count++;
            }
            if (count == 2) {
                success = assignable->assign(assignName, glm::vec2(nums[0], nums[1]));
            } else if (count == 3) {
                success = assignable->assign(assignName, glm::vec3(nums[0], nums[1], nums[2]));
            } else if (count == 4) {
                success = assignable->assign(assignName, glm::vec4(nums[0], nums[1], nums[2], nums[3]));
            }
        }
    } else {
        success = assignable->assign(assignName, valueStr);
    }

    return success;
}

// 调用示例
int main() {
    // ... 初始化对象代码不变 ...

    if (type == "SHADER") {
        Shader *shader = Shader::shaders[name];
        AssignableWrapper<Shader> wrapper(shader);
        parseAndAssign(&wrapper, valueName, value);
    }
    else if (type == "COMPONENT") {
        Component *component = Component::components[name];
        AssignableWrapper<Component> wrapper(component);
        parseAndAssign(&wrapper, valueName, value);
    }

    return 0;
}

优点:可以统一管理不同类型的可赋值对象;缺点:新增赋值类型时需要同步更新Assignable接口,有一定维护成本。


方案3:C++20 Concepts 约束(增强健壮性)

如果使用C++20,可以用Concepts明确约束模板参数必须拥有所需的assign重载,避免编译错误时的模糊提示。

定义Concept与通用函数

#include <glm/glm.hpp>
#include <concepts>

// 定义Concept,约束对象必须拥有所有需要的assign重载
template <typename T>
concept AssignableType = requires(T* obj, const std::string& name, bool b, int i, float f, glm::vec2 v2, glm::vec3 v3, glm::vec4 v4, const std::string& s) {
    { obj->assign(name, b) } -> std::same_as<int>;
    { obj->assign(name, i) } -> std::same_as<int>;
    { obj->assign(name, f) } -> std::same_as<int>;
    { obj->assign(name, v2) } -> std::same_as<int>;
    { obj->assign(name, v3) } -> std::same_as<int>;
    { obj->assign(name, v4) } -> std::same_as<int>;
    { obj->assign(name, s) } -> std::same_as<int>;
};

// 用</think_never_used_51bce0c785ca2f68081bfa7d91973934></think_never_used_51bce0c785ca2f68081bfa7d91973934>
</think_never_used_51bce0c785ca2f68081bfa7d91973934>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
<|FunctionCallBegin|>
<|FunctionCallEnd|>
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:31:21