【问题标题】:C++ how to create 'bind' string text replacement method?C ++如何创建'bind'字符串文本替换方法?
【发布时间】:2024-04-22 13:30:02
【问题描述】:

所以我看sqlite3cpp wiki。他们有这么好的 API:

sqlite3pp::command cmd(db, "INSERT INTO contacts (name, phone) VALUES (:user, :phone)");
cmd.bind(":user", "Mike");
cmd.bind(":phone", "555-1234");
cmd.execute();

我想知道如何使用 boost 创建类似于常规 std::string 的 API?意思是

std::string str = "INSERT INTO contacts (name, phone) VALUES (:user, :phone)";
bind(str, ":user", "Mike");
bind(str, ":phone", "555-1234");

是否有可能用 boost 来创建这样的东西以及如何做到这一点?

【问题讨论】:

    标签: c++ string boost replace


    【解决方案1】:

    可能:boost::algorithm::replace_all?或者boost::algorithm::replace_all_copy,如果您不想修改原始字符串。

    【讨论】:

      【解决方案2】:

      替换字符串很容易,但执行一些类型安全且能很好地转换 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);
      

      编辑:修复了一些错误。

      【讨论】: