Skip to content

Commit

Permalink
Added constructor descriptors.
Browse files Browse the repository at this point in the history
  • Loading branch information
klemens-morgenstern committed Oct 27, 2022
1 parent a155702 commit 66b75c1
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 2 deletions.
52 changes: 52 additions & 0 deletions doc/describe/classes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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<X>`, 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.
1 change: 1 addition & 0 deletions include/boost/describe.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <boost/describe/enumerators.hpp>
#include <boost/describe/bases.hpp>
#include <boost/describe/constructor.hpp>
#include <boost/describe/members.hpp>
#include <boost/describe/enum.hpp>
#include <boost/describe/class.hpp>
Expand Down
13 changes: 13 additions & 0 deletions include/boost/describe/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <boost/describe/detail/bases.hpp>
#include <boost/describe/detail/constructor.hpp>
#include <boost/describe/detail/members.hpp>
#include <type_traits>

Expand Down Expand Up @@ -59,15 +63,24 @@ 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<typename S> 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<C>::value || std::is_union<C>::value, "BOOST_DESCRIBE_STRUCT should only be used with class types"); \
BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_BASES_(C BOOST_DESCRIBE_PP_UNPACK Bases) \
BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_PUBLIC_MEMBERS_(C BOOST_DESCRIBE_PP_UNPACK Members) \
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

Expand Down
49 changes: 49 additions & 0 deletions include/boost/describe/constructor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// Copyright (c) 2022 Klemens Morgenstern ([email protected])
//
// 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 <boost/describe/modifiers.hpp>
#include <boost/describe/detail/void_t.hpp>
#include <boost/describe/detail/config.hpp>


#if defined(BOOST_DESCRIBE_CXX11)

#include <boost/mp11/algorithm.hpp>
#include <type_traits>

namespace boost
{
namespace describe
{
namespace detail
{

template<class T> using _describe_ctors = decltype( boost_ctor_descriptor_fn( static_cast<T**>(0) ) );

template<class T, class En = void> struct has_describe_ctors: std::false_type
{
};

template<class T> struct has_describe_ctors<T, void_t<_describe_ctors<T>>>: std::true_type
{
};

}

template<class T> using describe_constructors = detail::_describe_ctors<T>;

template<class T> using has_describe_constructors = detail::has_describe_ctors<T>;

}
}

#endif // !defined(BOOST_DESCRIBE_CXX11)

#endif //BOOST_DESCRIBE_CONSTRUCTOR_HPP_INCLUDED
62 changes: 62 additions & 0 deletions include/boost/describe/detail/constructor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// Copyright (c) 2022 Klemens Morgenstern ([email protected])
//
// 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 <boost/describe/modifiers.hpp>
#include <type_traits>

namespace boost
{
namespace describe
{
namespace detail
{

template<class S>
struct ctor_descriptor;

template<class C, class ... Args>
struct ctor_descriptor<C(Args...)>
{
using signature = C(Args...);
static constexpr unsigned modifiers = mod_constructor;
static constexpr bool is_noexcept = std::is_nothrow_constructible<C, Args...>::value;
static constexpr bool is_trivial = std::is_trivially_constructible<C, Args...>::value;

constexpr static C* construct_at(C* p, Args... args)
{
return new (p) C(static_cast<Args>(args)...);
}
};

template<class... T> auto ctor_descriptor_fn_impl( int, T... )
{
return list<T...>();
}

#define BOOST_DESCRIBE_CTOR_IMPL(C, B) , boost::describe::detail::ctor_descriptor<C B>()


#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
6 changes: 5 additions & 1 deletion include/boost/describe/detail/members.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ template<unsigned M, class... T> auto member_descriptor_fn_impl( int, T... )
template<class C, class F> constexpr auto mfn( F C::* p ) { return p; }
template<class C, class F> 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** ) \
Expand Down
4 changes: 3 additions & 1 deletion include/boost/describe/modifiers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum modifiers
mod_any_member = 64,
mod_inherited = 128,
mod_hidden = 256,
mod_constructor = 512
};

BOOST_DESCRIBE_ENUM(modifiers,
Expand All @@ -34,7 +35,8 @@ BOOST_DESCRIBE_ENUM(modifiers,
mod_function,
mod_any_member,
mod_inherited,
mod_hidden)
mod_hidden,
mod_constructor)

BOOST_DESCRIBE_CONSTEXPR_OR_CONST modifiers mod_any_access = static_cast<modifiers>( mod_public | mod_protected | mod_private );

Expand Down
2 changes: 2 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ compile unnamed_namespace_test2.cpp ;
run union_test.cpp ;
run union_test2.cpp ;

run constructor_test.cpp ;

# examples

obj describe_cxx14 : describe_cxx14.cpp ;
Expand Down
88 changes: 88 additions & 0 deletions test/constructor_test.cpp
Original file line number Diff line number Diff line change
@@ -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 <boost/describe/members.hpp>
#include <boost/describe/constructor.hpp>
#include <boost/describe/class.hpp>
#include <boost/core/lightweight_test.hpp>
#include <utility>

class X
{
private:

std::pair<int, int> p_;


X() : p_(0,0) {}
public:
X(int x, int y) noexcept : p_(x, y) {}
X(const std::pair<int, int> & p) : p_(p) {}
BOOST_DESCRIBE_CLASS(X, (), (), (), (p_));
BOOST_DESCRIBE_CLASS_CONSTRUCTORS(X, (), (int, int), (const std::pair<int, int>&));

int x() {return p_.first; }
int y() {return p_.second; }
};


#if !defined(BOOST_DESCRIBE_CXX14)

#include <boost/config/pragma_message.hpp>

BOOST_PRAGMA_MESSAGE("Skipping test because C++14 is not available")
int main() {}

#else

#include <boost/mp11.hpp>


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<X>;

BOOST_TEST_EQ( mp_size<L1>::value, 3 );

using C1 = mp_at_c<L1, 0>;
BOOST_TEST( (std::is_same<C1::signature, X() >::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<L1, 1>;
BOOST_TEST( (std::is_same<C2::signature, X(int, int) >::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<L1, 2>;
BOOST_TEST( (std::is_same<C3::signature, X(const std::pair<int, int> &) >::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)

0 comments on commit 66b75c1

Please sign in to comment.