替换字符串很容易,但执行一些类型安全且能很好地转换 SQL 的操作则略有不同。我们需要一个绑定器类,它可以通过传递的类型绑定并进行任何必要的转换。
首先,我们需要将std::type_info 包装起来,以便可以在哈希映射中使用:
class typeInfoWrapper
{
friend bool operator == (const typeInfoWrapper& l, const typeInfoWrapper& r);
private:
const std::type_info& typeInfo_;
public:
typeInfoWrapper(const std::type_info& info) : typeInfo_(info) { };
// hasher
class hash
{
public:
size_t operator()(const typeInfoWrapper& typeInfo) const
{
return typeInfo.typeInfo_.hash_code();
};
}; // eo class hash
}; // eo class typeInfoWrapper
bool operator == (const typeInfoWrapper& l, const typeInfoWrapper& r)
{
return l.typeInfo_.hash_code() == r.typeInfo_.hash_code();
} // eo operator ==
接下来,我们需要类本身。我在这里使用 C++11,所以我将使用 lambdas。对于我们注册的每种类型,我们将注册一个函数,该函数接受一个字符串并以适合 SQL 的格式返回它。在此示例中,我为字符串注册了一个,为 int 注册了一个。字符串 one 只是将 ' 替换为 '' 并用引号本身返回它。 int 只返回自身,无需解析 SQL。
class binder
{
private:
typedef std::function<std::string(std::string&)> ReplaceFunc;
typedef std::tr1::unordered_map<typeInfoWrapper, ReplaceFunc, typeInfoWrapper::hash> ReplaceMap;
typedef std::pair<typeInfoWrapper, ReplaceFunc> ReplacePair;
ReplaceMap typeMap_;
public:
binder()
{
// add string and int for test purposes
typeMap_.insert(ReplacePair(typeid(const char*), [](std::string& data) -> std::string
{
// escape the "'" to prevent SQL injection
boost::replace_all(data, "'", "''");
return "'" + data + "'";
}));
typeMap_.insert(ReplacePair(typeid(int), [](std::string& data) -> std::string
{
// for sql, this is easy, just return the value as is
return data;
}));
};
// func
template<class T>
void bind(std::string& input, const std::string& expr, T data)
{
ReplaceMap::const_iterator cit(typeMap_.find(typeid(T)));
if(cit != typeMap_.end())
boost::replace_all(input, expr, cit->second(boost::lexical_cast<std::string>(data)));
}; // eo bind
}; // eo class bind
如你所见,我们有绑定函数。
现在我们可以以类型安全的方式进行绑定了!
binder b;
std::string data = "SELECT * FROM table WHERE _user = :user AND _id = :id";
b.bind(data, ":user", "Moo-Juice");
b.bind(data, ":id", 32);
编辑:修复了一些错误。