- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我们的 Python 代码库包含与指标相关的代码,如下所示:
class Timer:
def __enter__(self, name):
self.name = name
self.start = time.time()
def __exit__(self):
elapsed = time.time() - self.start
log.info('%s took %f seconds' % (self.name, elapsed))
...
with Timer('foo'):
do some work
with Timer('bar') as named_timer:
do some work
named_timer.some_mutative_method()
do some more work
在 Python 的术语中,计时器是一个上下文管理器。
现在我们想在 C++ 中实现相同的功能,并使用同样漂亮的语法。不幸的是,C++ 没有with
。所以“明显”的成语是(经典的 RAII)
class Timer {
Timer(std::string name) : name_(std::move(name)) {}
~Timer() { /* ... */ }
};
if (true) {
Timer t("foo");
do some work
}
if (true) {
Timer named_timer("bar");
do some work
named_timer.some_mutative_method();
do some more work
}
但这是极其丑陋的语法加盐:它比需要的多了很多行,我们不得不为我们的“未命名”计时器引入一个名称 t
(如果我们忘记了,代码会静静地中断那个名字)......它只是丑陋。
人们用来处理 C++ 中的“上下文管理器”的句法习语有哪些?
我想到了这个滥用的想法,它减少了行数但没有去掉 t
这个名字:
// give Timer an implicit always-true conversion to bool
if (auto t = Timer("foo")) {
do some work
}
或者这个建筑上的怪物,我什至不相信我自己能够正确使用它:
Timer("foo", [&](auto&) {
do some work
});
Timer("bar", [&](auto& named_timer) {
do some work
named_timer.some_mutative_method();
do some more work
});
Timer
的构造函数实际调用给定的 lambda(带有参数 *this
)并一次完成所有日志记录。
不过,这些想法似乎都不是“最佳实践”。帮帮我!
表达该问题的另一种方式可能是:如果您从头开始设计 std::lock_guard
,您将如何设计以尽可能多地消除样板代码? lock_guard
是上下文管理器的一个完美示例:它是一个实用程序,本质上是 RAII,您几乎不想为它命名。
最佳答案
可以非常接近地模仿 Python 语法和语义。以下测试用例经过编译并具有与您在 Python 中所拥有的基本相似的语义:
// https://github.com/KubaO/stackoverflown/tree/master/questions/pythonic-with-33088614
#include <cassert>
#include <cstdio>
#include <exception>
#include <iostream>
#include <optional>
#include <string>
#include <type_traits>
[...]
int main() {
// with Resource("foo"):
// print("* Doing work!\n")
with<Resource>("foo") >= [&] {
std::cout << "1. Doing work\n";
};
// with Resource("foo", True) as r:
// r.say("* Doing work too")
with<Resource>("bar", true) >= [&](auto &r) {
r.say("2. Doing work too");
};
for (bool succeed : {true, false}) {
// Shorthand for:
// try:
// with Resource("bar", succeed) as r:
// r.say("Hello")
// print("* Doing work\n")
// except:
// print("* Can't do work\n")
with<Resource>("bar", succeed) >= [&](auto &r) {
r.say("Hello");
std::cout << "3. Doing work\n";
} >= else_ >= [&] {
std::cout << "4. Can't do work\n";
};
}
}
这是给定的
class Resource {
const std::string str;
public:
const bool successful;
Resource(const Resource &) = delete;
Resource(Resource &&) = delete;
Resource(const std::string &str, bool succeed = true)
: str(str), successful(succeed) {}
void say(const std::string &s) {
std::cout << "Resource(" << str << ") says: " << s << "\n";
}
};
with
自由函数将所有工作传递给 with_impl
类:
template <typename T, typename... Ts>
with_impl<T> with(Ts &&... args) {
return with_impl<T>(std::forward<Ts>(args)...);
}
我们如何到达那里?首先,我们需要一个 context_manager
类:实现 enter
和 exit
方法的 traits 类——相当于 Python 的 __enter__
和 __exit__
。一旦 is_detected
类型特征被引入 C++,这个类也可以很容易地转发到类类型的兼容 enter
和 exit
方法T
,从而更好地模仿 Python 的语义。就目前而言,上下文管理器相当简单:
template <typename T>
class context_manager_base {
protected:
std::optional<T> context;
public:
T &get() { return context.value(); }
template <typename... Ts>
std::enable_if_t<std::is_constructible_v<T, Ts...>, bool> enter(Ts &&... args) {
context.emplace(std::forward<Ts>(args)...);
return true;
}
bool exit(std::exception_ptr) {
context.reset();
return true;
}
};
template <typename T>
class context_manager : public context_manager_base<T> {};
让我们看看这个类将如何专门用于包装 Resource
对象,或 std::FILE *
。
template <>
class context_manager<Resource> : public context_manager_base<Resource> {
public:
template <typename... Ts>
bool enter(Ts &&... args) {
context.emplace(std::forward<Ts>(args)...);
return context.value().successful;
}
};
template <>
class context_manager<std::FILE *> {
std::FILE *file;
public:
std::FILE *get() { return file; }
bool enter(const char *filename, const char *mode) {
file = std::fopen(filename, mode);
return file;
}
bool leave(std::exception_ptr) { return !file || (fclose(file) == 0); }
~context_manager() { leave({}); }
};
核心功能的实现在 with_impl
类型中。请注意套件中的异常处理(第一个 lambda)和 exit
函数如何模仿 Python 行为。
static class else_t *else_;
class pass_exceptions_t {};
template <typename T>
class with_impl {
context_manager<T> mgr;
bool ok;
enum class Stage { WITH, ELSE, DONE } stage = Stage::WITH;
std::exception_ptr exception = {};
public:
with_impl(const with_impl &) = delete;
with_impl(with_impl &&) = delete;
template <typename... Ts>
explicit with_impl(Ts &&... args) {
try {
ok = mgr.enter(std::forward<Ts>(args)...);
} catch (...) {
ok = false;
}
}
template <typename... Ts>
explicit with_impl(pass_exceptions_t, Ts &&... args) {
ok = mgr.enter(std::forward<Ts>(args)...);
}
~with_impl() {
if (!mgr.exit(exception) && exception) std::rethrow_exception(exception);
}
with_impl &operator>=(else_t *) {
assert(stage == Stage::ELSE);
return *this;
}
template <typename Fn>
std::enable_if_t<std::is_invocable_r_v<void, Fn, decltype(mgr.get())>, with_impl &>
operator>=(Fn &&fn) {
assert(stage == Stage::WITH);
if (ok) try {
std::forward<Fn>(fn)(mgr.get());
} catch (...) {
exception = std::current_exception();
}
stage = Stage::ELSE;
return *this;
}
template <typename Fn>
std::enable_if_t<std::is_invocable_r_v<bool, Fn, decltype(mgr.get())>, with_impl &>
operator>=(Fn &&fn) {
assert(stage == Stage::WITH);
if (ok) try {
ok = std::forward<Fn>(fn)(mgr.get());
} catch (...) {
exception = std::current_exception();
}
stage = Stage::ELSE;
return *this;
}
template <typename Fn>
std::enable_if_t<std::is_invocable_r_v<void, Fn>, with_impl &> operator>=(Fn &&fn) {
assert(stage != Stage::DONE);
if (stage == Stage::WITH) {
if (ok) try {
std::forward<Fn>(fn)();
} catch (...) {
exception = std::current_exception();
}
stage = Stage::ELSE;
} else {
assert(stage == Stage::ELSE);
if (!ok) std::forward<Fn>(fn)();
if (!mgr.exit(exception) && exception) std::rethrow_exception(exception);
stage = Stage::DONE;
}
return *this;
}
template <typename Fn>
std::enable_if_t<std::is_invocable_r_v<bool, Fn>, with_impl &> operator>=(Fn &&fn) {
assert(stage != Stage::DONE);
if (stage == Stage::WITH) {
if (ok) try {
ok = std::forward<Fn>(fn)();
} catch (...) {
exception = std::current_exception();
}
stage = Stage::ELSE;
} else {
assert(stage == Stage::ELSE);
if (!ok) std::forward<Fn>(fn)();
if (!mgr.exit(exception) && exception) std::rethrow_exception(exception);
stage = Stage::DONE;
}
return *this;
}
};
关于c++ - 在 C++ 中实现 "contextmanager"的最佳实践 + 语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33088614/
背景: 我最近一直在使用 JPA,我为相当大的关系数据库项目生成持久层的轻松程度给我留下了深刻的印象。 我们公司使用大量非 SQL 数据库,特别是面向列的数据库。我对可能对这些数据库使用 JPA 有一
我已经在我的 maven pom 中添加了这些构建配置,因为我希望将 Apache Solr 依赖项与 Jar 捆绑在一起。否则我得到了 SolarServerException: ClassNotF
interface ITurtle { void Fight(); void EatPizza(); } interface ILeonardo : ITurtle {
我希望可用于 Java 的对象/关系映射 (ORM) 工具之一能够满足这些要求: 使用 JPA 或 native SQL 查询获取大量行并将其作为实体对象返回。 允许在行(实体)中进行迭代,并在对当前
好像没有,因为我有实现From for 的代码, 我可以转换 A到 B与 .into() , 但同样的事情不适用于 Vec .into()一个Vec . 要么我搞砸了阻止实现派生的事情,要么这不应该发
在 C# 中,如果 A 实现 IX 并且 B 继承自 A ,是否必然遵循 B 实现 IX?如果是,是因为 LSP 吗?之间有什么区别吗: 1. Interface IX; Class A : IX;
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我正在阅读标准haskell库的(^)的实现代码: (^) :: (Num a, Integral b) => a -> b -> a x0 ^ y0 | y0 a -> b ->a expo x0
我将把国际象棋游戏表示为 C++ 结构。我认为,最好的选择是树结构(因为在每个深度我们都有几个可能的移动)。 这是一个好的方法吗? struct TreeElement{ SomeMoveType
我正在为用户名数据库实现字符串匹配算法。我的方法采用现有的用户名数据库和用户想要的新用户名,然后检查用户名是否已被占用。如果采用该方法,则该方法应该返回带有数据库中未采用的数字的用户名。 例子: “贾
我正在尝试实现 Breadth-first search algorithm , 为了找到两个顶点之间的最短距离。我开发了一个 Queue 对象来保存和检索对象,并且我有一个二维数组来保存两个给定顶点
我目前正在 ika 中开发我的 Python 游戏,它使用 python 2.5 我决定为 AI 使用 A* 寻路。然而,我发现它对我的需要来说太慢了(3-4 个敌人可能会落后于游戏,但我想供应 4-
我正在寻找 Kademlia 的开源实现C/C++ 中的分布式哈希表。它必须是轻量级和跨平台的(win/linux/mac)。 它必须能够将信息发布到 DHT 并检索它。 最佳答案 OpenDHT是
我在一本书中读到这一行:-“当我们要求 C++ 实现运行程序时,它会通过调用此函数来实现。” 而且我想知道“C++ 实现”是什么意思或具体是什么。帮忙!? 最佳答案 “C++ 实现”是指编译器加上链接
我正在尝试使用分支定界的 C++ 实现这个背包问题。此网站上有一个 Java 版本:Implementing branch and bound for knapsack 我试图让我的 C++ 版本打印
在很多情况下,我需要在 C# 中访问合适的哈希算法,从重写 GetHashCode 到对数据执行快速比较/查找。 我发现 FNV 哈希是一种非常简单/好/快速的哈希算法。但是,我从未见过 C# 实现的
目录 LRU缓存替换策略 核心思想 不适用场景 算法基本实现 算法优化
1. 绪论 在前面文章中提到 空间直角坐标系相互转换 ,测绘坐标转换时,一般涉及到的情况是:两个直角坐标系的小角度转换。这个就是我们经常在测绘数据处理中,WGS-84坐标系、54北京坐标系
在软件开发过程中,有时候我们需要定时地检查数据库中的数据,并在发现新增数据时触发一个动作。为了实现这个需求,我们在 .Net 7 下进行一次简单的演示. PeriodicTimer .
二分查找 二分查找算法,说白了就是在有序的数组里面给予一个存在数组里面的值key,然后将其先和数组中间的比较,如果key大于中间值,进行下一次mid后面的比较,直到找到相等的,就可以得到它的位置。
我是一名优秀的程序员,十分优秀!