Заменить строку легко, но выполнить что-то, что безопасно для типов и будет хорошо преобразовывать 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 здесь, поэтому я собираюсь использовать лямбды. Для каждого типа, который мы регистрируем, мы зарегистрируем функцию, которая принимает строку и возвращает ее в формате, подходящем для 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);
РЕДАКТИРОВАТЬ: исправлены некоторые ошибки.