Created
June 15, 2021 08:25
-
-
Save sdebionne/af4e442eebef11638f2808b930b8508f to your computer and use it in GitHub Desktop.
Boost.Describe - Printing complexe data structure with annotations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright(C) 2021 Samuel Debionne, ESRF. | |
// Use, modification and distribution is subject to 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) | |
#include <type_traits> | |
#include <boost/describe.hpp> | |
#include <boost/mp11.hpp> | |
#include <boost/callable_traits/class_of.hpp> | |
#include <boost/callable_traits/return_type.hpp> | |
namespace boost | |
{ | |
namespace describe | |
{ | |
// descriptor_by_pointer<> | |
namespace detail | |
{ | |
template <auto pointer> | |
struct by_pointer | |
{ | |
template <typename D, typename T1, typename T2> | |
struct equal { static constexpr bool value = false; }; | |
template <typename D, typename T> | |
struct equal<D, T, T> { static constexpr bool value = (pointer == D::pointer); }; | |
template <typename D> | |
struct fn | |
{ | |
static constexpr bool value = | |
equal<D, std::decay_t<decltype(pointer)>, std::decay_t<decltype(D::pointer)>>::value; | |
}; | |
}; | |
} //namespace detail | |
template <typename Md, auto pointer, typename P = detail::by_pointer<pointer>> | |
using descriptor_by_pointer = mp11::mp_at<Md, mp11::mp_find_if_q<Md, P>>; | |
// is_described<> | |
template <typename T, typename Enable = void> | |
struct is_described : std::false_type {}; | |
template <typename T> | |
struct is_described<T, std::void_t<describe_members<T, mod_any_access>>> : std::true_type {}; | |
template <class T> | |
inline constexpr bool is_described_v = is_described<T>::value; | |
} //namespace describe | |
} //namespace boost | |
template <auto pointer> | |
using descriptor_by_pointer = boost::describe::descriptor_by_pointer< | |
boost::describe::describe_members<boost::callable_traits::class_of_t<decltype(pointer)>, | |
boost::describe::mod_any_access>, | |
pointer>; | |
/// Annotations of struct members | |
/// | |
/// \tparam Md Member Descriptor | |
template <typename Md> | |
struct annotations; | |
#define APP_ANNOTATE(ptm, arg_desc, arg_doc, arg_validate) \ | |
template <> \ | |
struct annotations<descriptor_by_pointer<ptm>> \ | |
{ \ | |
inline static const char* desc = (arg_desc); \ | |
inline static const char* doc = (arg_doc); \ | |
\ | |
inline static auto validate = (arg_validate); \ | |
}; | |
namespace app | |
{ | |
template <typename T> | |
struct point | |
{ | |
T x; | |
T y; | |
}; | |
template <typename T> | |
struct rectangle | |
{ | |
point<T> topleft; | |
point<T> dimensions; | |
}; | |
BOOST_DESCRIBE_STRUCT(rectangle<std::ptrdiff_t>, (), (topleft, dimensions)) | |
enum class acq_mode_enum : int | |
{ | |
normal, //!< Single image | |
accumulation //!< Multiple image accumulated (over time) | |
}; | |
BOOST_DESCRIBE_ENUM(acq_mode_enum, normal, accumulation) | |
struct acquisition | |
{ | |
int nb_frames = 1; // POD | |
std::chrono::duration<int> expo_time = std::chrono::seconds(1); // Class | |
acq_mode_enum acq_mode = acq_mode_enum::normal; // Enumerator | |
rectangle<std::ptrdiff_t> roi; // Described class | |
}; | |
BOOST_DESCRIBE_STRUCT(acquisition, (), (nb_frames, expo_time, acq_mode, roi)) | |
} //namespace app | |
APP_ANNOTATE(&app::acquisition::nb_frames, "number of frames", | |
"The number of frames to acquire (0 = continuous acquisition)", | |
[](auto const& val) { return val > 0; }) | |
APP_ANNOTATE(&app::acquisition::expo_time, "exposure time", "The exposure time [s]", | |
[](auto const& val) { return val > 0; }) | |
APP_ANNOTATE(&app::acquisition::acq_mode, "acquisition mode", | |
"The acquistion mode [normal, accumulation]", [](auto const& val) { return true; }) | |
APP_ANNOTATE(&app::acquisition::roi, "region of interest", "The region of interest to transfer", | |
[](auto const& val) { return val.validate(); }) | |
APP_ANNOTATE(&app::rectangle<std::ptrdiff_t>::topleft, "top left corner coordinate", | |
"The top left corner coordinate of the region of interest to transfer", | |
[](auto const& val) { return val.validate(); }) | |
APP_ANNOTATE(&app::rectangle<std::ptrdiff_t>::dimensions, "dimensions", | |
"The dimensions of the region of interest to transfer", | |
[](auto const& val) { return val.validate(); }) | |
// Insert stream operator for std::chrono::duration | |
template <typename Rep> | |
std::ostream& operator<<(std::ostream& os, std::chrono::duration<Rep> const& duration) | |
{ | |
os << duration.count() << "s"; | |
return os; | |
} | |
// Generic insert stream operator for enumerators | |
template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>> | |
std::ostream& operator<<(std::ostream& os, E const& e) | |
{ | |
char const* r = "(unnamed)"; | |
boost::mp11::mp_for_each<boost::describe::describe_enumerators<E>>([&](auto D) { | |
if (e == D.value) | |
os << D.name; | |
}); | |
return os; | |
} | |
// Insert stream operator for app structures | |
template <typename T> | |
std::ostream& operator<<(std::ostream& os, app::point<T> const& p) | |
{ | |
os << "{" << p.x << "," << p.x << "}"; | |
return os; | |
} | |
template <typename T> | |
std::ostream& operator<<(std::ostream& os, app::rectangle<T> const& r) | |
{ | |
os << "{" << r.topleft.x << "," << r.topleft.y << "}," | |
<< "{" << r.dimensions.x << "x" << r.dimensions.y << "}"; | |
return os; | |
} | |
// Recursively print described structures including annotations | |
template <class T, typename Md = boost::describe::describe_members<T, boost::describe::mod_any_access>> | |
void print_with_annotation(std::ostream& os, T const& t, int indent = 0) | |
{ | |
bool first = true; | |
boost::mp11::mp_for_each<Md>([&](auto D) { | |
for (int i = 0 ; i < indent ; i++) | |
os << " "; | |
using A = annotations<decltype(D)>; | |
os << "." << D.name << " = " << t.*D.pointer << "\n\tdesc: " << A::desc << "\n\tdoc: " << A::doc << std::endl; | |
// Recursively print class members if they are described | |
using return_t = std::decay_t<boost::callable_traits::return_type_t<decltype(D.pointer)>>; | |
if constexpr (std::is_class_v<return_t> && boost::describe::is_described_v<return_t>) | |
print_with_annotation(os, t.*D.pointer, ++indent); | |
}); | |
} | |
int main(int argc, const char* argv[]) | |
{ | |
using namespace std::chrono_literals; | |
using namespace app; | |
acquisition acq{100, 1s, acq_mode_enum::accumulation, {{0, 0}, {1024, 1024}}}; | |
print_with_annotation(std::cout, acq); | |
return 0; | |
} | |
//$ ./gist | |
//.nb_frames = 100 | |
// desc: number of frames | |
// doc: The number of frames to acquire (0 = continuous acquisition) | |
//.expo_time = 1s | |
// desc: exposure time | |
// doc: The exposure time [s] | |
//.acq_mode = accumulation | |
// desc: acquisition mode | |
// doc: The acquistion mode [normal, accumulation] | |
//.roi = {0,0},{1024x1024} | |
// desc: region of interest | |
// doc: The region of interest to transfer | |
// .topleft = {0,0} | |
// desc: top left corner coordinate | |
// doc: The top left corner coordinate of the region of interest to transfer | |
// .dimensions = {1024,1024} | |
// desc: dimensions | |
// doc: The dimensions of the region of interest to transfer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment