这是一个有趣的挑战。
我想创建一些样板,允许使用类似于 perl 的 x 或 die("reason") 方法的语法干净地处理前提条件失败。
我想到了这个删除器:
struct do_exit {
[[noreturn]]
void operator()(void*) const noexcept {
std::exit(code_);
}
int code_;
};
我们可以使用它来管理可能指向 std::cerr 的临时 std::unique_ptr 的“删除”:
struct exit_stream
{
exit_stream(int code, std::ostream& os)
: stream_(std::addressof(os), do_exit { code })
{}
std::ostream& as_stream() const { return *stream_; }
operator bool() const { return bool(*stream_); }
template<class T>
friend auto operator<<(const exit_stream& es, T const& t) -> exit_stream const&
{
es.as_stream() << t;
return es;
}
std::unique_ptr<std::ostream, do_exit> stream_;
};
使用以下函数创建:
exit_stream die(int code = 100)
{
return exit_stream(code, std::cerr);
}
现在我们可以编写如下程序:
int main(int argc, char* argv[]) {
// preconditions
argc >= 2 or die(4) << "usage: myprog <inputfile>\n";
auto input = std::ifstream(argv[1]);
input or die(100) << "failed to open " << argv[1] << "\n";
}
这是有效的,因为当 exit_stream 作为临时返回时,它必须被移动(unique_ptr 删除复制操作符)。 unique_ptr 保证移出指针将为空。它还保证仅当 unique_ptr 不为空时才会调用删除器。因此,只有在调用点执行完所有流操作后,exit 才会被调用。
然而,明显的失败是我不会写:
auto input = std::ifstream(argv[1]]) or die() << "failed to open " << argv[1] << "\n";
因为如果我这样做,input 将是一个 bool。
这是我的问题。你能想出一种方法来产生所需的效果,或者尽可能接近它吗?
我真的想避免这种情况,因为传递案例的效率,以及清晰和优雅:
auto input = (std::ifstream(argv[1]]) or die() << "failed to open " << argv[1] << "\n").get();
完整代码:
#include <fstream>
#include <iostream>
#include <cstdlib>
struct do_exit {
[[noreturn]]
void operator()(void*) const noexcept {
std::exit(code_);
}
int code_;
};
struct exit_stream
{
exit_stream(int code, std::ostream& os)
: stream_(std::addressof(os), do_exit { code })
{}
std::ostream& as_stream() const { return *stream_; }
operator bool() const { return bool(*stream_); }
template<class T>
friend auto operator<<(const exit_stream& es, T const& t) -> exit_stream const&
{
es.as_stream() << t;
return es;
}
std::unique_ptr<std::ostream, do_exit> stream_;
};
exit_stream die(int code = 100)
{
return exit_stream(code, std::cerr);
}
int main(int argc, char* argv[]) {
// preconditions
argc >= 2 or die(4) << "usage: myprog <inputfile>\n";
auto input = std::ifstream(argv[1]);
input or die(100) << "failed to open " << argv[1];
}
最佳答案
好的,这是我的第一次尝试。由于缺少短路,我有点担心冗余构造,但语法是我想要的。
欢迎评论/改进!
#include <fstream>
#include <iostream>
#include <utility>
#include <iomanip>
namespace detail {
struct tuple_emitter {
tuple_emitter(std::ostream &os) : os(os) {}
template<class...Refs>
void operator()(std::tuple<Refs...> const &t) const {
emit_impl(t, std::make_index_sequence<sizeof...(Refs)>());
}
private:
template<class T>
void emit_impl(T const &t) const {
os << t;
}
// make sure we handle lazy manipulators
void emit_impl(std::ios_base (* f)(std::ios_base &)) const
{
f(os);
}
template<class Tie, std::size_t...Is>
void emit_impl(Tie const &refs, std::index_sequence<Is...>) const {
using expand = int[];
void(expand{0,
((emit_impl(std::get<Is>(refs))), 0)...});
os << std::endl;
};
std::ostream &os;
};
// a class that emits a number of references to std::cerr and then
// exits
template<class...Refs>
struct dier {
dier(int code, std::tuple<Refs...> t) : code(code), emitter(std::cout), refs_(t) {}
template<class...Others, class Ref>
dier(dier<Others...> const &original, Ref const &r)
: code(original.code), emitter(original.emitter), refs_(std::tuple_cat(original.refs_, std::tie(r))) {};
void operator()() const {
emitter(refs_);
std::exit(code);
}
int code;
tuple_emitter emitter;
std::tuple<Refs&...> refs_;
};
template<class...Refs, class Ref>
auto operator<<(dier<Refs...> original, Ref const &ref) {
return dier<Refs..., const Ref &>(original, ref);
};
template<class T, class...Refs>
T operator||(T &&t, dier<Refs...> death) {
if (!t) {
death();
}
return std::move(t);
};
template<class T, class...Refs>
T const &operator||(T &t, dier<Refs...> death) {
if (!t) {
death();
}
return t;
};
}
auto die(int code = 100) {
return detail::dier<>(code, std::make_tuple());
}
int main(int argc, char *argv[]) {
// preconditions
argc >= 2 or die(4) << "usage: myprog <inputfile>\n";
auto input = std::ifstream(argv[1]) or die(5) << "failed to open " << std::setfill('-') << std::setw(10) << argv[1] << " and here is a hex number " << std::hex << 40;
}
预期产出:
没有参数:
usage: myprog <inputfile>
使用一个参数“foo.txt”:
failed to open ---foo.txt and here is a hex number 28
根据 Yakk 的建议更新
归结为这个 - 这更清晰:
#include <fstream>
#include <iostream>
#include <utility>
#include <iomanip>
namespace notstd {
namespace detail {
template<class F, class T, std::size_t...Is>
void apply_impl(F&& f, T&& t, std::index_sequence<Is...>)
{
using expand = int[];
void(expand{0,
((f(std::get<Is>(t))), 0)...});
};
}
template<class F, class...Ts>
void apply(F&& f, std::tuple<Ts...> const& t)
{
detail::apply_impl(std::forward<F>(f), t, std::make_index_sequence<sizeof...(Ts)>());
};
struct apply_to_stream
{
apply_to_stream(std::ostream& os)
: os(os)
{}
template<class T>
std::ostream& operator()(T&& t) const
{
return os << t;
};
std::ostream& operator()(std::ios_base& (*f)(std::ios_base&)) const
{
f(os);
return os;
};
std::ostream& os;
};
}
namespace detail {
template<class Tuple>
struct dier {
constexpr dier(int code, Tuple t) : code(code), refs_(std::move(t)) {}
void operator()() const {
notstd::apply(notstd::apply_to_stream(std::cout), refs_);
std::cout << '\n';
std::exit(code);
}
int code;
Tuple refs_;
};
template<class Tie>
constexpr auto make_dier(int code, Tie&& t) {
return detail::dier<Tie>(code, std::forward<Tie>(t));
}
template<class Tuple, class Ref>
constexpr auto operator<<(dier<Tuple> original, Ref&& ref) {
return make_dier(original.code, std::tuple_cat(original.refs_, std::tie(ref)));
}
template<class T, class...Refs>
T operator||(T &&t, dier<Refs...> death) {
if (!t) {
death();
}
return std::forward<T>(t);
}
}
template<class Tie = std::tuple<>>
constexpr auto die(int code = 100, Tie&& t = {}) {
return detail::make_dier(code, std::forward<Tie>(t));
}
int main(int argc, char *argv[]) {
// preconditions
argc >= 2 or die(4) << "usage: myprog <inputfile>\n";
auto input = std::ifstream(argv[1]) or die(5) << "failed to open " << std::setfill('-') << std::setw(10) << argv[1] << " and here is a hex number " << std::hex << 40;
}
关于c++ - C++ 中干净、功能性的脚本式错误处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42989330/
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我有一个在Linux服务器上运行的ruby脚本。它不使用rails或任何东西。它基本上是一个命令行ruby脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg
我遵循MichaelHartl的“RubyonRails教程:学习Web开发”,并创建了检查用户名和电子邮件长度有效性的测试(名称最多50个字符,电子邮件最多255个字符)。test/helpers/application_helper_test.rb的内容是:require'test_helper'classApplicationHelperTest在运行bundleexecraketest时,所有测试都通过了,但我看到以下消息在最后被标记为错误:ERROR["test_full_title_helper",ApplicationHelperTest,1.820016791]test
我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c
我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file
我克隆了一个rails仓库,我现在正尝试捆绑安装背景:OSXElCapitanruby2.2.3p173(2015-08-18修订版51636)[x86_64-darwin15]rails-v在您的Gemfile中列出的或native可用的任何gem源中找不到gem'pg(>=0)ruby'。运行bundleinstall以安装缺少的gem。bundleinstallFetchinggemmetadatafromhttps://rubygems.org/............Fetchingversionmetadatafromhttps://rubygems.org/...Fe
在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee