本文是对上篇 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 支持 intfloatbooldouble 四种类型,并通过隐式转换运算符对外提供统一调用入口。





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

在实际使用场景中,如果属性参数类型与映射函数的参数类型始终一致(即不存在 intfloat 这类隐式转换需求),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;}
};