Git Product home page Git Product logo

describe's Introduction

Describe

A C++14 reflection library. Provides macros for describing enumerators and struct/class members, and primitives for querying this information. See the documentation for more information and usage examples.

Supported Compilers

  • GCC 5 or later with -std=c++14 or above
  • Clang 3.9 or later with -std=c++14 or above
  • Visual Studio 2015 or later

Tested on Github Actions and Appveyor.

License

Distributed under the Boost Software License, Version 1.0.

describe's People

Contributors

denzor200 avatar ecatmur avatar pdimov avatar romain-geissler-1a avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

describe's Issues

BOOST_DESCRIBE_ENUM fails above 24 enumerators

Started integrating Boost.Describe in my code generator (as discussed on the Boost ML).

Works on all my enums, except the largest with 27 enumerators. If I reduce the described enumerators to 24, then it's OK.
(I didn't change the enum, my code would not compile anymore, just omitted a few enumerators in the DESCRIBE macro).

Is there a preprocessor limit that needs to be increased?
Aren't native variadic macros unlimited in number of arguments?
What's one supposed to do in this case?

No mention to that limit in https://pdimov.github.io/describe/doc/html/describe.html#implementation_limitations

Trait for described types

Right now I can create overload for functions that will only participate if there is reflection for them with Hana (trait boost::hana::Struct<T>). It'd be nice if describe also have a trait for types that were "described".

Clang warnings for described internal types

If I describe an enum or class in an unnamed namespace, clang emits warnings, either (if the descriptor is not used)

warning: unused function 'boost_enum_descriptor_fn' [-Wunused-function]

or, if the descriptor is used:

warning: function 'boost_enum_descriptor_fn' is not needed and will not be emitted [-Wunneeded-internal-declaration]

Example: https://godbolt.org/z/qPrdcT5KG

#include <boost/describe.hpp>
namespace {
BOOST_DEFINE_ENUM(E, A);
class S { BOOST_DESCRIBE_CLASS(S, (), (), (), ()); };
}
using namespace boost::describe;
using D = describe_enumerators<E>;
using B = describe_bases<S, mod_any_access>;
using M = describe_members<S, mod_any_access>;

It looks like these can be suppressed with [[maybe_unused]]; I'll submit a PR.

Use describe to serialize/unserialize xml (xml_oarchive/xm_iarchive is too limited)

Hi,

I am using describe to serialize.unserialize some structs to json (thanks to boost::json) but now I would also be able to do it in xml.
I tried boost::archive::: (xml_oarchive/xml_iarchive) but the resulting xml always have some useless nodes ( <xxx_version>) that cannot be easily removed from what I understand.
Let's consider the following code:

namespace JsonSerializer
{
   struct Item
   {
      std::string id;
      std::string label;
      std::string iconFile;
   };
   BOOST_DESCRIBE_STRUCT(Item, (), (id, label, iconFile))

   struct Items
   {
      using Type = std::vector<Item>;
      Type items;
   };
   BOOST_DESCRIBE_STRUCT(Items, (), (items));

} // namespace JsonSerializer


// Lines below allows to not add bunch of ugly tags/info when serializing in xml
BOOST_CLASS_IMPLEMENTATION(JsonSerializer::Item, boost::serialization::object_serializable);
BOOST_CLASS_IMPLEMENTATION(JsonSerializer::Items::Type, boost::serialization::object_serializable);
BOOST_CLASS_IMPLEMENTATION(JsonSerializer::Items, boost::serialization::object_serializable);

When I try fill an Items object and I serialize here is the result:

Items items;
// ... some code to fill items list ...
std::ostringstream os;
unsigned int flags = boost::archive::no_header;
boost::archive::xml_oarchive ar(os, flags);
ar << boost::make_nvp("items", items.items);
std::string sXml = os.str();
<items>
	<count>1</count>
	<item_version>0</item_version>
	<item>
		<id>standalone</id>
		<label>StandAlone</label>
		<iconFile>Logo_StandAlone.png</iconFile>
	</item>
</items>

Even with all the ugly BOOST_CLASS_IMPLEMENTATION macros and the use of boost::archive::no_header, the result is not satisfying (useless item_version, count), so where should I start to get a clean xml ?

Universal Printing example fails with std::map<std::string, described_struct>

There is no op<< for std::map. The universal printing example requires one.

Trouble is, Boost.Describe seems to require it in the std namespace, except of course that's illegal to add to std,
except for specializations. (I'm no expect, but I know that one). I cannot share the exact code that fails for me,
since it's part of a large code-generated library, but essentially it boils down to what's below.

With it's illegal op<< for the instantiated map template for <string, My::Inner>, this builds fine on MSVC 2019, C++17.
I did not even try providing a templated one, to simply my testing.

I realize it's not the best of report, but hopefully you can see the big picture, and perhaps even prove me wrong via a simple example.

#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <boost/test/unit_test.hpp>

#include <ostream>
#include <sstream>

namespace My {

struct PseudoVariant { // not described
  int idx_;
  std::any val_;
  // idx() getter, bunch of set_Xyz and get_Xyz() typed accessor to val_'s allowed content...
};
struct Inner { // described
  PseudoVariant item_;
};
struct Outer { // described
  std::map<std::string, Inner> inners_;
  // other fields
};

// Dummy op<<(PseudoVariant) for test_2level_struct() specifically, printing just ints, not the other types
std::ostream& operator<<(std::ostream& os, const PseudoVariant & dv_item) {
    return os
        << "{" << "#" << dv_item.idx() << "="
        << ((dv_item.idx() == 2)? dv_item.get_int(): -1) << "}"
    ;
}

} // My

namespace std {

std::ostream& operator<<(std::ostream& os, const std::map<std::string, My::Inner>& map) {
    return os << My::to_string(map); // implemented elsewhere
}

} // std

namespace describe_utils {

using namespace boost::describe;

std::ostream& operator<<(std::ostream& os, const T& t) {
    os << '{';
    bool first = true;
    boost::mp11::mp_for_each<Bd>(
        [&](auto D) {
            if (!first) {
                os << ", ";
            }
            first = false;

            using B = typename decltype(D)::type;
            os << (B const&)t;
        }
    );
    boost::mp11::mp_for_each<Md>(
        [&](auto D) {
            if (!first ) {
                os << ", ";
            }
            first = false;

            os << '.' << D.name << " = " << t.*D.pointer;
        }
    );
    return os << '}';
}

} // namespace describe_utils

namespace {

template <typename T>
std::string as_string(const T& t) {
    using namespace describe_utils;
    std::ostringstream oss;
    oss << t;
    return oss.str();
}

BOOST_AUTO_TEST_SUITE(DescribeTests)

BOOST_AUTO_TEST_CASE(test_2level_struct) {
    using namespace describe_utils;
    using namespace My;

    Outer o;
    // fill o

    std::string sps = as_string(sp);
}

BOOST_AUTO_TEST_SUITE_END()

} // End anonymous namespace

Here's the error I get, if not using that illegal std op<<.
I put it in a different namespace, like the local anon one, or another in-scope, similar error each time.

1>------ Build started: Project: tests_describe, Configuration: Debug x64 ------
1>DescribeTests.cpp
1>D:\dev\src\tests\misc\DescribeTests.cpp(104,1): error C2679: binary '<<': no operator found which takes a right-hand operand of type 'const std::map<std::string,My::Inner,std::less<std::string>,std::allocator<std::pair<const std::string,My::Inner>>>' (or there is no acceptable conversion)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(466,39): message : could be 'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::basic_streambuf<char,std::char_traits<char>> *)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(441,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(const void *)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(423,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(long double)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(405,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(double)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(387,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(float)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(369,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(unsigned __int64)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(351,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(__int64)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(333,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(unsigned long)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(315,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(long)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(296,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(unsigned int)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(270,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(int)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(251,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(unsigned short)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(217,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(short)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(199,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(bool)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(194,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::ios_base &(__cdecl *)(std::ios_base &))'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(189,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::basic_ios<char,std::char_traits<char>> &(__cdecl *)(std::basic_ios<char,std::char_traits<char>> &))'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(184,39): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<(std::basic_ostream<char,std::char_traits<char>> &(__cdecl *)(std::basic_ostream<char,std::char_traits<char>> &))'
1>D:\dev\src\tests\misc\DescribeTests.cpp(31,15): message : or       'std::ostream &My::operator <<(std::ostream &,const My::PSeudoVariant &)' [found using argument-dependent lookup]
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(461,20): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::basic_ostream<char,std::char_traits<char>>::operator <<<void>(std::nullptr_t)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(654,32): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const char *)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(698,32): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(734,31): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const char *)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(779,31): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(896,31): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const signed char *)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(902,31): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,signed char)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(907,31): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const unsigned char *)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(913,31): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,unsigned char)'
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.27.29110\include\ostream(1001,32): message : or       'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const std::error_code &)'
1>D:\dev\src\tests\misc\DescribeTests.cpp(104,1): message : while trying to match the argument list '(std::basic_ostream<char,std::char_traits<char>>, const std::map<std::string,My::Inner,std::less<std::string>,std::allocator<std::pair<const std::string,My::Inner>>>)'
1>D:\pdgm\kits\trunk\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/algorithm.hpp(1037): message : see reference to function template instantiation 'auto describe_utils::<<::<lambda_734585ecef8cae37a0607e5d54dd865b>::operator ()<boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_bab1101c997e04563d84b2536558ac70>::()::D,1>>(boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_bab1101c997e04563d84b2536558ac70>::()::D,1>) const' being compiled
1>D:\pdgm\kits\trunk\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/algorithm.hpp(1055): message : see reference to function template instantiation 'F boost::mp11::detail::mp_for_each_impl<boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_88c58abe40ddca9e64b2955f6dbfa7ea>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_5916e84125bc784d483539ad1d3a3d09>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_c9d2335107088328dfd07a87925d597b>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_bab1101c997e04563d84b2536558ac70>::()::D,1>,_Ty>(boost::mp11::mp_list<boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_88c58abe40ddca9e64b2955f6dbfa7ea>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_5916e84125bc784d483539ad1d3a3d09>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_c9d2335107088328dfd07a87925d597b>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_bab1101c997e04563d84b2536558ac70>::()::D,1>>,F &&)' being compiled
1>        with
1>        [
1>            F=describe_utils::<<::<lambda_734585ecef8cae37a0607e5d54dd865b>,
1>            _Ty=describe_utils::<<::<lambda_734585ecef8cae37a0607e5d54dd865b>
1>        ]
1>D:\dev\src\tests\misc\DescribeTests.cpp(106): message : see reference to function template instantiation 'describe_utils::<<::<lambda_734585ecef8cae37a0607e5d54dd865b> boost::mp11::mp_for_each<Md,describe_utils::<<::<lambda_734585ecef8cae37a0607e5d54dd865b>>(F &&)' being compiled
1>        with
1>        [
1>            Md=boost::describe::detail::list<boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_88c58abe40ddca9e64b2955f6dbfa7ea>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_5916e84125bc784d483539ad1d3a3d09>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_c9d2335107088328dfd07a87925d597b>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_bab1101c997e04563d84b2536558ac70>::()::D,1>>,
1>            F=describe_utils::<<::<lambda_734585ecef8cae37a0607e5d54dd865b>
1>        ]
1>D:\dev\src\tests\misc\DescribeTests.cpp(118): message : see reference to function template instantiation 'std::ostream &describe_utils::operator <<<T,boost::describe::detail::list<>,boost::describe::detail::list<boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_88c58abe40ddca9e64b2955f6dbfa7ea>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_5916e84125bc784d483539ad1d3a3d09>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_c9d2335107088328dfd07a87925d597b>::()::D,1>,boost::describe::detail::member_descriptor<My::_public_member_descriptor_fn::<lambda_bab1101c997e04563d84b2536558ac70>::()::D,1>>>(std::ostream &,const T &)' being compiled
1>        with
1>        [
1>            T=My::Outer
1>        ]
1>D:\dev\src\tests\misc\DescribeTests.cpp(200): message : see reference to function template instantiation 'std::string `anonymous-namespace'::as_string<My::Outer>(const T &)' being compiled
1>        with
1>        [
1>            T=My::Outer
1>        ]
1>Done building project "tests_describe.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Make manual macro annotation unnecessary for simple aggregates, integrating Boost PFR

Hi, @pdimov ! Let me introduce you a nice thing that I contribute for a long time.
The Boost PFR library provides everithing we need to get rid of BOOST_DESCRIBE_STRUCT macro(including extraction of fields names) and might be used in a lot of libraries that already relies of Boost Describe - Boost MySQL, Boost Json, etc.
It seemed nice to integrate PFR as yet another reflection library near the Describe into all of these libraries, but wait don't we have a more shorter way? Why don't we integrate PFR into Describe, keeping the old interface but without macro? And then all of these Describe-relied libraries will automatically get Boost PFR support for user defined types.

Of course we should bear in mind that PFR have limitations: https://www.boost.org/doc/libs/master/doc/html/boost_pfr/limitations_and_configuration.html

What do you think about such development? Does it worth to start working on it? I believe it's possible, but does it fit Describe's conception?
Please let me know if you are interested and don't hesitate to ask any question about Boost PFR.

Compile error while trying to use describe on a class named D

The following code does not compile (g++ version 7-10, linux, -std=c++17 or gnu++14 for gcc7):

struct D {
        int a;
};
BOOST_DESCRIBE_STRUCT(D, (), (a))

It seems that the “D” name is somehow used in describe internals (macros), and as such, reserved. Describe should use longer names, and ideally prefixed one, such as DESCRIBE_INTERNAL_D (and document this) to avoid name clashes.

No compile-time error when incorrectly using BOOST_DESCRIBE_STRUCT on an enum type

Made that mistake, because my descriptions are not hand-written, but code-generated,
and I forgot that container I was using had all types, not just structs. I fixed my generator
since, but the code should probably static_assert the type is not an enum (or primitive).

Of course, I generated something like

BOOST_DESCRIBE_STRUCT(
    MyEnum, (),
    () // No state
)

Add support for describe getters && setters

I suggest adding support for getters and setters to the library. I see this new functionality like this:

#include <boost/describe.hpp>
#include <iostream>

struct employee
{
    void                  set_name    (std::string name)         ;
    void                  set_age     (int age)                  ;
    const std::string&    get_name    ()                   const ;
    int                   get_age     ()                   const ;
};

BOOST_DESCRIBE_STRUCT(
    employee, (), (
        ("name", get_name, set_name),
        ("age", get_age,  set_age)))


int main()
{
    using boost::describe::describe_members;
    using boost::describe::mod_any_access;
	
    employee emp;
    emp.set_name("John");
    emp.set_age(20);
    
    boost::mp11::mp_for_each<describe_members<decltype(emp), mod_any_access>>([&](auto D){
        auto getter = D.getter; // D also have "setter" field, but a "pointer" field is unavailable
        std::cout << D.name << " = " << (emp.*getter)() << std::endl;
    }); 
	
    // Will print:
    //    name = John
    //    age = 20
    
    return 0;
}

How to build the tests?

Hi
I am interested in understanding this library for some possible extension to the enumerations utilities.

However I am struggling to build and run the tests with the cmake provided.

This is what I am doing:
git clone .... into new directory describe
cd describe
mkdir build
cd build
cmake ..
cmake --build .

No errors but the test are not built and the final build stage returns immediately.

What am I doing wrong?
Thanks

Automatic Conversion to JSON with base struct

The following code is mostly copied from documentation Automatic Conversion to JSON

#include <iostream>
#include <vector>
#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <boost/json.hpp>
#include <boost/utility/string_view.hpp>
namespace NS
{
    struct A
    {
        int x = 0;
        std::string s;
    };
    BOOST_DESCRIBE_STRUCT(A, (), (x, s));

    struct C: A
    {
        int y = 0;
    };
    BOOST_DESCRIBE_STRUCT(C, (A), (y));

    template<class T,
        class D1 = boost::describe::describe_members<T,
        boost::describe::mod_public | boost::describe::mod_protected>,
        class D2 = boost::describe::describe_members<T, boost::describe::mod_private>,
        class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value && !std::is_union<T>::value> >
    void tag_invoke(boost::json::value_from_tag const&, boost::json::value& v, T const& t)
    {
        auto& obj = v.emplace_object();

        boost::mp11::mp_for_each<D1>([&](auto D) {

            obj[D.name] = boost::json::value_from(t.*D.pointer);

            });
    }

    template<class T> void extract(boost::json::object const& obj, char const* name, T& value)
    {
        value = boost::json::value_to<T>(obj.at(name));
    }

    template<class T,
        class D1 = boost::describe::describe_members<T,
        boost::describe::mod_public | boost::describe::mod_protected>,
        class D2 = boost::describe::describe_members<T, boost::describe::mod_private>,
        class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value && !std::is_union<T>::value> >
    T tag_invoke(boost::json::value_to_tag<T> const&, boost::json::value const& v)
    {
        auto const& obj = v.as_object();

        T t{};

        boost::mp11::mp_for_each<D1>([&](auto D) {

            extract(obj, D.name, t.*D.pointer);

            });

        return t;
    }
}

namespace NS1
{
    using namespace NS;

    void fun()
    {
        C c;
        auto jv2 = boost::json::value_from(c);
        jv2.as_object()["x"] = 5;
        auto c1 = boost::json::value_to<C>(jv2);

        std::cout << jv2 << std::endl;
        std::cout<<c1.x << std::endl;
    }
}
int main()
{
    NS1::fun();
}

But it seems value_from and value_to will ignore base class. So manually set "x" to 5 is useless on value c1.

Code gives compiler error when using VC++ new preprocessor

The code:

`#include <boost/describe.hpp>

struct XXX
{
int x;
};
struct YYY : public XXX
{
long y;
};

BOOST_DESCRIBE_STRUCT(YYY,(XXX),(y))

int main()
{
return 0;
}`

compiles without error for gcc-10.2 in C++14 mode, clang-11.0 in C++14 mode, in clang-win-11.0 in C++14 mode, in VC++14.2 in C++14 mode using the default preprocessor, and in VC++14.2 Preview in C++14 mode using the default preprocessor. Yet if I try to compile it with VC++14.2 or VC++14.2 Preview, using the new C++ standard preprocessor which is triggered with the /Zc:preprocessor switch, the code gives the error:

test_describe.cpp test_describe.cpp(12): error C2589: ';': illegal token on right side of '::' test_describe.cpp(12): error C2062: type 'unknown-type' unexpected

I would try to report this to Microsoft but they would have to get Describe, put it into a Boost directory structure, and then try to test it out that way, which might be onerous for them. Obviously gcc and clang have extremely standard C++ preprocessors, so why the new VC++ preprocessor gives an error while gcc and clang do not is the mystery.

Using trailing comma to describe enum yields unscrutable errors

My enums are code-generated, and the last enumerator has a trailing comma, which is OK in C++11.
But when I copy/pasted it to turn it into a BOOST_DESCRIBE_ENUM, this yielding weird errors like below.
Removing the trailing comma fixed the problem, but I wasted quite a bit of time on this.

Any way to fix this, so it works with trailing commas, as enums do?
Or at least tweak the code to yield a better error message?

1>DescribeTests.cpp
1>D:\boost/describe/enum.hpp(29,1): error C2182: 'value': illegal use of type 'void'
1>D:\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/algorithm.hpp(1037): message : see reference to class template instantiation 'boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_9250c7397ae982f1d2ae378dbdd22917>::()::D>' being compiled
1>D:\boost\1.74.0\Win_x64_10_v16_debug\include\boost/mp11/algorithm.hpp(1055): message : see reference to function template instantiation 'F boost::mp11::detail::mp_for_each_impl<boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_22d39ba0fa5275074785db73206bfcec>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_b36e895947c870ba551a0beff49d6047>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_294ffb3a79474efc9e18b549fb7261d1>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_9250c7397ae982f1d2ae378dbdd22917>::()::D>,_Ty>(boost::mp11::mp_list<boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_22d39ba0fa5275074785db73206bfcec>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_b36e895947c870ba551a0beff49d6047>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_294ffb3a79474efc9e18b549fb7261d1>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_9250c7397ae982f1d2ae378dbdd22917>::()::D>>,F &&)' being compiled
1>        with
1>        [
1>            F=`anonymous-namespace'::enum_to_string::<lambda_35eeefbe1e328ec36c865ccd2344c156>,
1>            _Ty=`anonymous-namespace'::enum_to_string::<lambda_35eeefbe1e328ec36c865ccd2344c156>
1>        ]
1>D:\pdgm\trunk\psc3\SharedComponents\src\tests\eml\DescribeTests.cpp(26): message : see reference to function template instantiation '`anonymous-namespace'::enum_to_string::<lambda_35eeefbe1e328ec36c865ccd2344c156> boost::mp11::mp_for_each<boost::describe::detail::list<boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_22d39ba0fa5275074785db73206bfcec>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_b36e895947c870ba551a0beff49d6047>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_294ffb3a79474efc9e18b549fb7261d1>::()::D>,boost::describe::detail::enum_descriptor<Foo::_enum_descriptor_fn::<lambda_9250c7397ae982f1d2ae378dbdd22917>::()::D>>,`anonymous-namespace'::enum_to_string::<lambda_35eeefbe1e328ec36c865ccd2344c156>>(F &&)' being compiled
1>        with
1>        [
1>            F=`anonymous-namespace'::enum_to_string::<lambda_35eeefbe1e328ec36c865ccd2344c156>
1>        ]
1>D:\pdgm\trunk\psc3\SharedComponents\src\tests\eml\DescribeTests.cpp(42): message : see reference to function template instantiation 'const char *`anonymous-namespace'::enum_to_string<Foo::Bar>(E)' being compiled
1>        with
1>        [
1>            E=Foo::Bar
1>        ]
1>D:\boost/describe/enum.hpp(29,1): error C2440: 'initializing': cannot convert from 'void' to 'const int'
1>D:\boost/describe/enum.hpp(29,59): message : Expressions of type void cannot be converted to other types
1>Done building project "tests_describe.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Add support for custom class/struct member names

I suggest adding support for custom class/struct member names to the library.
Like this example:

#include <boost/describe.hpp>
#include <iostream>

struct my_struct
{
    int   a;
    float b;
    char  c;
};

BOOST_DESCRIBE_STRUCT(
    my_struct, (), (
        (a, "a_name"),
        (b, "b_name"),
        (c)
    )
)

int main()
{
    boost::mp11::mp_for_each<
        boost::describe::describe_members<my_struct,
        boost::describe::mod_any_access>
    >([&](auto D){
        std::cout << D.name << std::endl;
    }); 

    // Output:
    //    a_name
    //    b_name
    //    c

    return 0;
}

Add support for class template

Would support for class template description be implementable (eventually with a different macro)?

namespace app {
    template <typename T>
    struct point
    {
        T x;
        T y;
    };

    BOOST_DESCRIBE_STRUCT(point, (), (x, y))      // Not supported
    BOOST_DESCRIBE_STRUCT(point<int>, (), (x, y)) // OK
} //namespace app

Number of parameters for BOOST_DESCRIBE_CLASS and BOOST_DESCRIBE_STRUCT

You should be able to leave out the empty parentheses parameter completely for no base classes in the BOOST_DESCRIBE_CLASS and BOOST_DESCRIBE_STRUCT macros. OK, you do not want to rely on Boost PP and BOOST_PP_OVERLOAD, but you can steal the code for that from Boost PP and adjust your macro depending on the number of arguments passed to it. I think it would be more elegant. I assume that all compilers support variadic macros by now and all you need for your own internal version of BOOST_PP_OVERLOAD is variadic macro support.

Support for unions

BOOST_DESCRIBE_STRUCT and BOOST_DESCRIBE_CLASS reject unions with a static_assert, but they shouldn't.

However, once unions are allowed, all the examples need an is_class check added, because traversing all the described members of a union is guaranteed undefined behavior (because at most one is active.)

https://lists.boost.org/Archives/boost/2021/11/252270.php

using hash_value() for in different namespace

Hi.
I'm trying to implement a hash_value() from this example, but I'm getting a compilation error

full gist

#include <iostream>

#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <boost/container_hash/hash.hpp>

#include <gtest/gtest.h>


#if BOOST_VERSION < 108100

template <class T, class Bd = boost::describe::describe_bases<T, boost::describe::mod_any_access>,
          class Md = boost::describe::describe_members<T, boost::describe::mod_any_access>>
std::size_t hash_value(const T& t) {
    std::size_t r = 0;
    boost::mp11::mp_for_each<Bd>([&](auto D) {
        using B = typename decltype(D)::type;
        boost::hash_combine(r, (const B&)t);
    });
    boost::mp11::mp_for_each<Md>([&](auto D) { boost::hash_combine(r, t.*D.pointer); });
    return r;
}

#endif


namespace namespace_a {
struct PODTypeN1 {
    int a;
    int b;
    int c;
};
BOOST_DESCRIBE_STRUCT(PODTypeN1, (), (a, b, c))
} // namespace namespace_a

namespace namespace_b {
struct PODTypeN2 {
    float d;
    double e;
    bool f;
};
BOOST_DESCRIBE_STRUCT(PODTypeN2, (), (d, e, f))
} // namespace namespace_b

namespace namespace_c {
struct ComplexPodTypeN {
    namespace_a::PODTypeN1 podType1;
    namespace_b::PODTypeN2 podType2;
};
BOOST_DESCRIBE_STRUCT(ComplexPodTypeN, (), (podType1, podType2))
} // namespace namespace_c

namespace test {

TEST(test_hash, ComplexPodType_with_namespace) {
    namespace_c::ComplexPodTypeN a = {
        {1, 2, 3},
        {0.3, 0.4, true}
    };
    namespace_c::ComplexPodTypeN b = a;

    auto hash_a = boost::hash<namespace_c::ComplexPodTypeN>()(a);
    auto hash_b = boost::hash<namespace_c::ComplexPodTypeN>()(b);
    std::cout << "hash_a = " << hash_a << std::endl;
    std::cout << "hash_b = " << hash_b << std::endl;
    ASSERT_TRUE((hash_a == hash_b));

    b.podType2.e = 42.42;
    hash_b = boost::hash<namespace_c::ComplexPodTypeN>()(b);
    std::cout << "hash_a = " << hash_a << std::endl;
    std::cout << "hash_b = " << hash_b << std::endl;
    ASSERT_TRUE((hash_a != hash_b));
}

} // namespace test
Scanning dependencies of target boost_describe
[ 50%] Building CXX object CMakeFiles/boost_describe.dir/test_hash.cpp.o
In file included from /home/pftest/.conan/data/boost/1.78.0/_/_/package/4c0d21de97108c6489e888fd2e50a95c38b9f3aa/include/boost/container_hash/hash.hpp:766:0,
                 from /home/pftest/Documents/boost_describe3/test_hash.cpp:5:
/home/pftest/.conan/data/boost/1.78.0/_/_/package/4c0d21de97108c6489e888fd2e50a95c38b9f3aa/include/boost/container_hash/extensions.hpp: In instantiation of ‘std::size_t boost::hash<T>::operator()(const T&) const [with T = namespace_c::ComplexPodTypeN; std::size_t = long unsigned int]’:
/home/pftest/Documents/boost_describe3/test_hash.cpp:62:64:   required from here
/home/pftest/.conan/data/boost/1.78.0/_/_/package/4c0d21de97108c6489e888fd2e50a95c38b9f3aa/include/boost/container_hash/extensions.hpp:305:30: error: no matching function for call to ‘hash_value(const namespace_c::ComplexPodTypeN&)’
             return hash_value(val);

Provide integration with formatting libraries

Since the library provides a to_string / from_string method, it also makes sense that it provides integration with c++20 format library. Maybe at least as a sample in the documentation.

An example using libfmt (i currently don't have local access to a compiler with std::format support):

template<typename Type>
struct fmt::formatter<Type,
        std::enable_if_t<
        std::is_enum<Type>::value &&
        std::is_same<decltype(boost::mp11::mp_front<boost::describe::describe_enumerators<Type>>::name), char const* const>::value,
                char>
        > : formatter<std::string_view>
{
        template<typename FormatContext> auto format(Type v, FormatContext& ctx) -> decltype(ctx.out()) {
                std::string_view name = "unknown";
                boost::mp11::mp_for_each<boost::describe::describe_enumerators<Type>>([&](auto EnumValue) {
                        if(EnumValue.value == v)
                                name = EnumValue.name;
                });
                return formatter<std::string_view>::format(name, ctx);
        }
};

How to use name of the member with Boost Hana?

#include <boost/describe.hpp>
#include <boost/mp11.hpp>
#include <boost/hana.hpp>
#include <cstdio>

enum E {
    v1 = 11,
    v2,
    v3 = 5
};
BOOST_DESCRIBE_ENUM(E, v1, v2, v3)

int main()
{
    constexpr auto D = boost::mp11::mp_front< boost::describe::describe_enumerators<E> >();
    std::printf( "%s: %d\n", D.name, D.value );
}

Is there a way to convert value of D.name to boost::hana::string?
https://www.boost.org/doc/libs/1_79_0/libs/hana/doc/html/structboost_1_1hana_1_1string.html
If not, is it planned to introduce a similar feature on the Boost Describe side?

Add operators

Add a namespace boost::describe::operators containing ==, !=, <, <=, >, >=. Typical use would be

namespace user
{

struct X
{
};

BOOST_DESCRIBE_STRUCT(X, (), ())

using boost::describe::operators::operator==;
using boost::describe::operators::operator!=;

} // namespace user

Maybe add boost::describe::eq, boost::describe::lt, boost::describe::le as helper functions.

Could the operators be directly in boost::describe? (Probably not.)

Add reflection on attributes

Following up discussion on cpplang, it would be interesting to have a way to extend member descriptors with custom information (think units, text description). User-defined attributes seems the right tool to express this custom information:

[[units(cm), description("this is sparta")]] int x;

The emulation of the reflection would look something like:

BOOST_DESCRIBE_MEMATTR(x, units(cm), description("this is sparta")) int x;

We would have describe_member_attributes<D> returns L<A1, A2, …​, An>, where D is a member descriptor.

and Ai is an attribute descriptor of the form

struct Ai
{
        static constexpr char const * name = "units";
        static constexpr char const * value = "cm";   /* string attribute only for now */
};

As pointed out by @pdimov, there is a proposal about attribute reflection that introduces typed attributes.

Describe and json serialization with containers

Hello,

Let's say I have the following struct:

struct Item
   {
      std::string id;
      std::string label;
      std::string iconFile;
   };
   BOOST_DESCRIBE_STRUCT(Item, (), (id, label, iconFile))

When I serialize in json the following code:

Item item = { "anId", "aLabel", "anIcon"};
 std::stringstream ss;
 ss << boost::json::value_from(item);

I get the following json:

{"id":"anId","label":"aLabel","iconFile":"anIcon"}

Would it be possible to have directly:

{ "item": {"id":"anId","label":"aLabel","iconFile":"anIcon"} }

Without having to write:

boost::json::object jObj;
jObj["item"] = boost::json::value_from(item);
auto msg = boost::json::serialize(jObj);

Thanks

Add descriptor_by_name, descriptor_by_pointer

This can be useful for "detached" annotations, as suggested by @grisumbras:

namepace mine {
template <class T> struct annotation {};
} //mine

struct foobar {
  int foo;
  double bar;
};
BOOST_DESCRIBE_STRUCT(foobar, (), (foo, bar))

namepace mine {
template <> struct annotation<BOOST_DESCRIBE_GET_MEMBER(foobar, foo)> { constexpr auto& value = "some text here"; }
} // namespace mine

In C++14, a descriptor_by_name can be implemented and used like this:

using L1 = boost::describe::describe_members<X, boost::describe::mod_any_access>;
using N1 = BOOST_DESCRIBE_MAKE_NAME( y );
using D1 = descriptor_by_name<L1, N1>;

whereas in C++17 a descriptor_by_pointer can be used like this:

using D2 = descriptor_by_pointer<L1, &X::y>;

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.