diff --git a/doc/describe/classes.adoc b/doc/describe/classes.adoc index 811189c..513807f 100644 --- a/doc/describe/classes.adoc +++ b/doc/describe/classes.adoc @@ -187,3 +187,55 @@ public: The case where a member function and a static member function have the same name and the same function type is currently not supported. + +## Constructors + +Constructors can be described with either `BOOST_DESCRIBE_CLASS_CONSTRUCTORS` for classes with +private members inside the class decleration +or `BOOST_DESCRIBE_STRUCT_CONSTRUCTORS` for a public description from outside the class. + +```cpp +class Y +{ + private: + Y(); + Y(int); + + BOOST_DESCRIBE_CLASS_CONSTRUCTORS(Y, (), (int)); +}; +``` + +```cpp +struct X +{ + X(); + X(const double& ); +}; + +BOOST_DESCRIBE_STRUCT_CONSTRUCTORS(X, (), (const double &)); +``` + + +The constructor list can be obtained with `describe_constructors`, where `X` is the class. + +The entries in the list look like the following: + +```cpp +struct C1 +{ + // The signature of the constructors + using signature = Y(int); + + static constexpr unsigned modifiers = mod_constructor; + + static constexpr bool is_noexcept = false; + static constexpr bool is_trivial = false; + + constexpr static C* construct_at(C* p, int i) + { + return new (p) C(i); + } +}; +``` + +The constructor descriptors do not provide a way to determine visibility. diff --git a/include/boost/describe.hpp b/include/boost/describe.hpp index de3c883..a869c10 100644 --- a/include/boost/describe.hpp +++ b/include/boost/describe.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include diff --git a/include/boost/describe/class.hpp b/include/boost/describe/class.hpp index 8d3fdcb..6e2c863 100644 --- a/include/boost/describe/class.hpp +++ b/include/boost/describe/class.hpp @@ -12,9 +12,13 @@ #define BOOST_DESCRIBE_CLASS(C, Bases, Public, Protected, Private) #define BOOST_DESCRIBE_STRUCT(C, Bases, Members) +#define BOOST_DESCRIBE_CLASS_CONSTRUCTORS(C, ...) +#define BOOST_DESCRIBE_STRUCT_CONSTRUCTORS(C, ...) + #else #include +#include #include #include @@ -59,6 +63,10 @@ namespace describe BOOST_DESCRIBE_MAYBE_UNUSED friend BOOST_DESCRIBE_PROTECTED_MEMBERS_(C BOOST_DESCRIBE_PP_UNPACK Protected) \ BOOST_DESCRIBE_MAYBE_UNUSED friend BOOST_DESCRIBE_PRIVATE_MEMBERS_(C BOOST_DESCRIBE_PP_UNPACK Private) +#define BOOST_DESCRIBE_CLASS_CONSTRUCTORS(C, ...) \ + template friend struct ::boost::describe::detail::ctor_descriptor; \ + friend BOOST_DESCRIBE_CTORS(C, __VA_ARGS__) + #define BOOST_DESCRIBE_STRUCT(C, Bases, Members) \ static_assert(std::is_class::value || std::is_union::value, "BOOST_DESCRIBE_STRUCT should only be used with class types"); \ BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_BASES_(C BOOST_DESCRIBE_PP_UNPACK Bases) \ @@ -66,8 +74,13 @@ namespace describe BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_PROTECTED_MEMBERS_(C) \ BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_PRIVATE_MEMBERS_(C) +#define BOOST_DESCRIBE_STRUCT_CONSTRUCTORS(C, ...) \ + BOOST_DESCRIBE_CTORS(C, __VA_ARGS__) + #endif + + } // namespace describe } // namespace boost diff --git a/include/boost/describe/constructor.hpp b/include/boost/describe/constructor.hpp new file mode 100644 index 0000000..8c6404c --- /dev/null +++ b/include/boost/describe/constructor.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_DESCRIBE_CONSTRUCTOR_HPP_INCLUDED +#define BOOST_DESCRIBE_CONSTRUCTOR_HPP_INCLUDED + +#include +#include +#include + + +#if defined(BOOST_DESCRIBE_CXX11) + +#include +#include + +namespace boost +{ +namespace describe +{ +namespace detail +{ + +template using _describe_ctors = decltype( boost_ctor_descriptor_fn( static_cast(0) ) ); + +template struct has_describe_ctors: std::false_type +{ +}; + +template struct has_describe_ctors>>: std::true_type +{ +}; + +} + +template using describe_constructors = detail::_describe_ctors; + +template using has_describe_constructors = detail::has_describe_ctors; + +} +} + +#endif // !defined(BOOST_DESCRIBE_CXX11) + +#endif //BOOST_DESCRIBE_CONSTRUCTOR_HPP_INCLUDED diff --git a/include/boost/describe/detail/constructor.hpp b/include/boost/describe/detail/constructor.hpp new file mode 100644 index 0000000..cf1e7d8 --- /dev/null +++ b/include/boost/describe/detail/constructor.hpp @@ -0,0 +1,62 @@ +// +// Copyright (c) 2022 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_DESCRIBE_DETAIL_CONSTRUCTOR_HPP_INCLUDED +#define BOOST_DESCRIBE_DETAIL_CONSTRUCTOR_HPP_INCLUDED + +#include +#include + +namespace boost +{ +namespace describe +{ +namespace detail +{ + +template +struct ctor_descriptor; + +template +struct ctor_descriptor +{ + using signature = C(Args...); + static constexpr unsigned modifiers = mod_constructor; + static constexpr bool is_noexcept = std::is_nothrow_constructible::value; + static constexpr bool is_trivial = std::is_trivially_constructible::value; + + constexpr static C* construct_at(C* p, Args... args) + { + return new (p) C(static_cast(args)...); + } +}; + +template auto ctor_descriptor_fn_impl( int, T... ) +{ + return list(); +} + +#define BOOST_DESCRIBE_CTOR_IMPL(C, B) , boost::describe::detail::ctor_descriptor() + + +#if defined(_MSC_VER) && !defined(__clang__) + +#define BOOST_DESCRIBE_CTORS(C, ...) inline auto boost_ctor_descriptor_fn( C** ) \ +{ return boost::describe::detail::ctor_descriptor_fn_impl( 0 BOOST_DESCRIBE_PP_FOR_EACH(BOOST_DESCRIBE_CTOR_IMPL, C, __VA_ARGS__) ); } + +#else + +#define BOOST_DESCRIBE_CTORS(C, ...) inline auto boost_ctor_descriptor_fn( C** ) \ +{ return boost::describe::detail::ctor_descriptor_fn_impl( 0 BOOST_DESCRIBE_PP_FOR_EACH(BOOST_DESCRIBE_CTOR_IMPL, C, ##__VA_ARGS__) ); } + +#endif + +} +} +} + +#endif //BOOST_DESCRIBE_DETAIL_CONSTRUCTOR_HPP_INCLUDED diff --git a/include/boost/describe/detail/members.hpp b/include/boost/describe/detail/members.hpp index 8be5387..e9a0637 100644 --- a/include/boost/describe/detail/members.hpp +++ b/include/boost/describe/detail/members.hpp @@ -49,10 +49,14 @@ template auto member_descriptor_fn_impl( int, T... ) template constexpr auto mfn( F C::* p ) { return p; } template constexpr auto mfn( F * p ) { return p; } -#define BOOST_DESCRIBE_MEMBER_IMPL(C, m) , []{ struct _boost_desc { \ +#define BOOST_DESCRIBE_MEMBER_IMPL_(C, m) , []{ struct _boost_desc { \ static constexpr auto pointer() noexcept { return BOOST_DESCRIBE_PP_POINTER(C, m); } \ static constexpr auto name() noexcept { return BOOST_DESCRIBE_PP_NAME(m); } }; return _boost_desc(); }() + +#define BOOST_DESCRIBE_MEMBER_IMPL(C, m) \ + BOOST_DESCRIBE_MEMBER_IMPL_(C, m) + #if defined(_MSC_VER) && !defined(__clang__) #define BOOST_DESCRIBE_PUBLIC_MEMBERS(C, ...) inline auto boost_public_member_descriptor_fn( C** ) \ diff --git a/include/boost/describe/modifiers.hpp b/include/boost/describe/modifiers.hpp index 06650ea..b9f2537 100644 --- a/include/boost/describe/modifiers.hpp +++ b/include/boost/describe/modifiers.hpp @@ -22,7 +22,8 @@ enum modifiers mod_function = 32, mod_any_member = 64, mod_inherited = 128, - mod_hidden = 256 + mod_hidden = 256, + mod_constructor = 512 }; BOOST_DESCRIBE_CONSTEXPR_OR_CONST modifiers mod_any_access = static_cast( mod_public | mod_protected | mod_private ); diff --git a/test/Jamfile b/test/Jamfile index 194d86b..f6a7228 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -85,6 +85,8 @@ run pedantic_members_test.cpp run enum_from_string_test2.cpp ; +run constructor_test.cpp ; + # examples obj describe_cxx14 : describe_cxx14.cpp ; diff --git a/test/constructor_test.cpp b/test/constructor_test.cpp new file mode 100644 index 0000000..390472d --- /dev/null +++ b/test/constructor_test.cpp @@ -0,0 +1,88 @@ +// Copyright 2020 Peter Dimov +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include + +class X +{ +private: + + std::pair p_; + + + X() : p_(0,0) {} +public: + X(int x, int y) noexcept : p_(x, y) {} + X(const std::pair & p) : p_(p) {} + BOOST_DESCRIBE_CLASS(X, (), (), (), (p_)); + BOOST_DESCRIBE_CLASS_CONSTRUCTORS(X, (), (int, int), (const std::pair&)); + + int x() {return p_.first; } + int y() {return p_.second; } +}; + + +#if !defined(BOOST_DESCRIBE_CXX14) + +#include + +BOOST_PRAGMA_MESSAGE("Skipping test because C++14 is not available") +int main() {} + +#else + +#include + + +int main() +{ + using namespace boost::describe; + using namespace boost::mp11; + + { + + X x(1, 2); + BOOST_TEST_EQ(x.x(), 1); + BOOST_TEST_EQ(x.y(), 2); + using L1 = describe_constructors; + + BOOST_TEST_EQ( mp_size::value, 3 ); + + using C1 = mp_at_c; + BOOST_TEST( (std::is_same::value) ); + BOOST_TEST_EQ( C1::modifiers, mod_constructor ); + BOOST_TEST_EQ( C1::is_noexcept, false ); + BOOST_TEST_EQ( C1::is_trivial, false ); + BOOST_TEST_EQ(&x, C1::construct_at(&x)); + BOOST_TEST_EQ(x.x(), 0); + BOOST_TEST_EQ(x.y(), 0); + + using C2 = mp_at_c; + BOOST_TEST( (std::is_same::value) ); + BOOST_TEST_EQ( C2::modifiers, mod_constructor ); + BOOST_TEST_EQ( C2::is_noexcept, true ); + BOOST_TEST_EQ( C2::is_trivial, false ); + BOOST_TEST_EQ(&x, C2::construct_at(&x, 3, 4)); + BOOST_TEST_EQ(x.x(), 3); + BOOST_TEST_EQ(x.y(), 4); + + using C3 = mp_at_c; + BOOST_TEST( (std::is_same &) >::value) ); + BOOST_TEST_EQ( C3::modifiers, mod_constructor ); + BOOST_TEST_EQ( C3::is_noexcept, false ); + BOOST_TEST_EQ( C3::is_trivial, false ); + BOOST_TEST_EQ(&x, C3::construct_at(&x, {5,6})); + BOOST_TEST_EQ(x.x(), 5); + BOOST_TEST_EQ(x.y(), 6); + + } + + return boost::report_errors(); +} + +#endif // !defined(BOOST_DESCRIBE_CXX14)