C++函数式和泛型编程[使用MySQL连接器示例]

C++ functional & generic programming [with MySQL connector example]

本文关键字:连接器 MySQL 使用 函数 泛型编程 C++      更新时间:2023-10-16

我将使用MySQL连接器。它们提供了访问结果行的函数。例如getString(1), getInt(1), getDate(2)。括号内的数字是结果的索引值。

所以我必须使用以下代码来访问这个示例行:'John', 'M', 34

string name = row.getString(1);
string sex = row.getString(2);
int age = row.getInt(3);
我想尝试泛型编程的各种原因(主要是为了好玩)。但很令人失望的是,即使花了很多时间,我也不能让它发生。

我想要的最终结果:

std::tie<name, sex, age> = row.getResult<string, string, int>();

这个函数应该调用相应的MySQL API。

也很高兴看到类似下面的答案,尽管语法是错误的。

std::tie<name, sex, age> = row.getResult([string, string, int]);

请不要建议使用for循环。让我们尝试一些更通用的&功能;-)

这个适合我:

struct Row
{
   template <int N, typename ... Args> struct Helper;
   template <typename Arg1> struct Helper<1, Arg1>
   {
      static std::tuple<Arg1> getResult(Row& r)
      {
         return std::make_tuple(r.getResult<Arg1>(0));
      }
   };
   template <int N, typename Arg1, typename ... Args>
      struct Helper<N, Arg1, Args...>
      {
         static std::tuple <Arg1, Args ...> getResult(Row& r)
         {
            return std::tuple_cat(std::make_tuple(r.getResult<Arg1>(N-1)),
                                  Helper<N-1, Args...>::getResult(r));
         }
      };
   template <typename Arg> Arg getResult(int index)
   {
      // This is where the value needs to be extracted from the row.
      // It is a dummy implementation for testing purposes.
      return Arg{};
   }
   template <typename ... Args>
      std::tuple <Args ...> getResult()
      {
         return Helper<sizeof...(Args), Args...>::getResult(*this);
      }
};

使用例子:

Row r;
auto res1 = r.getResult<std::string>();
auto res2 = r.getResult<int>();
auto res3 = r.getResult<int, double, int>();
auto res4 = r.getResult<int, int, double, double>();
auto res5 = r.getResult<std::string, std::string, int, int, double, double>();

工作代码:http://ideone.com/6IpJ8q

首先你需要写这些:

template<class T> T get( MySQLRow const& row, unsigned index);
template<>
int get<int>( MySQLRow const& row, unsigned index) { return connector.GetInt(index); }
// etc

然后,添加一些模板元编程工具:

template<class...>struct types{using type=types;};
上面的

可以用std::tuple<?>*代替types<?>。也许吧。但无论如何,以上内容更清晰。

接下来,这可以用c++ 14的integral_sequence:

代替
template<unsigned...>struct indexes{using type=indexes;};
template<unsigned max, unsigned...is>struct make_indexes<max-1, max-1, is...>{};
template<unsigned...is>struct make_indexes<0,is...>:indexes<is...>{};
template<unsigned max>using make_indexes_t=typename make_indexes<max>::type;

最后,东西:

namespace details {
  template<unsigned... Is, class... Types>
  std::tuple< Types... >
  getResult( indexes<Is...>, types<Types...>, MySQLRow const& row ) {
    return { get<Types>( row, Is+1 )... };
  }
}
template<class... Types>
std::tuple<Types...>
getResult( MySQLRow const& row ) {
  return details::getResult( make_indexes_t<sizeof...(Ts)>{}, types<Types...>{}, row );
}

的语法是:

getResult<int, double, std::string>( row );

假设您编写了各种get函数,并将MySQLRow的类型固定为实际类型,并假设第一行是1

  1. 创建Get重载函数集,实现与GetX方法的统一接口。

    #define DEFINE_GET_FOR_TYPE(Type, TypeName) 
    void Get(Type& arg, const MySQLRow& row, std::size_t index) 
    { 
        arg = row.Get##TypeName(index); 
    }
    DEFINE_GET_FOR_TYPE(int, Int)
    DEFINE_GET_FOR_TYPE(std::string, String)
    // ...
    

    DEFINE_GET_FOR_TYPE用于创建必要的集合。

  2. 使用c++ 14 std::index_sequencestd::make_index_sequence实现GetResult函数模板(它们也可以在c++ 11程序中实现):

    struct Iterate
    {
        Iterate(...) {}
    };
    template <std::size_t... indices, typename... Types>
    void GetResultImpl(const MySQLRow& row, std::index_sequence<indices...>, Types&... args)
    {
        Iterate{(Get(args, row, indices + 1), 0)...};
    }
    template <typename... Types>
    void GetResult(const MySQLRow& row, Types&... args)
    {
        GetResultImpl(row, std::make_index_sequence<sizeof...(Types)>(), args...);
    }
    
  3. 使用GetResult函数模板从行获取值:

    std::string name;
    std::string sex;
    int age;
    GetResult(row, name, sex, age);
    

现场演示