From 39a95f6939417ebc5d8e716ca1664a1da1fc722c Mon Sep 17 00:00:00 2001
From: znone <glyc@sina.com.cn>
Date: Thu, 09 Feb 2017 12:04:19 +0000
Subject: [PATCH] 根据回调函数参数推断绑定的数据类型。

---
 test/TestSqlite.cpp    |   23 +-
 test/TestMysql.cpp     |   25 ++-
 include/qtl_common.hpp |  307 ++++++++++++++++++++++++++++++++++++++++---
 include/qtl_mysql.hpp  |    3 
 README.md              |   46 ++++-
 5 files changed, 343 insertions(+), 61 deletions(-)

diff --git a/README.md b/README.md
index 60e813a..e19e54c 100644
--- a/README.md
+++ b/README.md
@@ -33,18 +33,22 @@
 qtl::execute(stmt, &affected, "second_user", "third_user");
 
 ```
+或者
+```C++
+stmt<<"second_user"<<"third_user";
+
+```
 
 #### 4. 查询数据,以回调函数方式处理数据
-当回调函数返回false时,中止遍历数据。
+程序会一直遍历数据集,直到当回调函数返回false为止。如果回调函数无返回值,相当于返回true。
 
 ```C++
-db.query("select * from test where id=?",
-	id, std::tuple<uint32_t, std::string, qtl::mysql::time>(),
-	[](uint32_t id, const std::string& name, qtl::mysql::time& create_time) {
+db.query("select * from test where id=?", id, 
+	[](uint32_t id, const std::string& name, const qtl::mysql::time& create_time) {
 		printf("ID=\"%d\", Name=\"%s\"\n", id, name.data());
-		return true;
 });
 ```
+当无法根据回调函数的参数推断字段类型时,请使用query_explicit代替query,手动指定数据类型进行查询。
 
 #### 5. 也可以把数据绑定到结构上
 
@@ -72,14 +76,24 @@
 	}
 }
 
-db.query("select * from test where id=?",
-	id, TestMysqlRecord(),
-	[](TestMysqlRecord& record) {
+db.query("select * from test where id=?", id, 
+	[](const TestMysqlRecord& record) {
 		printf("ID=\"%d\", Name=\"%s\"\n", record.id, record.name);
-		return true;
 });
 ```
-#### 6. 以迭代器方式访问数据
+#### 6. 用成员函数做为查询的回调函数
+当记录类有不带参数的成员函数时,可以直接用作查询的回调函数
+```C++
+struct TestMysqlRecord
+{
+	void print();
+};
+
+db.query("select * from test where id=?", id,
+	&TestMysqlRecord::print);
+```
+
+#### 7. 以迭代器方式访问数据
 
 ```C++
 for(auto& record : db.result<TestMysqlRecord>("select * from test"))
@@ -87,6 +101,12 @@
 	printf("ID=\"%d\", Name=\"%s\"\n", record.id, record.name);
 }
 ```
+#### 8. 指示器
+可以用指示器获取查询结果的更多信息。指示器包含以下成员:
+- data 存储字段的数据
+- is_null 字段是否为空
+- length 数据的实际长度
+- is_truncated 数据是否被截断
 
 ## 有关MySQL的说明
 
@@ -116,7 +136,7 @@
 | bigint | int64_t<br/>uint64_t |
 | float | float |
 | double | double |
-| char<br>varchar | char[N]<br>std::array<char, N><br>std::string |
+| char<br>varchar | char[N]<br>std::array&lt;char, N&gt;<br>std::string |
 | blob<br>binary<br>text | qtl::blob_data<br>std::ostream |
 | date<br>time<br>datetime<br>timestamp | qtl::mysql::time |
 
@@ -152,8 +172,8 @@
 | ------- | ------ |
 | integer | int</br>int64_t |
 | real | double |
-| text | char[N]<br>std::array<char, N><br>std::string<br>std::wstring |
-| blob | qtl::const_blob_data<br>qtl::blob_data<br>ios::ostream |
+| text | char[N]<br>std::array&lt;char, N&gt;<br>std::string<br>std::wstring |
+| blob | qtl::const_blob_data<br>qtl::blob_data<br>std::ostream |
 
 当以qtl::const_blob_data接收blob数据时,直接返回SQLite给出的数据地址;当以qtl::blob_data接收blob数据时,数据被复制到qtl::blob_data指定的地址。
 
diff --git a/include/qtl_common.hpp b/include/qtl_common.hpp
index caf6c3e..e630599 100644
--- a/include/qtl_common.hpp
+++ b/include/qtl_common.hpp
@@ -14,6 +14,7 @@
 #include <type_traits>
 #include <tuple>
 #include <memory>
+#include <functional>
 #include "apply_tuple.h"
 
 namespace qtl
@@ -88,7 +89,9 @@
 	explicit indicator(data_type&& src) 
 		: is_null(false), is_truncated(false), length(0), data(std::move(src)) { }
 
+	operator bool() const { return is_null; }
 	operator data_type&() { return data; }
+	operator const data_type&() const { return data; }
 };
 
 template<typename Command, typename T>
@@ -119,15 +122,153 @@
 {
 
 template<typename F, typename T>
-auto apply(F&& f, T&& v) -> decltype(f(v))
+struct apply_impl
 {
-	return f(v);
-}
+private:
+	typedef typename std::result_of<F(T)>::type raw_result_type;
+	template<typename Ret, bool>
+	struct impl {};
+	template<typename Ret>
+	struct impl<Ret, true>
+	{
+		typedef bool result_type;
+		result_type operator()(F&& f, T&& v)
+		{
+			f(std::forward<T>(v));
+			return true;
+		}
+	};
+	template<typename Ret>
+	struct impl<Ret, false>
+	{
+		typedef Ret result_type;
+		result_type operator()(F&& f, T&& v)
+		{
+			return f(std::forward<T>(v));
+		}
+	};
+
+public:
+	typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
+	result_type operator()(F&& f, T&& v)
+	{
+		return impl<raw_result_type, std::is_void<raw_result_type>::value>()(std::forward<F>(f), std::forward<T>(v));
+	}
+};
 
 template<typename F, typename... Types>
-auto apply(F&& f, std::tuple<Types...>&& v) -> decltype(apply_tuple(f, v))
+struct apply_impl<F, std::tuple<Types...>>
 {
-	return apply_tuple(f, v);
+private:
+	typedef typename std::remove_reference<F>::type fun_type;
+	typedef std::tuple<Types...> arg_type;
+	typedef typename std::result_of<F(Types...)>::type raw_result_type;
+	template<typename Ret, bool>
+	struct impl {};
+	template<typename Ret>
+	struct impl<Ret, true>
+	{
+		typedef bool result_type;
+		result_type operator()(F&& f, arg_type&& v)
+		{
+			apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
+			return true;
+		}
+	};
+	template<typename Ret>
+	struct impl<Ret, false>
+	{
+		typedef Ret result_type;
+		result_type operator()(F&& f, arg_type&& v)
+		{
+			return apply_tuple(std::forward<F>(f), std::forward<arg_type>(v));
+		}
+	};
+
+public:
+	typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
+	result_type operator()(F&& f, arg_type&& v)
+	{
+		return impl<raw_result_type, std::is_void<raw_result_type>::value>()(std::forward<F>(f), std::forward<arg_type>(v));
+	}
+};
+
+template<typename Type, typename R>
+struct apply_impl<R (Type::*)(), Type>
+{
+private:
+	typedef R (Type::*fun_type)();
+	typedef R raw_result_type;
+	template<typename Ret, bool>
+	struct impl {};
+	template<typename Ret>
+	struct impl<Ret, true>
+	{
+		typedef bool result_type;
+		result_type operator()(fun_type f, Type&& v)
+		{
+			(v.*f)();
+			return true;
+		}
+	};
+	template<typename Ret>
+	struct impl<Ret, false>
+	{
+		typedef Ret result_type;
+		result_type operator()(fun_type f, Type&& v)
+		{
+			return (v.*f)();
+		}
+	};
+
+public:
+	typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
+	result_type operator()(R (Type::*f)(), Type&& v)
+	{
+		return impl<raw_result_type, std::is_void<raw_result_type>::value>()(f, std::forward<Type>(v));
+	}
+};
+
+template<typename Type, typename R>
+struct apply_impl<R (Type::*)() const, Type>
+{
+private:
+	typedef R (Type::*fun_type)() const;
+	typedef R raw_result_type;
+	template<typename Ret, bool>
+	struct impl {};
+	template<typename Ret>
+	struct impl<Ret, true>
+	{
+		typedef bool result_type;
+		result_type operator()(fun_type f, Type&& v)
+		{
+			(v.*f)();
+			return true;
+		}
+	};
+	template<typename Ret>
+	struct impl<Ret, false>
+	{
+		typedef Ret result_type;
+		result_type operator()(fun_type f, Type&& v)
+		{
+			return (v.*f)();
+		}
+	};
+
+public:
+	typedef typename impl<raw_result_type, std::is_void<raw_result_type>::value>::result_type result_type;
+	result_type operator()(fun_type f, Type&& v)
+	{
+		return impl<raw_result_type, std::is_void<raw_result_type>::value>()(f, std::forward<Type>(v));
+	}
+};
+
+template<typename F, typename T>
+typename apply_impl<F, T>::result_type apply(F&& f, T&& v)
+{
+	return apply_impl<F, T>()(std::forward<F>(f),  std::forward<T>(v));
 }
 
 template<typename Command, size_t N, typename... Types>
@@ -182,6 +323,72 @@
 	{
 	}
 };
+
+#define QTL_ARGS_TUPLE(Arg, Others) \
+	typename std::tuple<typename std::decay<Arg>::type, typename std::decay<Others>::type...>
+
+template<typename Ret, typename Arg>
+inline typename std::decay<Arg>::type make_values(Ret (*)(Arg))
+{
+	return typename std::decay<Arg>::type();
+};
+
+template<typename Ret, typename Arg, typename... Others>
+inline auto make_values(Ret (*)(Arg, Others...)) -> QTL_ARGS_TUPLE(Arg, Others)
+{
+	return QTL_ARGS_TUPLE(Arg, Others)();
+};
+
+template<typename Type, typename Ret>
+inline Type make_values(Ret (Type::*)())
+{
+	return Type();
+};
+template<typename Type, typename Ret>
+inline Type make_values(Ret (Type::*)() const)
+{
+	return Type();
+};
+
+template<typename Type, typename Ret, typename... Args>
+inline auto make_values(Ret (Type::*)(Args...)) -> QTL_ARGS_TUPLE(Type, Args)
+{
+	return QTL_ARGS_TUPLE(Type, Args)();
+};
+template<typename Type, typename Ret, typename... Args>
+inline auto make_values(Ret (Type::*)(Args...) const) -> QTL_ARGS_TUPLE(Type, Args)
+{
+	return QTL_ARGS_TUPLE(Type, Args)();
+};
+
+template<typename Type, typename Ret, typename Arg>
+inline typename std::decay<Arg>::type make_values_noclass(Ret (Type::*)(Arg))
+{
+	return typename std::decay<Arg>::type();
+};
+template<typename Type, typename Ret, typename Arg>
+inline typename std::decay<Arg>::type make_values_noclass(Ret (Type::*)(Arg) const)
+{
+	return typename std::decay<Arg>::type();
+};
+
+template<typename Type, typename Ret, typename Arg, typename... Others>
+inline auto make_values_noclass(Ret (Type::*)(Arg, Others...)) -> QTL_ARGS_TUPLE(Arg, Others)
+{
+	return QTL_ARGS_TUPLE(Arg, Others)();
+};
+template<typename Type, typename Ret, typename Arg, typename... Others>
+inline auto make_values_noclass(Ret (Type::*)(Arg, Others...) const) -> QTL_ARGS_TUPLE(Arg, Others)
+{
+	return QTL_ARGS_TUPLE(Arg, Others)();
+};
+
+template<typename Functor, typename=typename std::enable_if<std::is_member_function_pointer<decltype(&Functor::operator())>::value>::type>
+inline auto make_values(const Functor&) 
+-> decltype(make_values_noclass(&Functor::operator()))
+{
+	return make_values_noclass(&Functor::operator());
+}
 
 }
 
@@ -452,88 +659,137 @@
 	}
 
 	template<typename Params, typename Values, typename ValueProc>
-	void query(const char* query_text, size_t text_length, const Params& params, Values&& values, ValueProc proc)
+	void query_explicit(const char* query_text, size_t text_length, const Params& params, Values&& values, ValueProc&& proc)
 	{
 		T* pThis=static_cast<T*>(this);
 		Command command=pThis->open_command(query_text, text_length);
 		command.execute(params);
 		while(command.fetch(std::forward<Values>(values)))
 		{
-			if(!detail::apply(proc, std::forward<Values>(values))) break;
+			if(!detail::apply(std::forward<ValueProc>(proc), std::forward<Values>(values))) break;
 		}
 		command.close();
 	}
 
 	template<typename Params, typename Values, typename ValueProc>
-	void query(const char* query_text, const Params& params, Values&& values, ValueProc proc)
+	void query_explicit(const char* query_text, const Params& params, Values&& values, ValueProc&& proc)
 	{
-		query(query_text, strlen(query_text), params, std::forward<Values>(values), proc);
+		query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), std::forward<ValueProc>(proc));
 	}
 	template<typename Params, typename Values, typename ValueProc>
-	void query(const std::string& query_text, const Params& params, Values&& values, ValueProc proc)
+	void query_explicit(const std::string& query_text, const Params& params, Values&& values, ValueProc&& proc)
 	{
-		query(query_text.data(), query_text.size(), params, std::forward<Values>(values), proc);
+		query_explicit(query_text.data(), query_text.size(), params, std::forward<Values>(values), std::forward<ValueProc>(proc));
 	}
 	template<typename Values, typename ValueProc>
-	void query(const char* query_text, size_t text_length, Values&& values, ValueProc proc)
+	void query_explicit(const char* query_text, size_t text_length, Values&& values, ValueProc&& proc)
 	{
-		query(query_text, text_length, std::make_tuple(), std::forward<Values>(values), proc);
+		query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
 	}
 	template<typename Values, typename ValueProc>
-	void query(const char* query_text, Values&& values, ValueProc proc)
+	void query_explicit(const char* query_text, Values&& values, ValueProc&& proc)
 	{
-		query(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), proc);
+		query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
 	}
 	template<typename Values, typename ValueProc>
-	void query(const std::string& query_text, Values&& values, ValueProc proc)
+	void query_explicit(const std::string& query_text, Values&& values, ValueProc&& proc)
 	{
-		query(query_text, std::make_tuple(), std::forward<Values>(values), proc);
+		query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), std::forward<ValueProc>(proc));
+	}
+
+	template<typename Params, typename ValueProc>
+	void query(const char* query_text, size_t text_length, const Params& params, ValueProc&& proc)
+	{
+		query_explicit(query_text, text_length, params, detail::make_values(proc),  std::forward<ValueProc>(proc));
+	}
+	template<typename Params, typename ValueProc>
+	void query(const char* query_text, const Params& params, ValueProc&& proc)
+	{
+		query_explicit(query_text, params, detail::make_values(proc),  std::forward<ValueProc>(proc));
+	}
+	template<typename Params, typename ValueProc>
+	void query(const std::string& query_text, const Params& params, ValueProc&& proc)
+	{
+		query_explicit(query_text, params, detail::make_values(proc),  std::forward<ValueProc>(proc));
+	}
+	template<typename ValueProc>
+	void query(const char* query_text, size_t text_length, ValueProc&& proc)
+	{
+		query_explicit(query_text, text_length, detail::make_values(proc),  std::forward<ValueProc>(proc));
+	}
+	template<typename ValueProc>
+	void query(const char* query_text, ValueProc&& proc)
+	{
+		query_explicit(query_text, detail::make_values(proc),  std::forward<ValueProc>(proc));
+	}
+	template<typename ValueProc>
+	void query(const std::string& query_text, ValueProc&& proc)
+	{
+		query_explicit(query_text, detail::make_values(proc), std::forward<ValueProc>(proc));
 	}
 
 	template<typename Params, typename Values>
 	void query_first(const char* query_text, size_t text_length, const Params& params, Values&& values)
 	{
-		query(query_text, text_length, params, std::forward<Values>(values), first_record());
+		query_explicit(query_text, text_length, params, std::forward<Values>(values), first_record());
 	}
 
 	template<typename Params, typename Values>
 	void query_first(const char* query_text, const Params& params, Values&& values)
 	{
-		query(query_text, strlen(query_text), params, std::forward<Values>(values), first_record());
+		query_explicit(query_text, strlen(query_text), params, std::forward<Values>(values), first_record());
 	}
 
 	template<typename Params, typename Values>
 	void query_first(const std::string& query_text, const Params& params, Values&& values)
 	{
-		query(query_text, params, values, first_record());
+		query_explicit(query_text, params, values, first_record());
 	}
 
 	template<typename Values>
 	void query_first(const char* query_text, size_t text_length, Values&& values)
 	{
-		query(query_text, text_length, std::make_tuple(), std::forward<Values>(values), first_record());
+		query_explicit(query_text, text_length, std::make_tuple(), std::forward<Values>(values), first_record());
 	}
 
 	template<typename Values>
 	void query_first(const char* query_text, Values&& values)
 	{
-		query(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), first_record());
+		query_explicit(query_text, strlen(query_text), std::make_tuple(), std::forward<Values>(values), first_record());
 	}
 
 	template<typename Values>
 	void query_first(const std::string& query_text, Values&& values)
 	{
-		query(query_text, std::make_tuple(), std::forward<Values>(values), first_record());
+		query_explicit(query_text, std::make_tuple(), std::forward<Values>(values), first_record());
+	}
+
+	template<typename... Values>
+	void query_first_direct(const char* query_text, size_t text_length, Values&... values)
+	{
+		query_first(query_text, text_length, std::tie(values...));
+	}
+
+	template<typename... Values>
+	void query_first_direct(const char* query_text, Values&... values)
+	{
+		query_first(query_text, std::tie(values...));
+	}
+
+	template<typename... Values>
+	void query_first_direct(const std::string& query_text, Values&... values)
+	{
+		query_first(query_text, std::tie(values...));
 	}
 
 protected:
 	struct nothing 
 	{
-		template<typename... Values> bool operator()(Values&...) const {  return true; }
+		template<typename... Values> bool operator()(Values&&...) const {  return true; }
 	};
 	struct first_record 
 	{
-		template<typename... Values> bool operator()(Values&...) const {  return false; }
+		template<typename... Values> bool operator()(Values&&...) const {  return false; }
 	};
 };
 
@@ -593,6 +849,5 @@
 }
 
 }
-
 
 #endif //_MYDTL_DATABASE_H_
diff --git a/include/qtl_mysql.hpp b/include/qtl_mysql.hpp
index 1c9cf5d..60c552b 100644
--- a/include/qtl_mysql.hpp
+++ b/include/qtl_mysql.hpp
@@ -563,12 +563,13 @@
 	bool open(const char *host, const char *user, const char *passwd, const char *db,
 		unsigned long clientflag=0, unsigned int port=0, const char *unix_socket=NULL)
 	{
+		if(m_mysql==NULL) m_mysql=mysql_init(NULL);
 		return mysql_real_connect(m_mysql, host, user, passwd, db, port, unix_socket, clientflag)!=NULL;
 	}
 	void close()
 	{
 		mysql_close(m_mysql);
-		m_mysql=mysql_init(NULL);
+		m_mysql=NULL;
 	}
 	void refresh(unsigned int options)
 	{
diff --git a/test/TestMysql.cpp b/test/TestMysql.cpp
index 68dcb6f..53330c1 100644
--- a/test/TestMysql.cpp
+++ b/test/TestMysql.cpp
@@ -17,6 +17,12 @@
 	{
 		memset(this, 0, sizeof(TestMysqlRecord));
 	}
+
+	void print() const
+	{
+		printf("ID=\"%d\", Name=\"%s\"\n",
+			id, name);
+	}
 };
 
 namespace qtl
@@ -57,11 +63,9 @@
 	try
 	{
 		db.query("select 0, 'hello world' from dual",
-			std::tuple<uint32_t, std::string>(),
 			[](uint32_t i, const std::string& str) {
 				printf("0=\"%d\", 'hello world'=\"%s\"\n",
 					i, str.data());
-				return true;
 		});
 	}
 	catch(qtl::mysql::error& e)
@@ -77,21 +81,20 @@
 
 	try
 	{
-		db.query("select * from test where id=?", 0,
-			id, std::tuple<uint32_t, std::string, qtl::mysql::time>(),
-			[](uint32_t id, const std::string& name, qtl::mysql::time& create_time) {
+		db.query("select * from test where id=?", id, 
+			[](const qtl::indicator<uint32_t>& id, const std::string& name, const qtl::mysql::time& create_time) {
 				printf("ID=\"%d\", Name=\"%s\"\n",
-					id, name.data());
-				return true;
+					id.data, name.data());
 		});
 
-		db.query("select * from test where id=?", 0,
-			id, TestMysqlRecord(),
-			[](TestMysqlRecord& record) {
+		db.query("select * from test where id=?", id,
+			[](const TestMysqlRecord& record) {
 				printf("ID=\"%d\", Name=\"%s\"\n",
 					record.id, record.name);
-				return true;
 		});
+
+		db.query("select * from test where id=?", id, 
+			&TestMysqlRecord::print);
 	}
 	catch(qtl::mysql::error& e)
 	{
diff --git a/test/TestSqlite.cpp b/test/TestSqlite.cpp
index 04417ba..7ac4ad6 100644
--- a/test/TestSqlite.cpp
+++ b/test/TestSqlite.cpp
@@ -19,6 +19,12 @@
 	{
 		memset(this, 0, sizeof(TestSqliteRecord));
 	}
+
+	void print() const
+	{
+		printf("ID=\"%d\", Name=\"%s\"\n",
+			id, name);
+	}
 };
 
 namespace qtl
@@ -66,11 +72,9 @@
 	try
 	{
 		db.query("select 0, 'hello world'", 
-			std::tuple<int32_t, std::string>(),
 			[](int32_t i, const std::string& str) {
 				printf("0=\"%d\", 'hello world'=\"%s\"\n",
 					i, str.data());
-				return true;
 		});
 	}
 	catch(qtl::sqlite::error& e)
@@ -153,22 +157,21 @@
 
 	try
 	{
-		db.query("select * from test where id=?",
-			id, std::tuple<int, std::string, std::string>(),
-			[](int id, const std::string& name, std::string& create_time) {
+		db.query("select * from test where id=?", id, 
+			[](int id, const std::string& name, const std::string& create_time) {
 				printf("ID=\"%d\", Name=\"%s\", CrateTime=\"%s\"\n",
 					id, name.data(), create_time.data());
-				return true;
 		});
 
-		db.query("select ID, Name, strftime('%s', CreateTime) from test where id=?",
-			id, TestSqliteRecord(),
-			[](TestSqliteRecord& record) {
+		db.query("select ID, Name, strftime('%s', CreateTime) from test where id=?", id, 
+			[](const TestSqliteRecord& record) {
 				time_t t=record.create_time;
 				printf("ID=\"%d\", Name=\"%s\", CrateTime=\"%s\"\n",
 					record.id, record.name, asctime(localtime(&t)));
-				return true;
 		});
+
+		db.query("select * from test where id=?", id,
+			&TestSqliteRecord::print);
 	}
 	catch(qtl::sqlite::error& e)
 	{

--
Gitblit v1.9.3