可以添加注释到类型和/或成员吗?

huangapple go评论108阅读模式
英文:

boost::describe: Can I add annotations to types and/or members?

问题

使用 boost::describe 库,是否可以添加关于给定类型或成员的额外信息?

例如,如果我有一个类描述如下:

BOOST_DESCRIBE_STRUCT(Foo, (), (bar, baz))

是否有办法添加一些额外信息,比如标签或标志,要么添加到成员,要么添加到类型?关于成员有很多信息,比如名称、类型、可见性等等。是否有一种方法可以添加自定义信息?

要标记类型,我尝试使用类型特性,如 std::true_type 或 std::false_type。

然而,这些方法使用起来很麻烦,因为必须在定义类型特性的命名空间中进行模板特化,而不是在描述的类型所在的命名空间中。

英文:

Is it possible, using the boost::describe library, to add additional information about a given type or member?

For instance, if I have a class described like this:

BOOST_DESCRIBE_STRUCT(Foo, (), (bar, baz))

Is there any way to add some kind of additional info, like a tag or flag, to either the members or the type? There's a lot of information about members, such as name, type, visibility, etc. Is there a way to add some custom info to that?

To flag types, I've tried using type traits with std::true_type or std::false type.

However, these are cumbersome to use, because template specializations must be done in the namespace where the type traits were defined, not the namespace where the types being described are defined.

答案1

得分: 1

你可以使用一个特性(trait),通过描述符来映射。让我展示一个复杂的例子,结合了多个元数据和行为的示例:

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

namespace bd   = boost::describe;
namespace mp11 = boost::mp11;

namespace MyLib {
    struct Metadata {
        bool             some_flag = false;
        std::string_view some_text = "";
        static void      some_action() { std::cout << "default action" << std::endl; }
    };

    template <typename D> constexpr inline Metadata meta_v{};

    template <class T, class D1 = bd::describe_members<T, bd::mod_public | bd::mod_protected>,
              class En = std::enable_if_t<!std::is_union<T>::value>>
    void demo(T const&) {
        mp11::mp_for_each<D1>([&](auto D) {
            auto const& m = meta_v<decltype(D)>;
            std::cout << "meta for " << D.name << ": { " << m.some_flag << ", "
                      << quoted(m.some_text) << " }" << std::endl;
            if (m.some_flag)
                m.some_action();
        });
    }
} // namespace MyLib

// 应用程序部分
namespace MyLib {
    struct Foo {
        int         bar;
        std::string baz;
        double      qux;
    };

    BOOST_DESCRIBE_STRUCT(Foo, (), (bar, baz, qux))

    // 一个简化版本,你可能希望通过自动推断 `Foo` 来更加通用地定义
    template <auto Mem>
    using Desc = bd::descriptor_by_pointer<bd::describe_members<Foo, bd::mod_any_access>, Mem>;

    // 专门化一些元数据
    template <> constexpr inline Metadata meta_v<Desc<&Foo::baz>>{true, "hello"};

    struct Special {
        double some_flag = 42e-2;
        std::string_view some_text = "specialized!";
        static void some_action() { std::cerr << "stderr instead" << std::endl; }
    }
    template <> constexpr inline Special meta_v<Desc<&Foo::qux>>{};
} // namespace MyApp

int main() {
    std::cout << std::boolalpha;
    demo(MyLib::Foo{});
}

输出结果:

meta for bar: { false, "" }
meta for baz: { true, "hello" }
default action
meta for qux: { 0.42, "specialized!" }
stderr instead

进一步思考:

你也可以使用 Fusion/Hana 映射来将信息与描述符关联起来。当然,这主要取决于你希望你的代码看起来如何。

更新:

稍微泛化一下(从成员指针推断出类类型)并支持成员函数指针:

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

namespace bd   = boost::describe;
namespace mp11 = boost::mp11;

namespace MyLib {
    template <typename D> constexpr inline bool flag_v{};

    template <class T, class D1 = bd::describe_members<T, bd::mod_public | bd::mod_protected>,
              class F1 = bd::describe_members<T, bd::mod_public | bd::mod_protected | bd::mod_function>,
              class En = std::enable_if_t<!std::is_union<T>::value>>
    void demo(T const&) {
        mp11::mp_for_each<D1>([&](auto D) {
            std::cout << "flag for " << D.name << ": " << flag_v<decltype(D)> << std::endl;
        });
        mp11::mp_for_each<F1>([&](auto D) {
            std::cout << "flag for " << D.name << ": " << flag_v<decltype(D)> << std<<endl;
        });
    }

    namespace detail {
        template <typename C, typename T> constexpr C deduce_class(T(C::*));
        template <auto Mem> using Class = decltype(deduce_class(Mem));
        template <auto Mem, typename /*Enable*/ = void> struct DescF;

        template <auto Mem>
        struct DescF<Mem, std::enable_if_t<std::is_member_function_pointer_v<decltype(Mem)>>> {
            using type = bd::descriptor_by_pointer<
                bd::describe_members<Class<Mem>, bd::mod_any_access | bd::mod_function>, Mem>;
        };

        template <auto Mem>
        struct DescF<Mem, std::enable_if_t<!std::is_member_function_pointer_v<decltype(Mem)>>> {
            using type = bd::descriptor_by_pointer<bd::describe_members<Class<Mem>, bd::mod_any_access>, Mem>;
        };
    }

    template <auto Mem> using Desc = typename detail::DescF<Mem>::type;
} // namespace MyLib

// 应用程序部分
namespace MyApp {
    struct Foo {
        int         bar;
        std::string baz;
        double      qux;
    };

    struct Bar {
        int quuz(double) { return 42; }
    };

    BOOST_DESCRIBE_STRUCT(Foo, (), (bar, baz, qux))
    BOOST_DESCRIBE_STRUCT(Bar, (), (quuz))
} // namespace MyApp

using MyLib::Desc;

// 专门化一些元数据
template <> auto MyLib::flag_v<Desc<&MyApp::Foo::baz>> = true;
template <> auto MyLib::flag_v<Desc<&MyApp::Foo::qux>> = "surprise";
template <> auto MyLib::flag_v<Desc<&MyApp::Bar::quuz>> = "fun";

int main() {
    std::cout << std::boolalpha;
    MyLib::demo(MyApp::Foo{});
    MyLib::demo(MyApp::Bar{});
}

输出结果:

flag for bar: false
flag for baz: true
flag for qux: surprise
flag for quuz: fun
英文:

You could use a trait that maps by descriptor. Let me show a contrived complex example that combines multiple pieces of meta data and even behavior:

Live On Coliru

#include &lt;boost/describe.hpp&gt;
#include &lt;boost/mp11.hpp&gt;
#include &lt;iomanip&gt;
#include &lt;iostream&gt;
namespace bd   = boost::describe;
namespace mp11 = boost::mp11;
namespace MyLib {
struct Metadata {
bool             some_flag = false;
std::string_view some_text = &quot;&quot;;
static void      some_action() { std::cout &lt;&lt; &quot;default action&quot; &lt;&lt; std::endl; }
};
template &lt;typename D&gt; constexpr inline Metadata meta_v{};
template &lt;class T, class D1 = bd::describe_members&lt;T, bd::mod_public | bd::mod_protected&gt;,
class En = std::enable_if_t&lt;!std::is_union&lt;T&gt;::value&gt;&gt;
void demo(T const&amp;) {
mp11::mp_for_each&lt;D1&gt;([&amp;](auto D) {
auto const&amp; m = meta_v&lt;decltype(D)&gt;;
std::cout &lt;&lt; &quot;meta for &quot; &lt;&lt; D.name &lt;&lt; &quot;: { &quot; &lt;&lt; m.some_flag &lt;&lt; &quot;, &quot;
&lt;&lt; quoted(m.some_text) &lt;&lt; &quot; }&quot; &lt;&lt; std::endl;
if (m.some_flag)
m.some_action();
});
}
} // namespace MyLib
// application
namespace MyLib {
struct Foo {
int         bar;
std::string baz;
double      qux;
};
BOOST_DESCRIBE_STRUCT(Foo, (), (bar, baz, qux))
// a shorthand, you might want to make this more generically elegant by deducing `Foo` instead
template &lt;auto Mem&gt;
using Desc = bd::descriptor_by_pointer&lt;bd::describe_members&lt;Foo, bd::mod_any_access&gt;, Mem&gt;;
// specialize some metadata
template &lt;&gt; constexpr inline Metadata meta_v&lt;Desc&lt;&amp;Foo::baz&gt;&gt;{true, &quot;hello&quot;};
struct Special {
double some_flag = 42e-2;
std::string_view some_text = &quot;specialized!&quot;;
static void      some_action() { std::cerr &lt;&lt; &quot;stderr instead&quot; &lt;&lt; std::endl; }
};
template &lt;&gt; constexpr inline Special meta_v&lt;Desc&lt;&amp;Foo::qux&gt;&gt;{};
} // namespace MyApp
int main() {
std::cout &lt;&lt; std::boolalpha;
demo(MyLib::Foo{});
}

Prints

meta for bar: { false, &quot;&quot; }
meta for baz: { true, &quot;hello&quot; }
default action
meta for qux: { 0.42, &quot;specialized!&quot; }
stderr instead

Further Thoughts

You might also use a Fusion/Hana map to associate information to descriptors. It depends mostly on how you want your code to look of course.

UPDATE

Generalizing a bit (deducing the class type from a member pointer) and supporting member function pointers:

Live On Coliru

#include &lt;boost/describe.hpp&gt;
#include &lt;boost/mp11.hpp&gt;
#include &lt;iomanip&gt;
#include &lt;iostream&gt;
namespace bd   = boost::describe;
namespace mp11 = boost::mp11;
namespace MyLib {
template &lt;typename D&gt; constexpr inline bool flag_v{};
template &lt;
class T, class D1 = bd::describe_members&lt;T, bd::mod_public | bd::mod_protected&gt;,
class F1 = bd::describe_members&lt;T, bd::mod_public | bd::mod_protected | bd::mod_function&gt;,
class En = std::enable_if_t&lt;!std::is_union&lt;T&gt;::value&gt;&gt;
void demo(T const&amp;) {
mp11::mp_for_each&lt;D1&gt;([&amp;](auto D) {
std::cout &lt;&lt; &quot;flag for &quot; &lt;&lt; D.name &lt;&lt; &quot;: &quot; &lt;&lt; flag_v&lt;decltype(D)&gt; &lt;&lt; std::endl;
});
mp11::mp_for_each&lt;F1&gt;([&amp;](auto D) {
std::cout &lt;&lt; &quot;flag for &quot; &lt;&lt; D.name &lt;&lt; &quot;: &quot; &lt;&lt; flag_v&lt;decltype(D)&gt; &lt;&lt; std::endl;
});
}
namespace detail {
template &lt;typename C, typename T&gt; constexpr C deduce_class(T(C::*));
template &lt;auto Mem&gt; using Class = decltype(deduce_class(Mem));
template &lt;auto Mem, typename /*Enable*/ = void&gt; struct DescF;
template &lt;auto Mem&gt;
struct DescF&lt;Mem, std::enable_if_t&lt;std::is_member_function_pointer_v&lt;decltype(Mem)&gt;&gt;&gt; {
using type = bd::descriptor_by_pointer&lt;
bd::describe_members&lt;Class&lt;Mem&gt;, bd::mod_any_access | bd::mod_function&gt;, Mem&gt;;
};
template &lt;auto Mem&gt;
struct DescF&lt;Mem, std::enable_if_t&lt;not std::is_member_function_pointer_v&lt;decltype(Mem)&gt;&gt;&gt; {
using type =
bd::descriptor_by_pointer&lt;bd::describe_members&lt;Class&lt;Mem&gt;, bd::mod_any_access&gt;,
Mem&gt;;
};
}
template &lt;auto Mem&gt; using Desc = typename detail::DescF&lt;Mem&gt;::type;
} // namespace MyLib
// application
namespace MyApp {
struct Foo {
int         bar;
std::string baz;
double      qux;
};
struct Bar {
int quuz(double) { return 42; }
};
BOOST_DESCRIBE_STRUCT(Foo, (), (bar, baz, qux))
BOOST_DESCRIBE_STRUCT(Bar, (), (quuz))
} // namespace MyApp
using MyLib::Desc;
// specialize some metadata
template &lt;&gt; auto MyLib::flag_v&lt;Desc&lt;&amp; MyApp::Foo::baz&gt;&gt;  = true;
template &lt;&gt; auto MyLib::flag_v&lt;Desc&lt;&amp; MyApp::Foo::qux&gt;&gt;  = &quot;surprise&quot;;
template &lt;&gt; auto MyLib::flag_v&lt;Desc&lt;&amp; MyApp::Bar::quuz&gt;&gt; = &quot;fun&quot;;
int main() {
std::cout &lt;&lt; std::boolalpha;
MyLib::demo(MyApp::Foo{});
MyLib::demo(MyApp::Bar{});
}

Prints

flag for bar: false
flag for baz: true
flag for qux: surprise
flag for quuz: fun

huangapple
  • 本文由 发表于 2023年3月7日 02:31:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75654553.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定