本文是对上篇 Lite2D UI编辑器2 属性解析部分的优化迭代,核心思路是利用 map 将函数名与属性名关联起来,从而实现自动映射。
通过宏来完成映射关系的插入,当然完整实现还需要针对不同参数类型分别提供对应的宏。如果参数是对象类型,则需要额外处理。此外,也可以用静态数组配合宏来加快查找速度,因为函数地址本身是静态的。
迭代1
第一版使用 std::unordered_map 存储属性名到函数的映射,宏 REGISTER_MAP_ATTR_FLOAT 负责插入绑定关系,但只支持 float 参数类型。
static std::unordered_map<string, std::function<void(float )>> _attrs;
#define REGISTER_MAP_ATTR_FLOAT(MAP,KEY,FUNC,TARGET)\
MAP.insert(std::make_pair(string(KEY), std::bind(&FUNC,TARGET,std::placeholders::_1)));
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
Layer::init();
Sprite *s = Sprite::create("1.png");
REGISTER_MAP_ATTR_FLOAT(_attrs, "x", Node::setPositionX, s);
REGISTER_MAP_ATTR_FLOAT(_attrs, "y", Node::setPositionY, s);
std::map<string, float> _at;
_at.insert(std::make_pair( "x", 100.0f));
_at.insert(std::make_pair("y", 100.0f));
for (auto it = _attrs.begin(); it != _attrs.end(); ++it)
{
auto key = (*it).first;
(*it).second(_at[key]);
}
this->addChild(s);
return true;
}
迭代2
第二版将参数模板化,通过引入 Wapper 包装类实现统一接口,使数据类型与绑定函数的参数类型解耦,真正实现自动映射。Wapper 支持 int、float、bool、double 四种类型,并通过隐式转换运算符对外提供统一调用入口。
class Wapper
{
public:
Wapper(){}
Wapper(int arg)
{
this->i = arg;
this->_type = TYPE::INT;
}
Wapper(float arg)
{
this->f = arg;
this->_type = TYPE::FLOAT;
}
Wapper(bool arg)
{
this->b = arg;
this->_type = TYPE::BOOL;
}
Wapper(double arg)
{
this->d = arg;
this->_type = TYPE::DOUBLE;
}
int toInt()
{
if (this->_type == TYPE::INT)
{
return this->i;
}
if (this->_type == TYPE::FLOAT)
{
return (int)(this->f);
}
if (this->_type == TYPE::DOUBLE)
{
return (int)(this->d);
}
if (this->_type == TYPE::BOOL)
{
return this->b == 0 ? false : true;
}
}
float toFloat()
{
if (this->_type == TYPE::INT)
{
return (float)this->i;
}
if (this->_type == TYPE::FLOAT)
{
return (this->f);
}
if (this->_type == TYPE::DOUBLE)
{
return (float)(this->d);
}
if (this->_type == TYPE::BOOL)
{
return this->b == false ? 0.0f : 1.0f;
}
}
double toDouble()
{
if (this->_type == TYPE::INT)
{
return (double)this->i;
}
if (this->_type == TYPE::FLOAT)
{
return (double)this->f;
}
if (this->_type == TYPE::DOUBLE)
{
return this->d;
}
if (this->_type == TYPE::BOOL)
{
return this->b == false ? 0.0 : 1.0;
}
}
bool toBool()
{
if (this->_type == TYPE::INT)
{
return this->i == 0 ? false : true;;
}
if (this->_type == TYPE::FLOAT)
{
return this->f < 0.0000000001 ? false : true;;
}
if (this->_type == TYPE::DOUBLE)
{
return this->d < 0.0000000001 ? false : true;;
}
if (this->_type == TYPE::BOOL)
{
return this->b;
}
}
union
{
int i = 0;
float f;
bool b;
double d;
};
enum class TYPE
{
INT,
FLOAT,
DOUBLE,
BOOL
};
operator int() { return this->toInt(); }
operator float() { return this->toFloat(); }
operator bool() { return this->toBool(); }
operator double() { return this->toDouble(); }
private:
TYPE _type;
};
#define REGISTER_MAP_ATTR(MAP,KEY,FUNC,TARGET) \
MAP.insert(std::make_pair(string(KEY), \
std::bind(&FUNC, TARGET, std::placeholders::_1)));
static std::unordered_map<string, std::function<void(Wapper)>> _attrsAll;
template<class TT, class T >
void invoke(TT & func, T arg)
{
func((T)arg);
}
bool HelloWorld::init()
{
Layer::init();
Sprite *s = Sprite::create("1.png");
//注册属性 和对应的函数映射
REGISTER_MAP_ATTR(_attrsAll, "x", Node::setPositionX, s);
REGISTER_MAP_ATTR(_attrsAll, "y", Node::setPositionY, s);
REGISTER_MAP_ATTR(_attrsAll, "flipy", Sprite::setFlippedY, s);
// 添加属性描述
std::map<string, Wapper> _at;
_at.insert(std::make_pair("x", 300.0f));
_at.insert(std::make_pair("y", 100.0f));
_at.insert(std::make_pair("flipy", true));
//遍历解析属性
for (auto it = _attrsAll.begin(); it != _attrsAll.end(); ++it)
{
auto key = (*it).first;
auto value = (*it).second;
invoke((*it).second, _at[key]);
}
this->addChild(s);
return true;
}
简化版 Wapper
在实际使用场景中,如果属性参数类型与映射函数的参数类型始终一致(即不存在 int 转 float 这类隐式转换需求),Wapper 可以进一步简化,去掉类型标记和转换方法:
class Wapper
{
public:
Wapper(){}
Wapper(int arg)
{
this->i = arg;
}
Wapper(float arg)
{
this->f = arg;
}
Wapper(bool arg)
{
this->b = arg;
}
union
{
int i;
float f;
bool b;
};
operator int() const{return i;}
operator float() const{return f;}
operator bool() const{return b;}
};