SVG属性类设计 (3)
需求
在SVG属性类设计(1)中,定义了基本SVG属性类,提供了泛型的实现方式。接下来我们需要让SVG属性能进一步适配枚举型属性,例如"a|b"的类型要求。(具体的例子:rect元素中的cx
,其合法属性为"<number>|<length>"
)。由于其性质和元组一致,称之为属性元组。
属性枚举的特点是包含多个不同的类型,且选取第一个非空合法类型值作为最终类型。
设计
属性枚举既有容器的性质又是属性,因此要包含泛型组件中的所有操作。
-
底层容器
仍然使用泛型容器std::tuple
-
构造
构造时遍历内部维护的
_tuple
,依次设置getter和setter为。 -
提交与取值
属性枚举虽然是容器,但其中的所有属性类型不能独立存在。提交时并不需要将各属性依次提交,只需获取缓冲区。
遍历所有内部属性的提交区,最终的取值是第一个获取的非空值。
-
赋值、绑定
对所有内部属性分别操作即可。
细节
对于模板、参数展开等问题均在上一篇中说明,此处不再赘述。
-
函数静态类型转换
绑定函数时发现存在静态转换问题,即:
std::function<T()>
与
(T*)()
以及
[](){ return T(); }
在泛型下无法进行直接推导。
最终考虑分开处理,对函数指针独立写一个新的泛型成员函数。但是lambda函数并没有好的解决方法。
-
Lambda函数的静态类型转换
-
尝试一:static_cast
手动进行静态类型转换,但是没法推导Lamdba函数的类型。了解到Lambda函数类型是编译器推导的一个函数对象(Functor),并不能直接获取
-
尝试二:decltype
尝试用在static_cast基础上使用decltype对Lambda函数类型进行推导。随后发现C++11中不支持,高版本能支持。
-
最终解决方法:完全泛型+显式转换
考虑重载一个泛型成员函数接受所有类型,然后尝试对该类型进行显式转换为std::function。
具体的显式转换如下:
namespace detail { template <typename F> struct function_traits : public function_traits<decltype(&F::operator())> {}; template <typename R, typename C, typename... Args> struct function_traits<R (C::*)(Args...) const> { using function_type = std::function<R (Args...)>; }; } namespace Lewzen { template <typename F> using function_type_t = typename detail::function_traits<F>::function_type; template <typename F> function_type_t<F> lambda_to_function(F & lambda) { return static_cast<function_type_t<F>>(lambda); } }
先通过模板元编程,结合Lambda函数作为Functor的特性,利用其括号运算符重载,获取返回类型R、参数列表Args…,Lambda的原始类型C,进而协助进行静态类型转换为std::function。
-
-
返回只读类型的std::function作为参数的泛型函数重载问题
测试中发现Lambda函数的返回类型并非只读,因此无法有限匹配到返回值只读作为参数的重载,造成递归无限循环。但理论上是能直接静态类型转换的。
解决方法:另外创建一个函数,与返回值只读作为参数的重载参数列表相同,名称不同。强制Lambda对应的重载调用该函数。