英文:
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:
#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
// 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 <auto Mem>
using Desc = bd::descriptor_by_pointer<bd::describe_members<Foo, bd::mod_any_access>, Mem>;
// specialize some metadata
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{});
}
Prints
meta for bar: { false, "" }
meta for baz: { true, "hello" }
default action
meta for qux: { 0.42, "specialized!" }
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:
#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<not 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
// 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 <> 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{});
}
Prints
flag for bar: false
flag for baz: true
flag for qux: surprise
flag for quuz: fun
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论