我有一个结构
typedef struct A
{
int a;
int b;
char * c;
}aA;
我想遍历结构的每个成员并打印它的值。就像是:
void print_struct_value(struct *A)
{
for each member of struct A
cout << "struct name . member name" << "value";
}
这怎么能在 C++ 中完成?
也许您可以使用 Boost Fusion/Phoenix 将一些东西串在一起:
在Coliru上现场观看!
#include <boost/fusion/adapted/struct.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/phoenix/phoenix.hpp>
using boost::phoenix::arg_names::arg1;
#include <string>
#include <iostream>
struct A
{
int a;
int b;
std::string c;
};
BOOST_FUSION_ADAPT_STRUCT(A, (int,a)(int,b)(std::string,c));
int main()
{
const A obj = { 1, 42, "The Answer To LtUaE" };
boost::fusion::for_each(obj, std::cout << arg1 << "\n");
}
更新:最新版本的 boost 可以使用 C++11 类型推导:
BOOST_FUSION_ADAPT_STRUCT(A,a,b,c);
输出:
1
42
The Answer To LtUaE
您不能迭代对象的数据成员。但是,您可以使用std::ostream
' 流插入运算符单独打印:
struct A
{
int a;
int b;
std::string c;
friend std::ostream& operator <<(std::ostream& os, A const& a)
{
return os << a.a << '\n'
<< a.b << '\n'
<< a.c << '\n';
}
};
内部主要:
int main()
{
A a = {5, 10, "apple sauce"};
std::cout << a;
}
输出:
5
10
苹果酱
有几种方法可以做到这一点,但您需要使用一些宏来定义或调整结构。
您可以使用此答案REFLECTABLE
中给出的宏来定义这样的结构:
struct A
{
REFLECTABLE
(
(int) a,
(int) b,
(const char *) c
)
};
然后您可以遍历字段并像这样打印每个值:
struct print_visitor
{
template<class FieldData>
void operator()(FieldData f)
{
std::cout << f.name() << "=" << f.get() << std::endl;
}
};
template<class T>
void print_fields(T & x)
{
visit_each(x, print_visitor());
}
A x;
print_fields(x);
另一种方法是将结构调整为融合序列(参见文档)。这是一个例子:
struct A
{
int a;
int b;
const char * c;
};
BOOST_FUSION_ADAPT_STRUCT
(
A,
(int, a)
(int, b)
(const char *, c)
)
然后,您也可以使用以下方法打印字段:
struct print_visitor
{
template<class Index, class C>
void operator()(Index, C & c)
{
std::cout << boost::fusion::extension::struct_member_name<C, Index::value>::call()
<< "="
<< boost:::fusion::at<Index>(c)
<< std::endl;
}
};
template<class C>
void print_fields(C & c)
{
typedef boost::mpl::range_c<int,0, boost::fusion::result_of::size<C>::type::value> range;
boost::mpl::for_each<range>(boost::bind<void>(print_visitor(), _1, boost::ref(c)));
}
C++ 不支持开箱即用的反射,因此您所要求的在核心语言中是不可能的。
各种库试图提供这样的功能,通常是让您通过一些方法或宏注册您的字段,这将在幕后将它们保存到某种集合中,您可以在该集合上进行迭代。
我可以在标准 c++ 中迭代结构的成员吗?
不,标准c++没有提供一种方法来完成您所要求的,您“迭代”容器的元素 - 您不能迭代某种类型的成员。
“反射”(因为这种类型的功能通常被称为不是 C++ 的一部分)。
在c++11中,您可以使用 astd::tuple<int,int,char*>
而不是您的struct A
,它将存储相同类型的元素并且更容易迭代(使用一些模板魔法)。
这些元素的名称不会是从'a'
到,'c'
但如果你想以这种方式打印它,这当然可以通过一些额外的代码行来完成。
要访问特定元素,您将使用std::get<N> (your_tuple)
whereN
是从 0 到std::tuple_size<std::tuple<int,int,char*>>::value - 1
(即 2)的整数。
正如@Paul 所建议的,我使用 BOOST_FUSION_ADAPT_STRUCT 和一个自写的 for_each_member 函数:
/**
* \brief Allows iteration on member name and values of a Fusion adapted struct.
*
*
* BOOST_FUSION_ADAPT_STRUCT(ns::point,
* (int, x)
* (int, y)
* (int, z));
*
* template<class T>
* print_name_and_value(const char* name, T& value) const {
* std::cout << name << "=" << value << std::endl;
* }
*
*
* int main(void) {
*
* ns::point mypoint;
*
*
* boost::fusion::for_each_member(mypoint, &print_name_and_value);
*
*
* }
*
*/
#ifndef BOOST_FUSION_FOR_EACH_MEMBER_HPP
#define BOOST_FUSION_FOR_EACH_MEMBER_HPP
#include <functional>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/sequence/intrinsic/begin.hpp>
#include <boost/fusion/sequence/intrinsic/end.hpp>
#include <boost/fusion/sequence/intrinsic/front.hpp>
#include <boost/fusion/iterator/equal_to.hpp>
#include <boost/fusion/iterator/next.hpp>
#include <boost/fusion/iterator/deref.hpp>
#include <boost/fusion/iterator/distance.hpp>
#include <boost/fusion/support/category_of.hpp>
#include <boost/mpl/bool.hpp>
namespace boost { namespace fusion {
namespace detail {
template <typename First, typename Last, typename F>
inline void
for_each_member_linear(First const& first,
Last const& last,
F const& f,
boost::mpl::true_) {}
template <typename First, typename Last, typename F>
inline void
for_each_member_linear(First const& first,
Last const& last,
F const& f,
boost::mpl::false_) {
f(
extension::struct_member_name<
typename First::seq_type, First::index::value
>::call(),
*first
);
for_each_member_linear(
next(first),
last,
f,
result_of::equal_to< typename result_of::next<First>::type, Last>()
);
}
template <typename Sequence, typename F>
inline void
for_each_member(Sequence& seq, F const& f) {
detail::for_each_member_linear(
fusion::begin(seq),
fusion::end(seq),
f,
result_of::equal_to<
typename result_of::begin<Sequence>::type,
typename result_of::end<Sequence>::type>()
);
}
}
template <typename Sequence, typename F>
inline void
for_each_member(Sequence& seq, F f) {
detail::for_each_member(seq, f);
}
}}
#endif
如果结构中的所有字段都相同,则可以
template <typename S> uint64_t* get_begin(S *s)
{
return (uint64_t*)s;
}
template <typename S> uint64_t* get_end(S *s)
{
return (uint64_t*)((uint8_t*)s+sizeof(*s));
}
struct statistics_s {
uint64_t f1;
uint64_t f2;
} statistics;
for (uint64_t* p = get_begin(&statistics);p < get_end(&statistics);p++)
printf("%lu ", *p);
我写了一个没有 Boost 或其他第三方库的版本,已经使用 GCC 4.9(c++11)、clang 5.0(c++11)、VS 2008、VS 2019 进行了测试。
#include <iostream>
#include <string>
#define REFLECTION_WITH_FIELD_NAME 1
#define _PP_EVAL(...) __VA_ARGS__
#define _PP_EAT(...)
#define _PP_EMPTY
#define _PP_STR2(x) #x
#define _PP_STR(x) _PP_STR2(x)
#define _PP_MAP01(f, x) f(x)
#define _PP_MAP02(f, x, ...) f(x) _PP_EVAL(_PP_MAP01(f, __VA_ARGS__))
#define _PP_MAP03(f, x, ...) f(x) _PP_EVAL(_PP_MAP02(f, __VA_ARGS__))
#define _PP_MAP04(f, x, ...) f(x) _PP_EVAL(_PP_MAP03(f, __VA_ARGS__))
#define _PP_MAP05(f, x, ...) f(x) _PP_EVAL(_PP_MAP04(f, __VA_ARGS__))
#define _PP_MAP06(f, x, ...) f(x) _PP_EVAL(_PP_MAP05(f, __VA_ARGS__))
#define _PP_MAP07(f, x, ...) f(x) _PP_EVAL(_PP_MAP06(f, __VA_ARGS__))
#define _PP_MAP08(f, x, ...) f(x) _PP_EVAL(_PP_MAP07(f, __VA_ARGS__))
#define _PP_MAP09(f, x, ...) f(x) _PP_EVAL(_PP_MAP08(f, __VA_ARGS__))
#define _PP_MAP10(f, x, ...) f(x) _PP_EVAL(_PP_MAP09(f, __VA_ARGS__))
#define _PP_MAP11(f, x, ...) f(x) _PP_EVAL(_PP_MAP10(f, __VA_ARGS__))
#define _PP_MAP12(f, x, ...) f(x) _PP_EVAL(_PP_MAP11(f, __VA_ARGS__))
#define _PP_MAP13(f, x, ...) f(x) _PP_EVAL(_PP_MAP12(f, __VA_ARGS__))
#define _PP_MAP14(f, x, ...) f(x) _PP_EVAL(_PP_MAP13(f, __VA_ARGS__))
#define _PP_MAP15(f, x, ...) f(x) _PP_EVAL(_PP_MAP14(f, __VA_ARGS__))
#define _PP_MAP16(f, x, ...) f(x) _PP_EVAL(_PP_MAP15(f, __VA_ARGS__))
#define _PP_MAP17(f, x, ...) f(x) _PP_EVAL(_PP_MAP16(f, __VA_ARGS__))
#define _PP_MAP18(f, x, ...) f(x) _PP_EVAL(_PP_MAP17(f, __VA_ARGS__))
#define _PP_MAP19(f, x, ...) f(x) _PP_EVAL(_PP_MAP18(f, __VA_ARGS__))
#define _PP_MAP20(f, x, ...) f(x) _PP_EVAL(_PP_MAP19(f, __VA_ARGS__))
#define _PP_MAP21(f, x, ...) f(x) _PP_EVAL(_PP_MAP20(f, __VA_ARGS__))
#define _PP_MAP22(f, x, ...) f(x) _PP_EVAL(_PP_MAP21(f, __VA_ARGS__))
#define _PP_MAP23(f, x, ...) f(x) _PP_EVAL(_PP_MAP22(f, __VA_ARGS__))
#define _PP_MAP24(f, x, ...) f(x) _PP_EVAL(_PP_MAP23(f, __VA_ARGS__))
#define _PP_MAP25(f, x, ...) f(x) _PP_EVAL(_PP_MAP24(f, __VA_ARGS__))
#define _PP_MAP26(f, x, ...) f(x) _PP_EVAL(_PP_MAP25(f, __VA_ARGS__))
#define _PP_MAP27(f, x, ...) f(x) _PP_EVAL(_PP_MAP26(f, __VA_ARGS__))
#define _PP_MAP28(f, x, ...) f(x) _PP_EVAL(_PP_MAP27(f, __VA_ARGS__))
#define _PP_MAP29(f, x, ...) f(x) _PP_EVAL(_PP_MAP28(f, __VA_ARGS__))
#define _PP_MAP30(f, x, ...) f(x) _PP_EVAL(_PP_MAP29(f, __VA_ARGS__))
#define _PP_MAP31(f, x, ...) f(x) _PP_EVAL(_PP_MAP30(f, __VA_ARGS__))
#define _PP_MAP32(f, x, ...) f(x) _PP_EVAL(_PP_MAP31(f, __VA_ARGS__))
#define _PP_MAP33(f, x, ...) f(x) _PP_EVAL(_PP_MAP32(f, __VA_ARGS__))
#define _PP_MAP34(f, x, ...) f(x) _PP_EVAL(_PP_MAP33(f, __VA_ARGS__))
#define _PP_MAP35(f, x, ...) f(x) _PP_EVAL(_PP_MAP34(f, __VA_ARGS__))
#define _PP_MAP36(f, x, ...) f(x) _PP_EVAL(_PP_MAP35(f, __VA_ARGS__))
#define _PP_MAP37(f, x, ...) f(x) _PP_EVAL(_PP_MAP36(f, __VA_ARGS__))
#define _PP_MAP38(f, x, ...) f(x) _PP_EVAL(_PP_MAP37(f, __VA_ARGS__))
#define _PP_MAP39(f, x, ...) f(x) _PP_EVAL(_PP_MAP38(f, __VA_ARGS__))
#define _PP_MAP40(f, x, ...) f(x) _PP_EVAL(_PP_MAP39(f, __VA_ARGS__))
#define _PP_MAP41(f, x, ...) f(x) _PP_EVAL(_PP_MAP40(f, __VA_ARGS__))
#define _PP_MAP42(f, x, ...) f(x) _PP_EVAL(_PP_MAP41(f, __VA_ARGS__))
#define _PP_MAP43(f, x, ...) f(x) _PP_EVAL(_PP_MAP42(f, __VA_ARGS__))
#define _PP_MAP44(f, x, ...) f(x) _PP_EVAL(_PP_MAP43(f, __VA_ARGS__))
#define _PP_MAP45(f, x, ...) f(x) _PP_EVAL(_PP_MAP44(f, __VA_ARGS__))
#define _PP_MAP46(f, x, ...) f(x) _PP_EVAL(_PP_MAP45(f, __VA_ARGS__))
#define _PP_MAP47(f, x, ...) f(x) _PP_EVAL(_PP_MAP46(f, __VA_ARGS__))
#define _PP_MAP48(f, x, ...) f(x) _PP_EVAL(_PP_MAP47(f, __VA_ARGS__))
#define _PP_MAP49(f, x, ...) f(x) _PP_EVAL(_PP_MAP48(f, __VA_ARGS__))
#define _PP_MAP50(f, x, ...) f(x) _PP_EVAL(_PP_MAP49(f, __VA_ARGS__))
#define _PP_MAP51(f, x, ...) f(x) _PP_EVAL(_PP_MAP50(f, __VA_ARGS__))
#define _PP_MAP52(f, x, ...) f(x) _PP_EVAL(_PP_MAP51(f, __VA_ARGS__))
#define _PP_MAP53(f, x, ...) f(x) _PP_EVAL(_PP_MAP52(f, __VA_ARGS__))
#define _PP_MAP54(f, x, ...) f(x) _PP_EVAL(_PP_MAP53(f, __VA_ARGS__))
#define _PP_MAP55(f, x, ...) f(x) _PP_EVAL(_PP_MAP54(f, __VA_ARGS__))
#define _PP_MAP56(f, x, ...) f(x) _PP_EVAL(_PP_MAP55(f, __VA_ARGS__))
#define _PP_MAP57(f, x, ...) f(x) _PP_EVAL(_PP_MAP56(f, __VA_ARGS__))
#define _PP_MAP58(f, x, ...) f(x) _PP_EVAL(_PP_MAP57(f, __VA_ARGS__))
#define _PP_MAP59(f, x, ...) f(x) _PP_EVAL(_PP_MAP58(f, __VA_ARGS__))
#define _PP_MAP60(f, x, ...) f(x) _PP_EVAL(_PP_MAP59(f, __VA_ARGS__))
#define _PP_MAP61(f, x, ...) f(x) _PP_EVAL(_PP_MAP60(f, __VA_ARGS__))
#define _PP_MAP62(f, x, ...) f(x) _PP_EVAL(_PP_MAP61(f, __VA_ARGS__))
#define _PP_MAP63(f, x, ...) f(x) _PP_EVAL(_PP_MAP62(f, __VA_ARGS__))
#define _PP_MAP64(f, x, ...) f(x) _PP_EVAL(_PP_MAP63(f, __VA_ARGS__))
#define _PP_GET_NTH_ARG( \
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
_61, _62, _63, _64, N, ...) N
#define _PP_MAP(f, ...) _PP_EVAL(_PP_EVAL(_PP_GET_NTH_ARG(__VA_ARGS__, \
_PP_MAP64, _PP_MAP63, _PP_MAP62, _PP_MAP61, \
_PP_MAP60, _PP_MAP59, _PP_MAP58, _PP_MAP57, _PP_MAP56, \
_PP_MAP55, _PP_MAP54, _PP_MAP53, _PP_MAP52, _PP_MAP51, \
_PP_MAP50, _PP_MAP49, _PP_MAP48, _PP_MAP47, _PP_MAP46, \
_PP_MAP45, _PP_MAP44, _PP_MAP43, _PP_MAP42, _PP_MAP41, \
_PP_MAP40, _PP_MAP39, _PP_MAP38, _PP_MAP37, _PP_MAP36, \
_PP_MAP35, _PP_MAP34, _PP_MAP33, _PP_MAP32, _PP_MAP31, \
_PP_MAP30, _PP_MAP29, _PP_MAP28, _PP_MAP27, _PP_MAP26, \
_PP_MAP25, _PP_MAP24, _PP_MAP23, _PP_MAP22, _PP_MAP21, \
_PP_MAP20, _PP_MAP19, _PP_MAP18, _PP_MAP17, _PP_MAP16, \
_PP_MAP15, _PP_MAP14, _PP_MAP13, _PP_MAP12, _PP_MAP11, \
_PP_MAP10, _PP_MAP09, _PP_MAP08, _PP_MAP07, _PP_MAP06, \
_PP_MAP05, _PP_MAP04, _PP_MAP03, _PP_MAP02, _PP_MAP01 \
))(f, __VA_ARGS__))
#if REFLECTION_WITH_FIELD_NAME
#define _PP_REFLECTION_FIELD_NAME(x) _PP_STR(_PP_EVAL(x))
#else
#define _PP_REFLECTION_FIELD_NAME(x) ""
#endif
#define _PP_REFLECTION_ALL(x) _PP_EVAL x
#define _PP_REFLECTION_SECOND(x) _PP_EAT x
#define _PP_REFLECTION_FIELD(x) _PP_REFLECTION_ALL(x);
#define _PP_REFLECTION_METHOD2(x) v(this, _PP_REFLECTION_FIELD_NAME(x), x);
#define _PP_REFLECTION_METHOD(x) _PP_REFLECTION_METHOD2(_PP_REFLECTION_SECOND(x))
#define _PP_REFLECTION_VISTOR_METHOD(type, ...) \
template <class Vistor> \
void _reflect(Vistor& v) type { \
_PP_MAP(_PP_REFLECTION_METHOD, __VA_ARGS__) \
}
#define REFLECT(...) \
_PP_MAP(_PP_REFLECTION_FIELD, __VA_ARGS__) \
_PP_REFLECTION_VISTOR_METHOD(_PP_EMPTY, __VA_ARGS__) \
_PP_REFLECTION_VISTOR_METHOD(const, __VA_ARGS__)
// Usage of REFLECT()
#define OBJECT_NAME_METHOD(obj) \
static const char* object_name() { \
return #obj; \
}
struct Demo
{
OBJECT_NAME_METHOD(Demo)
REFLECT(
(int) a,
(int) b,
(std::string) c
)
int d; // DO NOT REFLECT
};
struct Amplifier {
template <class Obj>
void apply(Obj* obj) {
obj->_reflect(*this);
}
template <class Obj, class Field>
void operator() (Obj* /*obj*/, const char* /*name*/, Field& field) {
field *= 100;
}
template <class Obj>
void operator() (Obj* /*obj*/, const char* /*name*/, std::string& field) {
field += "00";
}
};
struct Printer {
template <class Obj>
void print(const Obj& obj) {
obj._reflect(*this);
}
template <class Obj, class Field>
void operator() (Obj* obj, const char* name, const Field& field) {
std::cout << obj->object_name() << "."
<< name << ": " << field << std::endl;
}
};
int main() {
Demo a = {100, 200, "300", 400};
Amplifier amplifier;
amplifier.apply(&a);
Printer printer;
printer.print(a);
}
/*
Output:
Demo.a: 10000
Demo.b: 20000
Demo.c: 30000
*/
顺便说一句,以下代码用于生成所有_PP_MAP_?宏(用 JS 编写,因此您可以在 Web 浏览器中运行它)。
(function() {
const maxNumOfEle = 64;
const mapNamePrefix = '_PP_MAP';
let codeText = '';
function formatNumWidth(num) {
return ("0" + num).slice(-2);
}
function AddNewLine() {
if (codeText.slice(-1) != ' ') {
codeText += ' ';
}
codeText += '\\\n';
codeText += ' '.repeat(2);
}
codeText += `#define ${mapNamePrefix}${formatNumWidth(1)}(f, x) f(x)\n`;
for (let i = 2; i <= maxNumOfEle; ++i) {
let funId = formatNumWidth(i);
codeText += `#define ${mapNamePrefix}${funId}(f, x, ...) f(x)`;
let nextFunId = formatNumWidth(i - 1);
codeText += ' _PP_EVAL(';
codeText += `${mapNamePrefix}${nextFunId}(f, __VA_ARGS__)`;
codeText += ')';
codeText += '\n';
}
codeText += '\n#define _PP_GET_NTH_ARG(';
AddNewLine();
for (let i = 1; i <= maxNumOfEle; ++i) {
codeText += `_${i}, `;
if ((i % 10) == 0) {
AddNewLine();
}
}
codeText += 'N, ...) N\n';
codeText += `\n#define ${mapNamePrefix}(f, ...) `;
codeText += '_PP_EVAL(_PP_EVAL(_PP_GET_NTH_ARG(__VA_ARGS__,';
AddNewLine();
for (let i = maxNumOfEle; i >= 1; --i) {
let funId = formatNumWidth(i);
codeText += `${mapNamePrefix}${funId}`;
if (i != 1) {
codeText += ', ';
}
if ((i % 5) == 1) {
AddNewLine();
}
}
codeText += '))(f, __VA_ARGS__))\n';
console.log(codeText);
})();