Skip to content

Commit

Permalink
[Struct] Add the BOOST_HANA_ADAPT_ADT macro
Browse files Browse the repository at this point in the history
Also
- Reorganize adaptation macro unit tests
- Add BOOST_HANA_PP_DROP_FRONT and BOOST_HANA_PP_FRONT macros
  • Loading branch information
ldionne committed Sep 22, 2015
1 parent df7bdf5 commit 4293490
Show file tree
Hide file tree
Showing 17 changed files with 3,803 additions and 2,290 deletions.
15 changes: 11 additions & 4 deletions doc/tutorial.md
Expand Up @@ -1598,12 +1598,19 @@ The `BOOST_HANA_ADAPT_STRUCT` macro must be used at global scope.

The effect is exactly the same as with the `BOOST_HANA_DEFINE_STRUCT` macro,
except you do not need to modify the type you want to adapt, which is
sometimes useful. Before we move on to a concrete example of using these
sometimes useful. Finally, it is also possible to define custom accessors
by using the `BOOST_HANA_ADAPT_ADT` macro:

@snippet example/tutorial/introspection.adapt.cpp BOOST_HANA_ADAPT_ADT

This way, the names used to access the members of the `Struct` will be those
specified, and the associated function will be called on the `Struct` when
retrieving that member. Before we move on to a concrete example of using these
introspection features, it should also be mentioned that `struct`s can be
adapted without using macros. This advanced interface for defining `Struct`s
can be used to specify keys that are not compile-time strings and to specify
custom accessors for the members of the structure. The advanced interface is
described in the documentation for the `Struct` concept.
can be used for example to specify keys that are not compile-time strings.
The advanced interface is described in the documentation of the `Struct`
concept.


@subsection tutorial-introspection-json Example: generating JSON
Expand Down
69 changes: 69 additions & 0 deletions example/adapt_adt.cpp
@@ -0,0 +1,69 @@
/*
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#include <boost/hana/accessors.hpp>
#include <boost/hana/adapt_adt.hpp>
#include <boost/hana/assert.hpp>
#include <boost/hana/core/convert.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/find.hpp>
#include <boost/hana/first.hpp>
#include <boost/hana/map.hpp>
#include <boost/hana/not_equal.hpp>
#include <boost/hana/optional.hpp>
#include <boost/hana/pair.hpp>
#include <boost/hana/string.hpp>
#include <boost/hana/transform.hpp>
#include <boost/hana/tuple.hpp>

#include <string>
namespace hana = boost::hana;


namespace ns {
struct Person {
explicit Person(std::string const& name, int age)
: name_(name), age_(age)
{ }
std::string const& get_name() const { return name_; }
int get_age() const { return age_; }

private:
std::string name_;
int age_;
};
}

BOOST_HANA_ADAPT_ADT(ns::Person,
(name, [](ns::Person const& p) { return p.get_name(); }),
(age, [](ns::Person const& p) { return p.get_age(); })
);

// The member names are hana::strings:
auto names = hana::transform(hana::accessors<ns::Person>(), hana::first);
BOOST_HANA_CONSTANT_CHECK(
names == hana::make_tuple(BOOST_HANA_STRING("name"), BOOST_HANA_STRING("age"))
);

int main() {
ns::Person john{"John", 30}, bob{"Bob", 40};
BOOST_HANA_RUNTIME_CHECK(hana::equal(john, john));
BOOST_HANA_RUNTIME_CHECK(hana::not_equal(john, bob));

BOOST_HANA_RUNTIME_CHECK(hana::find(john, BOOST_HANA_STRING("name")) == hana::just("John"));
BOOST_HANA_RUNTIME_CHECK(hana::find(john, BOOST_HANA_STRING("age")) == hana::just(30));
BOOST_HANA_CONSTANT_CHECK(hana::find(john, BOOST_HANA_STRING("foo")) == hana::nothing);

BOOST_HANA_RUNTIME_CHECK(hana::to<hana::tuple_tag>(john) == hana::make_tuple(
hana::make_pair(BOOST_HANA_STRING("name"), "John"),
hana::make_pair(BOOST_HANA_STRING("age"), 30)
));

BOOST_HANA_RUNTIME_CHECK(hana::to<hana::map_tag>(john) == hana::make_map(
hana::make_pair(BOOST_HANA_STRING("name"), "John"),
hana::make_pair(BOOST_HANA_STRING("age"), 30)
));
}
15 changes: 15 additions & 0 deletions example/tutorial/introspection.adapt.cpp
Expand Up @@ -85,3 +85,18 @@ namespace not_my_namespace {

BOOST_HANA_ADAPT_STRUCT(not_my_namespace::Person, name, age);
//! [BOOST_HANA_ADAPT_STRUCT]


//! [BOOST_HANA_ADAPT_ADT]
namespace also_not_my_namespace {
struct Person {
std::string get_name();
int get_age();
};
}

BOOST_HANA_ADAPT_ADT(also_not_my_namespace::Person,
(name, [](auto const& p) { return p.get_name(); }),
(age, [](auto const& p) { return p.get_age(); })
);
//! [BOOST_HANA_ADAPT_ADT]
1 change: 1 addition & 0 deletions include/boost/hana.hpp
Expand Up @@ -50,6 +50,7 @@ namespace boost { namespace hana {
#include <boost/hana/config.hpp>

#include <boost/hana/accessors.hpp>
#include <boost/hana/adapt_adt.hpp>
#include <boost/hana/adapt_struct.hpp>
#include <boost/hana/adjust.hpp>
#include <boost/hana/adjust_if.hpp>
Expand Down
17 changes: 17 additions & 0 deletions include/boost/hana/adapt_adt.hpp
@@ -0,0 +1,17 @@
/*!
@file
Defines the `BOOST_HANA_ADAPT_ADT` macro.
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/

#ifndef BOOST_HANA_ADAPT_ADT_HPP
#define BOOST_HANA_ADAPT_ADT_HPP

#include <boost/hana/fwd/adapt_adt.hpp>

#include <boost/hana/detail/struct_macros.hpp>

#endif // !BOOST_HANA_ADAPT_ADT_HPP
11 changes: 11 additions & 0 deletions include/boost/hana/detail/preprocessor.hpp
Expand Up @@ -92,4 +92,15 @@ Distributed under the Boost Software License, Version 1.0.
#define BOOST_HANA_PP_DROP_BACK_IMPL_19(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18) e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17
#define BOOST_HANA_PP_DROP_BACK_IMPL_20(e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19) e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18

//! @ingroup group-details
//! Expands to its first argument.
#define BOOST_HANA_PP_FRONT(...) BOOST_HANA_PP_FRONT_IMPL(__VA_ARGS__, )
#define BOOST_HANA_PP_FRONT_IMPL(e0, ...) e0

//! @ingroup group-details
//! Expands to all of its arguments, except for the first one.
//!
//! This macro may not be called with less than 2 arguments.
#define BOOST_HANA_PP_DROP_FRONT(e0, ...) __VA_ARGS__

#endif // !BOOST_HANA_DETAIL_PREPROCESSOR_HPP
147 changes: 79 additions & 68 deletions include/boost/hana/detail/struct_macros.erb.hpp
Expand Up @@ -20,7 +20,8 @@

/*!
@file
Defines the `BOOST_HANA_DEFINE_STRUCT` and the `BOOST_HANA_ADAPT_STRUCT` macros.
Defines the `BOOST_HANA_DEFINE_STRUCT`, `BOOST_HANA_ADAPT_STRUCT`, and
`BOOST_HANA_ADAPT_ADT` macros.
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
Expand Down Expand Up @@ -73,12 +74,12 @@ namespace boost { namespace hana { namespace struct_detail {
}
}}}

template <typename ...>
struct BOOST_HANA_ADAPT_STRUCT_must_be_called_in_the_global_namespace;

//////////////////////////////////////////////////////////////////////////////
// BOOST_HANA_ADAPT_STRUCT
//////////////////////////////////////////////////////////////////////////////
template <typename ...>
struct BOOST_HANA_ADAPT_STRUCT_must_be_called_in_the_global_namespace;

#define BOOST_HANA_ADAPT_STRUCT(...) \
template <> \
struct BOOST_HANA_ADAPT_STRUCT_must_be_called_in_the_global_namespace<>; \
Expand All @@ -89,43 +90,64 @@ struct BOOST_HANA_ADAPT_STRUCT_must_be_called_in_the_global_namespace;
#define BOOST_HANA_ADAPT_STRUCT_IMPL(N, ...) \
BOOST_HANA_PP_CONCAT(BOOST_HANA_ADAPT_STRUCT_IMPL_, N)(__VA_ARGS__)

#define BOOST_HANA_MEMBER_PAIR_IMPL(TYPE, i, MEMBER) \
::boost::hana::make_pair( \
::boost::hana::struct_detail::prepare_member_name<i, member_names>(), \
::boost::hana::struct_detail::member_ptr< \
decltype(&TYPE::MEMBER), &TYPE::MEMBER \
>{} \
) \
<% (0..MAX_NUMBER_OF_MEMBERS).each do |n| %>
#define BOOST_HANA_ADAPT_STRUCT_IMPL_<%= n+1 %>(TYPE <%= (1..n).map { |i| ", m#{i}" }.join %>) \
namespace boost { namespace hana { \
template <> \
struct accessors_impl<TYPE> { \
static constexpr auto apply() { \
struct member_names { \
static constexpr auto get() { \
return ::boost::hana::make_tuple( \
<%= (1..n).map { |i| "BOOST_HANA_PP_STRINGIZE(m#{i})" }.join(', ') %> \
); \
} \
}; \
return ::boost::hana::make_tuple( \
<%= (1..n).map { |i| "::boost::hana::make_pair(::boost::hana::struct_detail::prepare_member_name<#{i-1}, member_names>(), ::boost::hana::struct_detail::member_ptr<decltype(&TYPE::m#{i}), &TYPE::m#{i}>{})" }.join(', ') %>\
); \
} \
}; \
}} \
/**/
<% end %>

<% (0..MAX_NUMBER_OF_MEMBERS).each do |n|
members = (1..n).to_a.map { |i| "m#{i}" }
args = ["TYPE"] + members
member_pairs = members.map.with_index { |member, i|
"BOOST_HANA_MEMBER_PAIR_IMPL(TYPE, #{i}, #{member})"
}
member_names = members.map { |member|
"BOOST_HANA_PP_STRINGIZE(#{member})"
}
%>
#define BOOST_HANA_ADAPT_STRUCT_IMPL_<%= n+1 %>(<%= args.join(', ') %>) \
namespace boost { namespace hana { \
template <> \
struct accessors_impl<TYPE> { \
static constexpr auto apply() { \
struct member_names { \
static constexpr auto get() { \
return ::boost::hana::make_tuple( \
<%= member_names.join(', ') %> \
); \
} \
}; \
return ::boost::hana::make_tuple( \
<%= member_pairs.join(', ') %> \
); \
} \
}; \
}} \
//////////////////////////////////////////////////////////////////////////////
// BOOST_HANA_ADAPT_ADT
//////////////////////////////////////////////////////////////////////////////
template <typename ...>
struct BOOST_HANA_ADAPT_ADT_must_be_called_in_the_global_namespace;

#define BOOST_HANA_ADAPT_ADT(...) \
template <> \
struct BOOST_HANA_ADAPT_ADT_must_be_called_in_the_global_namespace<>; \
BOOST_HANA_ADAPT_ADT_IMPL(BOOST_HANA_PP_NARG(__VA_ARGS__), __VA_ARGS__) \
static_assert(true, "force the usage of a trailing semicolon") \
/**/

#define BOOST_HANA_ADAPT_ADT_IMPL(N, ...) \
BOOST_HANA_PP_CONCAT(BOOST_HANA_ADAPT_ADT_IMPL_, N)(__VA_ARGS__)

<% (0..MAX_NUMBER_OF_MEMBERS).each do |n| %>
#define BOOST_HANA_ADAPT_ADT_IMPL_<%= n+1 %>(TYPE <%= (1..n).map { |i| ", m#{i}" }.join %>) \
namespace boost { namespace hana { \
template <> \
struct accessors_impl<TYPE> { \
template <typename ...> \
static constexpr auto apply() { \
struct member_names { \
static constexpr auto get() { \
return ::boost::hana::make_tuple( \
<%= (1..n).map { |i| "BOOST_HANA_PP_STRINGIZE(BOOST_HANA_PP_FRONT m#{i})" }.join(', ') %>\
); \
} \
}; \
return ::boost::hana::make_tuple( \
<%= (1..n).map { |i| "::boost::hana::make_pair(::boost::hana::struct_detail::prepare_member_name<#{i-1}, member_names>(), BOOST_HANA_PP_DROP_FRONT m#{i})" }.join(', ') %>\
); \
} \
}; \
}} \
/**/
<% end %>

Expand All @@ -138,35 +160,24 @@ struct BOOST_HANA_ADAPT_STRUCT_must_be_called_in_the_global_namespace;
#define BOOST_HANA_DEFINE_STRUCT_IMPL(N, ...) \
BOOST_HANA_PP_CONCAT(BOOST_HANA_DEFINE_STRUCT_IMPL_, N)(__VA_ARGS__)

<% (0..MAX_NUMBER_OF_MEMBERS).each do |n|
members = (1..n).to_a.map { |i| "m#{i}" }
args = ["TYPE"] + members
member_pairs = members.map.with_index { |member, i|
"BOOST_HANA_MEMBER_PAIR_IMPL(TYPE, #{i}, BOOST_HANA_PP_BACK #{member})"
}
member_decls = members.map { |member|
"BOOST_HANA_PP_DROP_BACK #{member} BOOST_HANA_PP_BACK #{member};"
}
member_names = members.map { |member|
"BOOST_HANA_PP_STRINGIZE(BOOST_HANA_PP_BACK #{member})"
}
%>
#define BOOST_HANA_DEFINE_STRUCT_IMPL_<%= n+1 %>(<%= args.join(', ') %> ) \
<%= member_decls.join(' ') %> \
struct hana_accessors_impl { \
static constexpr auto apply() { \
struct member_names { \
static constexpr auto get() { \
return ::boost::hana::make_tuple( \
<%= member_names.join(', ') %> \
); \
} \
}; \
return ::boost::hana::make_tuple( \
<%= member_pairs.join(", ") %> \
); \
} \
} \
<% (0..MAX_NUMBER_OF_MEMBERS).each do |n| %>
#define BOOST_HANA_DEFINE_STRUCT_IMPL_<%= n+1 %>(TYPE <%= (1..n).map { |i| ", m#{i}" }.join %>) \
<%= (1..n).map { |i| "BOOST_HANA_PP_DROP_BACK m#{i} BOOST_HANA_PP_BACK m#{i};" }.join(' ') %> \
\
struct hana_accessors_impl { \
static constexpr auto apply() { \
struct member_names { \
static constexpr auto get() { \
return ::boost::hana::make_tuple( \
<%= (1..n).map { |i| "BOOST_HANA_PP_STRINGIZE(BOOST_HANA_PP_BACK m#{i})" }.join(', ') %>\
); \
} \
}; \
return ::boost::hana::make_tuple( \
<%= (1..n).map { |i| "::boost::hana::make_pair(::boost::hana::struct_detail::prepare_member_name<#{i-1}, member_names>(), ::boost::hana::struct_detail::member_ptr<decltype(&TYPE::BOOST_HANA_PP_BACK m#{i}), &TYPE::BOOST_HANA_PP_BACK m#{i}>{})" }.join(', ') %>\
); \
} \
} \
/**/
<% end %>

Expand Down

0 comments on commit 4293490

Please sign in to comment.