From db5a19380c09923ce224aaafde1e44fc441db16b Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Fri, 24 Nov 2023 13:05:58 -0500 Subject: [PATCH] cpp-common/bt2: add `bt2::OptionalBorrowedObject` An instance of this new class template manages an optional contained borrowed object of type `ObjT`, that is, a borrowed object that may or may not be present. Such an object considers that a `nullptr` libbabeltrace2 object pointer means none. Therefore, using a `bt2::OptionalBorrowedObject` isn't more costly, in time and space, as using a libbabeltrace2 object pointer in C, but offers the typical C++ optional interface. There's no `bt2s::nullopt` equivalent: just call reset(). An optional borrowed object stores the library pointer itself, only constructing `ObjT` when it's known that the library pointer isn't `nullptr`. The class is pretty straightforward, except that: * It has a few constructors and assignment operators to make things such as this work: bt2::OptionalBorrowedObject f(const bt2::StringValue val) { if (std::string {*val} == "meow") { return val; } return {}; } Also, you may assign a `bt2::OptionalBorrowedObject` instance to a `bt2::OptionalBorrowedObject` instance if `Y` inherits `X`. * operator->() would need to return the address of some `ObjT` instance, but an optional borrowed object has none, so return a proxy instead. The result is a very natural usage: std::uint64_t getCsValue(const bt2::OptionalBorrowedObject optCs) { return optCs ? optCs->value() : 0; } When optimized, the resulting instructions are the same as for an equivalent C function: getCsValue(bt2::Optional): test rdi, rdi je .L2 jmp bt_clock_snapshot_get_value .L2: xor eax, eax ret Signed-off-by: Philippe Proulx Change-Id: Ic873ea4ff8b0f084189ece3ac139680469c907d3 Reviewed-on: https://review.lttng.org/c/babeltrace/+/11438 Reviewed-by: Simon Marchi Tested-by: jenkins CI-Build: Simon Marchi --- src/Makefile.am | 1 + src/cpp-common/bt2/borrowed-object.hpp | 3 + .../bt2/optional-borrowed-object.hpp | 196 ++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 src/cpp-common/bt2/optional-borrowed-object.hpp diff --git a/src/Makefile.am b/src/Makefile.am index 88f54afb..f158fe27 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,6 +31,7 @@ noinst_HEADERS = \ cpp-common/bt2/message.hpp \ cpp-common/bt2/shared-object.hpp \ cpp-common/bt2/raw-value-proxy.hpp \ + cpp-common/bt2/optional-borrowed-object.hpp \ cpp-common/bt2/plugin-dev.hpp \ cpp-common/bt2/private-query-executor.hpp \ cpp-common/bt2/self-component-class.hpp \ diff --git a/src/cpp-common/bt2/borrowed-object.hpp b/src/cpp-common/bt2/borrowed-object.hpp index f3b4fdcc..fbfd5b10 100644 --- a/src/cpp-common/bt2/borrowed-object.hpp +++ b/src/cpp-common/bt2/borrowed-object.hpp @@ -25,6 +25,9 @@ namespace bt2 { * * The user of a borrowed object, including methods of a derived class, * can call libObjPtr() to access the libbabeltrace2 object pointer. + * + * You may only build a borrowed object with a pointer which isn't + * `nullptr`. See `bt2::OptionalBorrowedObject` for an optional version. */ template class BorrowedObject diff --git a/src/cpp-common/bt2/optional-borrowed-object.hpp b/src/cpp-common/bt2/optional-borrowed-object.hpp new file mode 100644 index 00000000..100abf4d --- /dev/null +++ b/src/cpp-common/bt2/optional-borrowed-object.hpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2023 Philippe Proulx + * + * SPDX-License-Identifier: MIT + */ + +#ifndef BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP +#define BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP + +#include + +#include "common/assert.h" + +#include "borrowed-object-proxy.hpp" + +namespace bt2 { + +/* + * An instance of this class template manages an optional contained + * borrowed object of type `ObjT`, that is, a borrowed object that may + * or may not be present. + * + * Such an object considers that a `nullptr` libbabeltrace2 object + * pointer means none. Therefore, using an `OptionalBorrowedObject` + * isn't more costly, in time and space, as using a libbabeltrace2 + * object pointer in C, but offers the typical C++ optional interface. + * + * There's no `bt2s::nullopt` equivalent: just use the default + * constructor or call reset(). + * + * There are a constructors an assignment operators which accept an + * instance of another wrapper object or of another optional borrowed + * object. For those, static assertions make sure that the assignment + * would work with borrowed objects, for example: + * + * auto sharedIntVal = createValue(23L); + * + * OptionalBorrowedObject myVal {*sharedIntVal}; + * + * This is needed because `OptionalBorrowedObject` only keeps a + * libbabeltrace2 pointer (`_mLibObjPtr`), therefore it doesn't + * automatically know the relation between wrapper classes. + */ +template +class OptionalBorrowedObject final +{ +public: + using Obj = ObjT; + using LibObjPtr = typename ObjT::LibObjPtr; + + /* + * Builds an optional borrowed object without an object. + * + * Intentionally not explicit. + */ + OptionalBorrowedObject() noexcept = default; + + /* + * Builds an optional borrowed object with an instance of `ObjT` + * wrapping the libbabeltrace2 pointer `libObjPtr`. + * + * Intentionally not explicit. + */ + OptionalBorrowedObject(const LibObjPtr libObjPtr) noexcept : _mLibObjPtr {libObjPtr} + { + } + + /* + * Builds an optional borrowed object with an instance of `ObjT` + * constructed from `obj`. + * + * It must be possible to construct an instance of `ObjT` with an + * instance of `OtherObjT`. + * + * Intentionally not explicit. + */ + template + OptionalBorrowedObject(const OtherObjT obj) noexcept : _mLibObjPtr {obj.libObjPtr()} + { + static_assert(std::is_constructible::value, + "`ObjT` is constructible with an instance of `OtherObjT`."); + } + + /* + * Builds an optional borrowed object from `optObj`, with or without + * an object. + * + * It must be possible to construct an instance of `ObjT` with an + * instance of `OtherObjT`. + * + * Intentionally not explicit. + */ + template + OptionalBorrowedObject(const OptionalBorrowedObject optObj) noexcept : + _mLibObjPtr {optObj.libObjPtr()} + { + static_assert(std::is_constructible::value, + "`ObjT` is constructible with an instance of `OtherObjT`."); + } + + /* + * Makes this optional borrowed object have an instance of `ObjT` + * wrapping the libbabeltrace2 pointer `libObjPtr`. + */ + OptionalBorrowedObject& operator=(const LibObjPtr libObjPtr) noexcept + { + _mLibObjPtr = libObjPtr; + return *this; + } + + /* + * Makes this optional borrowed object have an instance of `ObjT` + * constructed from `obj`. + * + * It must be possible to construct an instance of `ObjT` with an + * instance of `OtherObjT`. + */ + template + OptionalBorrowedObject& operator=(const ObjT obj) noexcept + { + static_assert(std::is_constructible::value, + "`ObjT` is constructible with an instance of `OtherObjT`."); + _mLibObjPtr = obj.libObjPtr(); + return *this; + } + + /* + * Sets this optional borrowed object to `optObj`. + * + * It must be possible to construct an instance of `ObjT` with an + * instance of `OtherObjT`. + */ + template + OptionalBorrowedObject& operator=(const OptionalBorrowedObject optObj) noexcept + { + static_assert(std::is_constructible::value, + "`ObjT` is constructible with an instance of `OtherObjT`."); + _mLibObjPtr = optObj.libObjPtr(); + return *this; + } + + /* Wrapped libbabeltrace2 object pointer (may be `nullptr`) */ + LibObjPtr libObjPtr() const noexcept + { + return _mLibObjPtr; + } + + ObjT object() const noexcept + { + BT_ASSERT_DBG(_mLibObjPtr); + return ObjT {_mLibObjPtr}; + } + + ObjT operator*() const noexcept + { + return this->object(); + } + + /* + * We want to return the address of an `ObjT` instance here, but we + * only have a library pointer (`_mLibObjPtr`) because an `ObjT` + * instance may not wrap `nullptr`. + * + * Therefore, return a proxy object which holds an internal + * `ObjT` instance and implements operator->() itself. + * + * In other words, doing `myOptObj->something()` is equivalent to + * calling `myOptObj.operator->().operator->()->something()`. + */ + BorrowedObjectProxy operator->() const noexcept + { + return BorrowedObjectProxy {_mLibObjPtr}; + } + + bool hasObject() const noexcept + { + return _mLibObjPtr; + } + + operator bool() const noexcept + { + return this->hasObject(); + } + + void reset() noexcept + { + _mLibObjPtr = nullptr; + } + +private: + typename ObjT::LibObjPtr _mLibObjPtr = nullptr; +}; + +} /* namespace bt2 */ + +#endif /* BABELTRACE_CPP_COMMON_BT2_OPTIONAL_BORROWED_OBJECT_HPP */ -- 2.34.1