C 中的链选项
Chain Optionals in C++
如果在C 中带有链式选项的语句?
中如何避免嵌套?例如,如果类型A包含std::optional<B> b
和类型B A std::optional<C> c
,我希望能够写出以下内容:
const auto v = if_exists(if_exists(a->b)->c);
和v如果B或C为空选件,则可以从C或空的可选。
我认为这会更好,如果这样嵌套了:
if (a->b) {
const auto b = *(a->b);
if (b->c) {
const auto c = *(b->c);
}
}
以下问题似乎朝着这个方向发展,但我不确定如何使其适应我的用例:Haskell样式。类型&amp;*链接*在C 11
您可以做这样的事情(pseudocode-ish;链接到可构建代码的链接如下(:
// wrap std::optional for chaining
template <class T> class Maybe {
std::optional<T> t;
// ... constructors etc
// Maybe chaining
// If A has a member named m of type M,
// then Maybe<A>.fetch(&A::m) returns a Maybe<M>
template <class M>
Maybe<M> fetch(M T::*mem_ptr) {
return (bool(t)) ? Maybe<M>((*t).*mem_ptr) : Maybe<M>() ;
}
// Maybe chaining special case
// If A has a member named m, which is itself a Maybe<M>,
// then return it without wrapping it in an additional Maybe
template <class M>
Maybe<M> fetch(Maybe<M> T::*mem_ptr) {
return (bool(t)) ? ((*t).*mem_ptr) : Maybe<M>() ;
}
};
现在,如果您有:
struct C { int d ; }
struct B { C c; }
struct A { B b; }
A a;
Maybe<A> ma;
您可以做这个
int d = a.b.c.d;
您不能对ma
做同样的事情,但是您可以使用下一个最好的事情,即:
Maybe<int> md = ma.fetch(&A::b).fetch(&B::c).fetch(&C::d);
,如果您Maybe
,您仍然可以使用此功能 - 以上任何或所有struct
成员:
struct C { Maybe<int> d ; }
struct B { Maybe<C> c; }
struct A { Maybe<B> b; }
实时示例(不是生产质量,而是建立(。
您可以使用
template <typename T, typename F>
auto convert_optional(const std::optional<T>& o, F&& f)
-> std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f), *o))>>
{
if (o)
return std::invoke(std::forward<F>(f), *o);
else
return std::nullopt;
}
template <typename T, typename F>
auto convert_optional(std::optional<T>& o, F&& f)
-> std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f), *o))>>
{
if (o)
return std::invoke(std::forward<F>(f), *o);
else
return std::nullopt;
}
template <typename T, typename F>
auto convert_optional(std::optional<T>&& o, F&& f)
-> std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f), *std::move(o)))>>
{
if (o)
return std::invoke(std::forward<F>(f), *std::move(o));
else
return std::nullopt;
}
或
template <typename> struct is_optional : std::false_type {};
template <typename T> struct is_optional<std::optional<T>> : std::true_type {};
template <typename O, typename F>
auto convert_optional(O&& o, F&& f)
-> std::enable_if_t<
is_optional<std::decay_t<O>>::value,
std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f),
*std::forward<O>(o)))>>>
{
if (o)
return std::invoke(std::forward<F>(f), *o);
else
return std::nullopt;
}
您的示例变为:
auto c = convert_optional(convert_optional(a, &A::b).value_or(std::nullopt),
&B::c).value_or(std::nullopt);
convert_optional(a, &A::b)
将返回std::optional<std::optional<B>>
您甚至可以通过其他功能来简化:
template <typename O, typename F>
auto convert_optional_fact(O&& o, F&& f)
-> decltype(convert_optional(std::forward<O>(o),
std::forward<F>(f)).value_or(std::nullopt))
{
return convert_optional(std::forward<O>(o),
std::forward<F>(f)).value_or(std::nullopt);
}
,然后
auto c = convert_optional_fact(convert_optional_fact(a, &A::b), &B::c);
demo
这可以通过简单的宏来实现。
#define CHAIN(OPTIONAL, MEMBER)
([](auto &&opt) {
return opt ? std::optional{opt->MEMBER} : std::nullopt;
}(OPTIONAL))
const auto v = CHAIN(CHAIN(a, b), c);
请注意,有一个建议在C 中允许此可选链接:
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0798r3.html
我喜欢这个部分:
这在其他编程语言中很常见。这是具有可选类型的编程语言,带有单声学界面或一些类似的句法糖:
- Java:可选
- Swift:可选
- haskell:也许
- 锈:选项
- OCAML:选项
- scala:选项
- agda:也许
- idris:也许
- kotlin:t?
- standardml:选项
- c#:无效
这是具有可选的编程语言列表不带有单声道界面或句法糖的键入:
c
我找不到其他任何人
该建议将允许这样的代码:
std::optional<image> get_cute_cat (const image& img) {
return crop_to_cat(img)
.and_then(add_bow_tie)
.and_then(make_eyes_sparkle)
.transform(make_smaller)
.transform(add_rainbow);
}
c 23介绍和_ the or_else解决此不便。
这是一些提议的论文。
在我们可以使用C 23之前,您可以尝试编写一些可以解决此问题的模板。
我的尝试:
namespace detail {
template <auto Field, class T>
struct field_from_opt;
template<typename T, typename FieldType, FieldType T::*ptr>
struct field_from_opt<ptr, T>
{
static auto get(const std::optional<T>& x) -> std::optional<FieldType>
{
if (x) return (*x).*ptr;
return {};
}
};
}
template<auto Field, typename T>
auto if_exists(const std::optional<T>& x)
{
return detail::field_from_opt<Field, T>::get(x);
}
https://godbolt.org/z/dscjyqrx1
使用引用问题中给出的代码,您可以做类似的事情
maybe_do(a->b, [](B &b){
return maybe_do(b.c, [](C &c){
//do what You want to do with the C optional
})
});
- 如何在选项卡视图Qt中设置一个新项目,并保存以前的项目
- Win32编译器选项和内存分配
- C/C++预处理器是否可以检测一些编译器选项
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 将--whole archive链接器选项与CMake和具有其他库依赖项的库一起使用
- 通过选项卡的文本设置QTabWidget顺序
- 通过ccmake在cmake中缓存依赖选项
- 如何传递多个 std::文件系统选项?
- 基于编译器选项的编译二进制路径
- "perf_event_attr"结构的"read_format"属性的选项到底是什么?
- 如何应用 libcurl 的持久连接选项
- 文件中.dat Dlib 选项
- LLVM | codegen 用于带有命令行选项的程序输入功能
- 编译 Boost 时在 OS X 上的"ld:未知选项:-soname"
- 按钮悬停在 QT 中垂直布局的选项卡小部件中不起作用
- Visual Studio C++ 它只构建选项卡中显示的文件吗?
- 在C++不适用于猜数字游戏的情况下再次播放选项
- Qt5 用户界面编译器:-i 选项不可用
- CMake - 更改共享库链接选项
- 从 C++ 运行的进程偶尔会错过某个选项