diff --git a/include/libipc/def.h b/include/libipc/def.h index b3ebd6f..de9a496 100755 --- a/include/libipc/def.h +++ b/include/libipc/def.h @@ -9,7 +9,10 @@ #include #include -#define LIBIPC_NAMESPACE_BEG_ namespace ipc { +#if !defined(LIBIPC_NAMESPACE) +# define LIBIPC_NAMESPACE ipc +#endif +#define LIBIPC_NAMESPACE_BEG_ namespace LIBIPC_NAMESPACE { #define LIBIPC_NAMESPACE_END_ } LIBIPC_NAMESPACE_BEG_ diff --git a/src/libipc/utility/construct.h b/src/libipc/utility/construct.h new file mode 100644 index 0000000..1cf7f3a --- /dev/null +++ b/src/libipc/utility/construct.h @@ -0,0 +1,35 @@ +/** + * @file src/construct.h + * @author mutouyun (orz@orzz.org) + * @brief Construct an object from a memory buffer + * @date 2022-02-27 + */ +#pragma once + +#include // placement-new +#include // std::enable_if_t +#include // std::forward + +#include "libipc/def.h" + +LIBIPC_NAMESPACE_BEG_ + +template +auto construct(void *p, A &&... args) + -> std::enable_if_t<::std::is_constructible::value, T *> { + return ::new (p) T(std::forward(args)...); +} + +template +auto construct(void *p, A &&... args) + -> std::enable_if_t::value, T *> { + return ::new (p) T{std::forward(args)...}; +} + +template +void *destroy(T *p) noexcept { + p->~T(); + return p; +} + +LIBIPC_NAMESPACE_END_ diff --git a/src/libipc/utility/pimpl.h b/src/libipc/utility/pimpl.h new file mode 100644 index 0000000..596fe94 --- /dev/null +++ b/src/libipc/utility/pimpl.h @@ -0,0 +1,73 @@ +/** + * @file src/pimpl.h + * @author mutouyun (orz@orzz.org) + * @brief Pointer To Implementation (pImpl) idiom + * @date 2022-02-27 + */ +#pragma once + +#include +#include +#include + +#include "libipc/utility/construct.h" +#include "libipc/def.h" + +LIBIPC_NAMESPACE_BEG_ +namespace pimpl { + +template +struct is_comfortable { + enum : bool { + value = (sizeof(T) <= sizeof(R)) && (alignof(T) <= alignof(R)) + }; +}; + +template +auto make(A &&... args) -> std::enable_if_t::value, T *> { + T *buf {}; + // construct an object using memory of a pointer + construct(&buf, std::forward(args)...); + return buf; +} + +template +auto get(T * const (& p)) noexcept -> std::enable_if_t::value, T *> { + return reinterpret_cast(&const_cast(reinterpret_cast(p))); +} + +template +auto clear(T *p) noexcept -> std::enable_if_t::value> { + if (p != nullptr) destroy(get(p)); +} + +template +auto make(A &&... args) -> std::enable_if_t::value, T *> { + return new T{std::forward(args)...}; +} + +template +auto get(T * const (& p)) noexcept -> std::enable_if_t::value, T *> { + return p; +} + +template +auto clear(T *p) noexcept -> std::enable_if_t::value> { + if (p != nullptr) delete p; +} + +template +class Obj { + public: + template + static T *make(A &&... args) { + return pimpl::make(std::forward(args)...); + } + + void clear() noexcept { + pimpl::clear(static_cast(const_cast(this))); + } +}; + +} // namespace pimpl +LIBIPC_NAMESPACE_END_ diff --git a/test/test_src_utility.cpp b/test/test_src_utility.cpp new file mode 100644 index 0000000..4d6e7d8 --- /dev/null +++ b/test/test_src_utility.cpp @@ -0,0 +1,68 @@ + +#include // std::aligned_storage_t +#include + +#include "gtest/gtest.h" + +#include "libipc/utility/construct.h" +#include "libipc/utility/pimpl.h" + +TEST(utility, construct) { + struct Foo { + int a_; + short b_; + char c_; + }; + std::aligned_storage_t foo; + Foo *pfoo = ipc::construct(&foo, 123, short{321}, '1'); + EXPECT_EQ(pfoo->a_, 123); + EXPECT_EQ(pfoo->b_, 321); + EXPECT_EQ(pfoo->c_, '1'); + + struct Bar : Foo { + Bar(int a, short b, char c) + : Foo{a, b, c} {} + }; + std::aligned_storage_t bar; + Bar *pbar = ipc::construct(&bar, 123, short(321), '1'); + EXPECT_EQ(pbar->a_, 123); + EXPECT_EQ(pbar->b_, 321); + EXPECT_EQ(pbar->c_, '1'); +} + +namespace { + +struct Foo : ipc::pimpl::Obj { + int *pi_; + Foo(int *pi) : pi_(pi) {} +}; + +struct Bar : ipc::pimpl::Obj { + int *pi_; + int *pj_; + Bar(int *pi, int *pj) : pi_(pi), pj_(pj) {} +}; + +} // namespace + +TEST(utility, pimpl_is_comfortable) { + EXPECT_TRUE ((ipc::pimpl::is_comfortable::value)); + EXPECT_TRUE ((ipc::pimpl::is_comfortable::value)); + EXPECT_FALSE((ipc::pimpl::is_comfortable::value)); + + EXPECT_TRUE ((ipc::pimpl::is_comfortable::value)); + EXPECT_FALSE((ipc::pimpl::is_comfortable::value)); +} + +TEST(utility, pimpl_inherit) { + int i = 123; + Foo *pfoo = Foo::make(&i); + EXPECT_EQ(ipc::pimpl::get(pfoo)->pi_, &i); + pfoo->clear(); + + int j = 321; + Bar *pbar = Bar::make(&i, &j); + EXPECT_EQ(ipc::pimpl::get(pbar)->pi_, &i); + EXPECT_EQ(ipc::pimpl::get(pbar)->pj_, &j); + pbar->clear(); +}