include/editeng/outlobj.hxx  |   90 +++++++++++++++++++++++++++++++++++++++++--
 include/o3tl/cow_wrapper.hxx |   27 ++++++++++++
 2 files changed, 113 insertions(+), 4 deletions(-)

New commits:
commit 62d11b0e072b2f34b9f52c33a73cbb54d8112307
Author:     Noel Grandin <noel.gran...@collabora.co.uk>
AuthorDate: Mon Aug 16 18:47:58 2021 +0200
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Wed Aug 18 12:09:09 2021 +0200

    specialise std::optional for OutlineParaObject
    
    we can re-use the o3tl::wrapper pointer to indicate empty
    
    Change-Id: I26e502a7dacfb2bd3d465e71c8fdd89a6a80969b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/120553
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>

diff --git a/include/editeng/outlobj.hxx b/include/editeng/outlobj.hxx
index fac9bed9ffb9..8ba1dedb3f16 100644
--- a/include/editeng/outlobj.hxx
+++ b/include/editeng/outlobj.hxx
@@ -17,8 +17,7 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
-#ifndef INCLUDED_EDITENG_OUTLOBJ_HXX
-#define INCLUDED_EDITENG_OUTLOBJ_HXX
+#pragma once
 
 #include <editeng/paragraphdata.hxx>
 #include <editeng/editengdllapi.h>
@@ -26,6 +25,7 @@
 #include <svl/poolitem.hxx>
 #include <svl/style.hxx>
 #include <o3tl/cow_wrapper.hxx>
+#include <stdexcept>
 #include <memory>
 
 class EditTextObject;
@@ -36,7 +36,7 @@ enum class TextRotation;
  * This is the guts of OutlinerParaObject, refcounted and shared among
  * multiple instances of OutlinerParaObject.
  */
-struct OutlinerParaObjData
+struct EDITENG_DLLPUBLIC OutlinerParaObjData
 {
     // data members
     std::unique_ptr<EditTextObject>  mpEditTextObject;
@@ -64,8 +64,14 @@ struct OutlinerParaObjData
 
 class EDITENG_DLLPUBLIC OutlinerParaObject
 {
+friend class std::optional<OutlinerParaObject>;
     ::o3tl::cow_wrapper< OutlinerParaObjData > mpImpl;
 
+    OutlinerParaObject(std::nullopt_t) noexcept
+        : mpImpl(std::nullopt) {}
+    OutlinerParaObject( const OutlinerParaObject& other, std::nullopt_t ) 
noexcept
+        : mpImpl(other.mpImpl, std::nullopt) {}
+
 public:
     // constructors/destructor
     OutlinerParaObject(std::unique_ptr<EditTextObject>, const 
ParagraphDataVector&, bool bIsEditDoc);
@@ -117,6 +123,82 @@ public:
     void dumpAsXml(xmlTextWriterPtr pWriter) const;
 };
 
-#endif
+namespace std
+{
+    /** Specialise std::optional template for the case where we are wrapping a 
o3tl::cow_wrapper
+        type, and we can make the pointer inside the cow_wrapper act as an 
empty value,
+        and save ourselves some storage */
+    template<>
+    class optional<OutlinerParaObject>
+    {
+    public:
+        optional() noexcept : maParaObject(std::nullopt) {}
+        optional(std::nullopt_t) noexcept : maParaObject(std::nullopt) {}
+        optional(const optional& other) :
+            maParaObject(other.maParaObject, std::nullopt) {}
+        optional(optional&& other) noexcept :
+            maParaObject(std::move(other.maParaObject)) {}
+        optional(OutlinerParaObject&& para) noexcept :
+            maParaObject(std::move(para)) {}
+        optional(const OutlinerParaObject& para) noexcept :
+            maParaObject(para) {}
+        template< class... Args >
+        explicit optional( std::in_place_t, Args&&... args ) :
+            maParaObject(std::forward<Args>(args)...) {}
+
+        optional& operator=(optional const & other)
+        {
+            maParaObject = other.maParaObject;
+            return *this;
+        }
+        optional& operator=(optional&& other) noexcept
+        {
+            maParaObject = std::move(other.maParaObject);
+            return *this;
+        }
+        template< class... Args >
+        void emplace(Args&&... args )
+        {
+            maParaObject = OutlinerParaObject(std::forward<Args>(args)...);
+        }
+
+        bool has_value() const noexcept { return !maParaObject.mpImpl.empty(); 
}
+        explicit operator bool() const noexcept { return 
!maParaObject.mpImpl.empty(); }
+        void reset() { maParaObject.mpImpl.set_empty(); }
+
+        OutlinerParaObject& value()
+        {
+            throwIfEmpty();
+            return maParaObject;
+        }
+        OutlinerParaObject& operator*()
+        {
+            throwIfEmpty();
+            return maParaObject;
+        }
+        const OutlinerParaObject& operator*() const
+        {
+            throwIfEmpty();
+            return maParaObject;
+        }
+        OutlinerParaObject* operator->()
+        {
+            throwIfEmpty();
+            return &maParaObject;
+        }
+        const OutlinerParaObject* operator->() const
+        {
+            throwIfEmpty();
+            return &maParaObject;
+        }
+    private:
+        void throwIfEmpty() const
+        {
+            if (maParaObject.mpImpl.empty())
+                throw std::logic_error("empty 
std::optional<OutlinerParaObject>");
+        }
+        OutlinerParaObject maParaObject;
+    };
+};
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/o3tl/cow_wrapper.hxx b/include/o3tl/cow_wrapper.hxx
index c05d28699259..ced27c1fda32 100644
--- a/include/o3tl/cow_wrapper.hxx
+++ b/include/o3tl/cow_wrapper.hxx
@@ -22,6 +22,7 @@
 
 #include <osl/interlck.h>
 
+#include <optional>
 #include <utility>
 #include <cstddef>
 
@@ -252,6 +253,20 @@ int cow_wrapper_client::queryUnmodified() const
             rSrc.m_pimpl = nullptr;
         }
 
+        // Only intended to be used by std::optional specialisations
+        explicit cow_wrapper( std::nullopt_t ) noexcept :
+            m_pimpl( nullptr )
+        {
+        }
+
+        // Only intended to be used by std::optional specialisations
+        explicit cow_wrapper( const cow_wrapper& rSrc, std::nullopt_t ) : // 
nothrow
+            m_pimpl( rSrc.m_pimpl )
+        {
+            if (m_pimpl)
+                MTPolicy::incrementCount( m_pimpl->m_ref_count );
+        }
+
         ~cow_wrapper() // nothrow, if ~T does not throw
         {
             release();
@@ -325,6 +340,18 @@ int cow_wrapper_client::queryUnmodified() const
             return rOther.m_pimpl == m_pimpl;
         }
 
+        // Only intended to be used by std::optional specialisations
+        bool empty() const { return m_pimpl == nullptr; }
+        // Only intended to be used by std::optional specialisations
+        void set_empty()
+        {
+            if (m_pimpl)
+            {
+                release();
+                m_pimpl = nullptr;
+            }
+        }
+
     private:
         impl_t* m_pimpl;
     };

Reply via email to