本文是对上篇 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;}
                };