From f7cce780cd6a83fca9413abeafb32246999f4b99 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 31 Oct 2020 13:00:53 +0100 Subject: Use static polymorphism in C++ bindings This removes virtual function overhead, and the weird situation of having to include pugl.ipp once (or worse, for pugl to provide a binary C++ library). --- bindings/cxx/include/pugl/pugl.hpp | 170 +++++++++++++++++++-------------- bindings/cxx/include/pugl/pugl.ipp | 176 ----------------------------------- bindings/cxx/include/pugl/vulkan.hpp | 2 +- 3 files changed, 100 insertions(+), 248 deletions(-) delete mode 100644 bindings/cxx/include/pugl/pugl.ipp (limited to 'bindings/cxx') diff --git a/bindings/cxx/include/pugl/pugl.hpp b/bindings/cxx/include/pugl/pugl.hpp index 54c0648..dde29c8 100644 --- a/bindings/cxx/include/pugl/pugl.hpp +++ b/bindings/cxx/include/pugl/pugl.hpp @@ -353,8 +353,14 @@ enum class Cursor { static_assert(Cursor(PUGL_CURSOR_UP_DOWN) == Cursor::upDown, ""); -/// @copydoc PuglView -class View : protected detail::Wrapper +/** + A drawable region that receives events. + + This is the generic base class for all views. It can be used directly by + manually setting an event handling function, but typical applications should + use pugl::View which handles dispatching events. +*/ +class ViewBase : protected detail::Wrapper { public: /** @@ -363,26 +369,13 @@ public: @{ */ - explicit View(World& world) + explicit ViewBase(World& world) : Wrapper{puglNewView(world.cobj())} , _world(world) { - if (cobj()) { - puglSetHandle(cobj(), this); - puglSetEventFunc(cobj(), dispatchEvent); - } - PUGL_CHECK_CONSTRUCTION(cobj(), "Failed to create pugl::View"); } - virtual ~View() noexcept = default; - - View(const View&) = delete; - View& operator=(const View&) = delete; - - View(View&&) = delete; - View&& operator=(View&&) = delete; - const World& world() const noexcept { return _world; } World& world() noexcept { return _world; } @@ -559,6 +552,38 @@ public: return static_cast(puglStopTimer(cobj(), id)); } + PuglView* cobj() noexcept { return Wrapper::cobj(); } + const PuglView* cobj() const noexcept { return Wrapper::cobj(); } + +private: + World& _world; +}; + +/** + A view with event handlers. + + To implement a view, applications can inherit from this class, which handles + type-safe dispatching of events to methods. This class uses the CRTP + pattern, so the name of the derived class needs to be passed as a template + paramter, for example: + + @code + class MyView : public pugl::View + @endcode +*/ +template +class View : public ViewBase +{ +public: + explicit View(World& world) + : ViewBase{world} + { + if (cobj()) { + puglSetHandle(cobj(), this); + puglSetEventFunc(cobj(), eventFunc); + } + } + /** @} @name Event Handlers @@ -566,40 +591,45 @@ public: @{ */ - virtual Status onCreate(const CreateEvent&) PUGL_CONST_FUNC; - virtual Status onDestroy(const DestroyEvent&) PUGL_CONST_FUNC; - virtual Status onConfigure(const ConfigureEvent&) PUGL_CONST_FUNC; - virtual Status onMap(const MapEvent&) PUGL_CONST_FUNC; - virtual Status onUnmap(const UnmapEvent&) PUGL_CONST_FUNC; - virtual Status onUpdate(const UpdateEvent&) PUGL_CONST_FUNC; - virtual Status onExpose(const ExposeEvent&) PUGL_CONST_FUNC; - virtual Status onClose(const CloseEvent&) PUGL_CONST_FUNC; - virtual Status onFocusIn(const FocusInEvent&) PUGL_CONST_FUNC; - virtual Status onFocusOut(const FocusOutEvent&) PUGL_CONST_FUNC; - virtual Status onKeyPress(const KeyPressEvent&) PUGL_CONST_FUNC; - virtual Status onKeyRelease(const KeyReleaseEvent&) PUGL_CONST_FUNC; - virtual Status onText(const TextEvent&) PUGL_CONST_FUNC; - virtual Status onPointerIn(const PointerInEvent&) PUGL_CONST_FUNC; - virtual Status onPointerOut(const PointerOutEvent&) PUGL_CONST_FUNC; - virtual Status onButtonPress(const ButtonPressEvent&) PUGL_CONST_FUNC; - virtual Status onButtonRelease(const ButtonReleaseEvent&) PUGL_CONST_FUNC; - virtual Status onMotion(const MotionEvent&) PUGL_CONST_FUNC; - virtual Status onScroll(const ScrollEvent&) PUGL_CONST_FUNC; - virtual Status onClient(const ClientEvent&) PUGL_CONST_FUNC; - virtual Status onTimer(const TimerEvent&) PUGL_CONST_FUNC; - virtual Status onLoopEnter(const LoopEnterEvent&) PUGL_CONST_FUNC; - virtual Status onLoopLeave(const LoopLeaveEvent&) PUGL_CONST_FUNC; + Status onEvent(const CreateEvent&) noexcept { return Status::success; } + Status onEvent(const DestroyEvent&) noexcept { return Status::success; } + Status onEvent(const ConfigureEvent&) noexcept { return Status::success; } + Status onEvent(const MapEvent&) noexcept { return Status::success; } + Status onEvent(const UnmapEvent&) noexcept { return Status::success; } + Status onEvent(const UpdateEvent&) noexcept { return Status::success; } + Status onEvent(const ExposeEvent&) noexcept { return Status::success; } + Status onEvent(const CloseEvent&) noexcept { return Status::success; } + Status onEvent(const FocusInEvent&) noexcept { return Status::success; } + Status onEvent(const FocusOutEvent&) noexcept { return Status::success; } + Status onEvent(const KeyPressEvent&) noexcept { return Status::success; } + Status onEvent(const KeyReleaseEvent&) noexcept { return Status::success; } + Status onEvent(const TextEvent&) noexcept { return Status::success; } + Status onEvent(const PointerInEvent&) noexcept { return Status::success; } + Status onEvent(const PointerOutEvent&) noexcept { return Status::success; } + Status onEvent(const ButtonPressEvent&) noexcept { return Status::success; } + Status onEvent(const ButtonReleaseEvent&) noexcept + { + return Status::success; + } + Status onEvent(const MotionEvent&) noexcept { return Status::success; } + Status onEvent(const ScrollEvent&) noexcept { return Status::success; } + Status onEvent(const ClientEvent&) noexcept { return Status::success; } + Status onEvent(const TimerEvent&) noexcept { return Status::success; } + Status onEvent(const LoopEnterEvent&) noexcept { return Status::success; } + Status onEvent(const LoopLeaveEvent&) noexcept { return Status::success; } /** @} */ - PuglView* cobj() noexcept { return Wrapper::cobj(); } - const PuglView* cobj() const noexcept { return Wrapper::cobj(); } - private: - static PuglStatus - dispatchEvent(PuglView* view, const PuglEvent* event) noexcept + Derived& self() noexcept { return *static_cast(this); } + const Derived& self() const noexcept + { + return *static_cast(this); + } + + static PuglStatus eventFunc(PuglView* view, const PuglEvent* event) noexcept { View* self = static_cast(puglGetHandle(view)); @@ -621,79 +651,77 @@ private: return PUGL_SUCCESS; case PUGL_CREATE: return static_cast( - onCreate(static_cast(event->any))); + self().onEvent(static_cast(event->any))); case PUGL_DESTROY: return static_cast( - onDestroy(static_cast(event->any))); + self().onEvent(static_cast(event->any))); case PUGL_CONFIGURE: - return static_cast(onConfigure( + return static_cast(self().onEvent( static_cast(event->configure))); case PUGL_MAP: return static_cast( - onMap(static_cast(event->any))); + self().onEvent(static_cast(event->any))); case PUGL_UNMAP: return static_cast( - onUnmap(static_cast(event->any))); + self().onEvent(static_cast(event->any))); case PUGL_UPDATE: return static_cast( - onUpdate(static_cast(event->any))); + self().onEvent(static_cast(event->any))); case PUGL_EXPOSE: return static_cast( - onExpose(static_cast(event->expose))); + self().onEvent(static_cast(event->expose))); case PUGL_CLOSE: return static_cast( - onClose(static_cast(event->any))); + self().onEvent(static_cast(event->any))); case PUGL_FOCUS_IN: return static_cast( - onFocusIn(static_cast(event->focus))); + self().onEvent(static_cast(event->focus))); case PUGL_FOCUS_OUT: - return static_cast( - onFocusOut(static_cast(event->focus))); + return static_cast(self().onEvent( + static_cast(event->focus))); case PUGL_KEY_PRESS: return static_cast( - onKeyPress(static_cast(event->key))); + self().onEvent(static_cast(event->key))); case PUGL_KEY_RELEASE: - return static_cast( - onKeyRelease(static_cast(event->key))); + return static_cast(self().onEvent( + static_cast(event->key))); case PUGL_TEXT: return static_cast( - onText(static_cast(event->text))); + self().onEvent(static_cast(event->text))); case PUGL_POINTER_IN: - return static_cast(onPointerIn( + return static_cast(self().onEvent( static_cast(event->crossing))); case PUGL_POINTER_OUT: - return static_cast(onPointerOut( + return static_cast(self().onEvent( static_cast(event->crossing))); case PUGL_BUTTON_PRESS: - return static_cast(onButtonPress( + return static_cast(self().onEvent( static_cast(event->button))); case PUGL_BUTTON_RELEASE: - return static_cast(onButtonRelease( + return static_cast(self().onEvent( static_cast(event->button))); case PUGL_MOTION: return static_cast( - onMotion(static_cast(event->motion))); + self().onEvent(static_cast(event->motion))); case PUGL_SCROLL: return static_cast( - onScroll(static_cast(event->scroll))); + self().onEvent(static_cast(event->scroll))); case PUGL_CLIENT: return static_cast( - onClient(static_cast(event->client))); + self().onEvent(static_cast(event->client))); case PUGL_TIMER: return static_cast( - onTimer(static_cast(event->timer))); + self().onEvent(static_cast(event->timer))); case PUGL_LOOP_ENTER: return static_cast( - onLoopEnter(static_cast(event->any))); + self().onEvent(static_cast(event->any))); case PUGL_LOOP_LEAVE: return static_cast( - onLoopLeave(static_cast(event->any))); + self().onEvent(static_cast(event->any))); } return PUGL_FAILURE; } - - World& _world; }; /** diff --git a/bindings/cxx/include/pugl/pugl.ipp b/bindings/cxx/include/pugl/pugl.ipp deleted file mode 100644 index c11f7d0..0000000 --- a/bindings/cxx/include/pugl/pugl.ipp +++ /dev/null @@ -1,176 +0,0 @@ -/* - Copyright 2012-2020 David Robillard - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -/** - @file pugl.ipp - @brief Pugl C++ API wrapper implementation. - - This file must be included exactly once in the application. -*/ - -#include "pugl/pugl.hpp" - -namespace pugl { - -#ifdef PUGL_HPP_THROW_FAILED_CONSTRUCTION - -const char* -FailedConstructionError::what() const noexcept -{ - return _msg; -} - -#endif - -Status -View::onCreate(const CreateEvent&) -{ - return Status::success; -} - -Status -View::onDestroy(const DestroyEvent&) -{ - return Status::success; -} - -Status -View::onConfigure(const ConfigureEvent&) -{ - return Status::success; -} - -Status -View::onMap(const MapEvent&) -{ - return Status::success; -} - -Status -View::onUnmap(const UnmapEvent&) -{ - return Status::success; -} - -Status -View::onUpdate(const UpdateEvent&) -{ - return Status::success; -} - -Status -View::onExpose(const ExposeEvent&) -{ - return Status::success; -} - -Status -View::onClose(const CloseEvent&) -{ - return Status::success; -} - -Status -View::onFocusIn(const FocusInEvent&) -{ - return Status::success; -} - -Status -View::onFocusOut(const FocusOutEvent&) -{ - return Status::success; -} - -Status -View::onKeyPress(const KeyPressEvent&) -{ - return Status::success; -} - -Status -View::onKeyRelease(const KeyReleaseEvent&) -{ - return Status::success; -} - -Status -View::onText(const TextEvent&) -{ - return Status::success; -} - -Status -View::onPointerIn(const PointerInEvent&) -{ - return Status::success; -} - -Status -View::onPointerOut(const PointerOutEvent&) -{ - return Status::success; -} - -Status -View::onButtonPress(const ButtonPressEvent&) -{ - return Status::success; -} - -Status -View::onButtonRelease(const ButtonReleaseEvent&) -{ - return Status::success; -} - -Status -View::onMotion(const MotionEvent&) -{ - return Status::success; -} - -Status -View::onScroll(const ScrollEvent&) -{ - return Status::success; -} - -Status -View::onClient(const ClientEvent&) -{ - return Status::success; -} - -Status -View::onTimer(const TimerEvent&) -{ - return Status::success; -} - -Status -View::onLoopEnter(const LoopEnterEvent&) -{ - return Status::success; -} - -Status -View::onLoopLeave(const LoopLeaveEvent&) -{ - return Status::success; -} - -} // namespace pugl diff --git a/bindings/cxx/include/pugl/vulkan.hpp b/bindings/cxx/include/pugl/vulkan.hpp index a7b16cb..f9737e8 100644 --- a/bindings/cxx/include/pugl/vulkan.hpp +++ b/bindings/cxx/include/pugl/vulkan.hpp @@ -143,7 +143,7 @@ getInstanceExtensions() noexcept /// @copydoc puglCreateSurface inline VkResult createSurface(const VulkanLoader& loader, - View& view, + ViewBase& view, VkInstance instance, const VkAllocationCallbacks* const allocator, VkSurfaceKHR* const surface) noexcept -- cgit v1.2.1