我正在开发一个处理非类型化 C 函数 (SQLite) 的库,我想对其进行强类型化处理。
想法是拥有一个 FieldDef 强类型,允许用户将原始类型(如 int、double 和 std::string)绑定(bind)到弱数据库类型。
我的问题是库的语义很重,我想添加一些自动类型推导。
所以我有一堆“基本类型”:
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>
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());
}
这工作正常,问题来自查询:
template <typename ...Ts, typename ...Us>
void query(std::tuple<Ts...> def, std::function<void(std::tuple<Us...>)> resultFeedbackFunc) {
...
}
当调用它时,编译器无法正确推导类型,所以我猜它需要一个迂腐的构造:
SQLiteTable::query<FieldType::Text, FieldType::Integer, /* whatever */> (...)
这是不切实际和冗长的。
可否简化查询功能?
由于我们在使用上有一个限制,即 Us 包只能是与 FieldType::*:rawtype 兼容的某种类型,我想问一下这是否可能使用一些解包和应用方法的构造。在 insert 的情况下,是否可以简化为:
template<typename Ts...>
bool insert (std::tuple<Ts...> def, std::tuple<Ts::rawtype ...> values)
不使用元组,使用可变参数包怎么样?我还没有测试过,但我担心使用类似的东西
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 中使用的其他函数也是如此、执行 和columnsCount。函数 getAllValues 使用递归来填充 tuple。因此,将为数据库的每一行调用仿函数 resultFeedbackFunc()。例如,客户端代码可以填充容器(如 vector )。
更新:
我遵循了@bolov 的解决方案,并添加了@massimiliano-jones 的改进。
这是内部调用反馈函数的正确实现:
resultFeedbackFunc(getValueR<decltype (std::get<Is>(def).rawType())>
(stmt.get(), Is)...);
getValueR 对 sqlite_column_xxx(sqlite3_stmt *, int index) 进行内部调用。如果我理解正确的话,解包是有效的,因为参数列表是解包的有效上下文。如果我想在参数之外进行调用,我必须进行折叠(或解决方法,因为我使用的是 c++11)。
最佳答案
很难提供具体的帮助,因为您的帖子中缺少重要的代码部分。
但是这是我的 2 美分。请注意,我已经用我的想象力填补了您代码中缺失的部分。
首先你需要去掉 std::function争论。使用 std::function仅当您需要它提供的类型删除时。在您的情况下(至少从您显示的代码来看)您不需要它。所以我们用一个简单的 template <class F> 代替它范围。这解决了演绎问题。
现在,当您传递一个无效的函数对象时,您将在 query 的碗深处得到一个编译错误。执行。如果您不想那样,并且想快速失败,那么有一些选择。我选择用 decltype 向您展示 SFINAE 方法.
namespace FieldType
{
struct Integer { using rawtype = int; };
struct Real{ using rawtype = double; };
struct Text{ using rawtype = std::string; };
};
template <class FT>
struct FieldDef
{
using Type = FT;
using RawTye = typename Type::rawtype;
auto getRaw() -> RawTye { return {}; }
};
template <class... Args, class F, std::size_t... Is>
auto query_impl(std::tuple<Args...> def, F f, std::index_sequence<Is...>)
-> decltype(f(std::get<Is>(def).getRaw()...), std::declval<void>())
{
f(std::get<Is>(def).getRaw()...);
}
template <class... Args, class F>
auto query(std::tuple<Args...> def, F f)
-> decltype(query_impl(def, f, std::make_index_sequence<sizeof...(Args)>{}))
{
query_impl(def, f, std::make_index_sequence<sizeof...(Args)>{});
}
auto test()
{
FieldDef<FieldType::Text> mName = {};
FieldDef<FieldType::Integer> mValue = {};
query(std::make_tuple(mName, mValue), [](std::string, int) {}); // OK
query(std::make_tuple(mName, mValue), [](std::string, int, int) {}); // Error
query(std::make_tuple(mName, mValue), [](int, int) {}); // Error
}
无效调用失败并显示类似于以下内容的消息:
error: no matching function for call to 'query' ... note: candidate template ignored: substitution failure [with Args = ...]: no matching function for call to 'query_impl' ...
关于您的第 2 点。那是不可扣除的。即使是这样,您也希望对参数进行分组以提高可读性。 IE。你想要insert({a, b}, {c, d})而不是 insert(a, b, c, d) .
我不明白你的第 3 点。
关于使用推导的 C++ 可变参数扩展,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47326863/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po