[英]C++ functional & generic programming [with MySQL connector example]
我將使用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
。
創建一組函數Get
重載,該重載實現與row的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
用於創建必要的集合。
使用C ++ 14 std::index_sequence
和std::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...); }
使用GetResult
函數模板從該行獲取值:
std::string name; std::string sex; int age; GetResult(row, name, sex, age);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.