मैं एक लाइब्रेरी विकसित कर रहा हूं जो गैर टाइप किए गए सी फ़ंक्शंस (SQLite) से संबंधित है और मैं इसे मजबूत टाइप करना चाहता हूं।कटौती का उपयोग कर सी ++ वैरिएडिक विस्तार
विचार FieldDef
मजबूत प्रकार है जो उपयोगकर्ता को कमजोर डीबी प्रकारों के लिए int, double और std :: string जैसे कच्चे प्रकारों को जोड़ने की अनुमति देता है। मेरी समस्या यह है कि लाइब्रेरी का अर्थात् बहुत भारी है, और मैं कुछ स्वचालित प्रकार की कटौती जोड़ना चाहता हूं।
namespace FieldType {
struct Integer { using rawtype = int; };
struct Real{ using rawtype = double; };
struct Text{ using rawtype = std::string; };
struct Blob{ using rawtype = std::vector<uint8_t>; };
}
मैं भी एक insert
एक और query
कार्यों कि डालने और SQL कथन का उपयोग किए बिना तालिकाओं से क्वेरी की अनुमति है:
तो मैं "बुनियादी प्रकार" का एक समूह है। क्वेरी सादा चयन होगा। वैसे भी। इच्छित उपयोग है:
FieldDef<FieldType::Integer> mId = makeFieldDef("id", FieldType::Integer()).primaryKey().autoincrement();
FieldDef<FieldType::Text> mName = makeFieldDef("name", FieldType::Text());
FieldDef<FieldType::Integer> mValue = makeFieldDef("value", FieldType::Integer());
SQLiteTable::insert(std::make_tuple(mName, mValue), std::make_tuple(record.name, record.value));
std::vector<Record> r;
SQLiteTable::query
(std::make_tuple(mName, mValue), [&r](std::tuple<std::string, int> res) {
r.push_back(Record{std::get<0>(res), std::get<1>(res)});
});
मैं डालने इस तरह से लागू किया: जब यह बुला संकलक असमर्थ है
template <typename ...Ts, typename ...Us>
void query(std::tuple<Ts...> def, std::function<void(std::tuple<Us...>)> resultFeedbackFunc) {
...
}
:
template <typename ...Ts, typename ...Us>
bool insert (std::tuple<Ts...> def, std::tuple<Us...> values) {
std::ostringstream ss;
ss << "INSERT INTO " << mName << "("
<< buildSqlInsertFieldList<0>(def)
<< ") VALUES ("
<< buildSqlInsertValuesListPlaceholder<0>(values)
<< ");";
auto stmt = newStatement(ss.str());
bindAllValues<0>(stmt.get(), values);
return execute(stmt.get());
}
यह ठीक काम करता है, समस्याओं क्वेरी के साथ आते हैं प्रकारों को सही ढंग से कम करने के लिए, इसलिए मुझे लगता है कि इसे एक पैडेंटिक निर्माण की आवश्यकता है:
SQLiteTable::query<FieldType::Text, FieldType::Integer, /* whatever */> (...)
यह अनैतिक और वर्बोज़ है।
यह क्वेरी समारोह को आसान बनाने के लिए संभव होगा? चूंकि हमारे पास उपयोग में बाधा है, इसलिए
Us
पैकFieldType::*:rawtype
के साथ कुछ प्रकार के अनुकूल हो सकता है, मैं पूछ रहा हूं कि कुछ ऐसे निर्माण का उपयोग करना संभव होगा जो अनपॅक और विधि लागू करें।template<typename Ts...> bool insert (std::tuple<Ts...> def, std::tuple<Ts::rawtype ...> values)
के बजाय का उपयोग कर tuples, क्या variadic पैक का उपयोग कर के बारे में:
insert
के मामले में, यह की तरह कुछ के साथ सरल किया जा सकता है? मैं यह परीक्षण नहीं किया है, लेकिन मुझे डर है की तरहtemplate<typename Ts..., typename Us....> bool insert (Ts... def, Us ... values)
कुछ का उपयोग कर संकलक भ्रमित हैं और चीजों को बदतर बना होगा। आपको क्या लगता है?
- यदि क्वेरी के वास्तविक कार्यान्वयन का उपयोग करना संभव है, तो उपयोग कोड को अधिक अभिव्यक्तिपूर्ण बनाने के लिए एक कार्यवाही क्या होगी?
यहाँ कोड के बारे में कुछ विवरण हैं, समझाने के लिए:
क्वेरी समारोह निम्नलिखित स्यूडोकोड का उपयोग कर कार्यान्वित किया जाता है:
template <typename ...Ts, typename ...Us>
void query(std::tuple<Ts...> def, std::function<void(std::tuple<Us...>)> resultFeedbackFunc) {
std::ostringstream ss;
ss << "SELECT " << buildSqlInsertFieldList<0>(def) << " FROM " << mName <<";";
auto stmt = newStatement(ss.str());
auto r = execute(stmt.get());
SQLiteException::throwIfNotOk(r, db()->handle());
while (hasData(stmt.get())) {
auto nColumns = columnCount(stmt.get());
if (nColumns != sizeof...(Ts))
throw std::runtime_error("Column count differs from data size");
std::tuple<Us...> res;
getAllValues<0>(stmt.get(), res);
resultFeedbackFunc(res);
}
};
Statement
एक अपारदर्शी प्रकार है कि sqlite
छुपाता है कथन संरचना, query
विधि newStatement
, execute
और columnsCount
में उपयोग किए जाने वाले अन्य कार्यों के रूप में। फंक्शन getAllValues
tuple
भरने के लिए रिकर्सन का उपयोग करता है। इसलिए डेटाबेस की प्रत्येक पंक्ति के लिए मजेदार resultFeedbackFunc()
कहा जाएगा। तो क्लाइंट कोड, उदाहरण के लिए, एक कंटेनर (एक वेक्टर की तरह) भर सकते हैं।
अद्यतन:
मैं @ bolov के समाधान पीछा किया, और @ मासीमिलियानो-जोन्स के सुधार जोड़ा।
यह प्रतिक्रिया कार्य करने के लिए आंतरिक कॉल का सही कार्यान्वयन है:
resultFeedbackFunc(getValueR<decltype (std::get<Is>(def).rawType())>
(stmt.get(), Is)...);
getValueR
sqlite_column_xxx(sqlite3_stmt *, int index)
करने के लिए आंतरिक कॉल करता है। अगर मैं सही ढंग से समझता हूं, तो अनपॅकिंग काम करता है क्योंकि तर्क सूची अनपॅकिंग के लिए एक वैध संदर्भ है। अगर मैं तर्कों के बाहर कॉल करना चाहता था, तो मुझे एक फोल्डिंग (या कामकाज करना था क्योंकि मैं सी ++ 11 का उपयोग कर रहा हूं)।
हो, परियोजना यहाँ प्रकाशित किया जाता है: https://github.com/studiofuga/mSqliteCpp – HappyCactus