如何将不同类的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|>
相关产品推荐
相关产品推荐

