我有一些结构,其中可能包含一定数量的无符号整数(它们是uint32_t或uint64_t)。我想以一种易于理解的方式打印出这些结构的值。 例如,我可能具有以下结构。
struct test {
uint32_t ab = 0;
uint64_t cd = 1;
uint32_t ef = 2;
};
我想我可以有一个方法传递此结构的地址,并使用此结构的大小将其打印出来。但不确定如何编写代码。
答案 0 :(得分:3)
如上所述,C ++ 11没有反射机制。获取成员列表的唯一方法是发挥自己的机制作用。由于您提到每个成员始终都是两种类型之一,因此应该很简单。例如,通过创建特征类。
template<class T>
struct mem_ptr {
// Tagged union type to generically refer to a member of a struct T
enum {u32, u64} alive;
union {
uint32_t T::* u32_ptr;
uint64_t T::* u64_ptr;
};
mem_ptr(uint32_t T::* u32_ptr) : alive(u32), u32_ptr(u32_ptr) {}
mem_ptr(uint64_t T::* u64_ptr) : alive(u64), u64_ptr(u64_ptr) {}
};
template<class> struct struct_members;
template<>
struct struct_members<test> {
mem_ptr<test> members[3] = {
&test::ab, &test::cd, &test::ef
};
};
template<class T>
auto operator<<(std::ostream& os, T const& str) -> decltype(struct_members<T>::members, os) {
struct_members<T> const mem;
char const *delim = "";
os << "{ ";
for(auto& m : mem.members) {
os << delim;
delim = ", ";
switch(m.alive) {
case m.u32: os << (str.*(m.u32_ptr)); break;
case m.u64: os << (str.*(m.u64_ptr)); break;
default: break;
}
}
os << " }";
return os;
}
将以上内容放入wandbox上的测试(双关语)中,打印:
{ 0, 1, 2 }
就位后,您只需为其定义struct_members
表即可添加对新结构的支持:
struct test2 {
uint32_t ab1 = 5;
uint64_t cd2 = 3;
uint32_t ef3 = 8;
};
template<>
struct struct_members<test2> {
mem_ptr<test2> members[3] = {
&test2::ab1, &test2::cd2, &test2::ef3
};
};
先前编写的流运算符也适用于此。
答案 1 :(得分:2)
如果您只需要基本了解内部内容,则可以将结构重新解释为uint32_t
-s数组,并以十六进制打印。
#include <iostream>
#include <iomanip>
struct test {
uint32_t ab = 0;
uint64_t cd = 1;
uint32_t ef = 2;
};
// For those who don't respect strict aliasing rules mentioned in comments
/*
template<class T>
void printStruct(const T& s)
{
auto* b = reinterpret_cast<const uint32_t*>(&s);
auto* e = b + sizeof(T)/sizeof(uint32_t);
std::cout << std::hex;
for (auto* i = b; i != e; ++i)
std::cout << std::setw(sizeof(uint32_t)*2) << std::setfill('0') << *i << ' ';
std::cout << std::dec;
}
*/
// For those who do respect strict aliasing rules mentioned in comments
template<class T>
void printStruct(const T& s)
{
const auto sc = sizeof(char);
const auto n = sizeof(uint32_t)/sc;
auto* b = reinterpret_cast<const unsigned char*>(&s);
auto* e = b + sizeof(T)/(sc*n);
std::cout << std::hex;
for (auto* i = b; i != e; i += n)
{
for (auto j = 0; j < n; ++j)
// For a big-endian machine n - 1 - j must be replaced by j
std::cout << std::setw(sc*2) << std::setfill('0') << *(i + n - 1 - j);
std::cout << ' ';
}
std::cout << std::dec;
}
int main()
{
printStruct(test());
return 0;
}
但是该例程还将打印对齐字节,并且在小字节序机器上,uint64_t
的两个部分将被反转。
例如在我的机器上可以打印
00000000 00000000 00000001 00000000 00000002 00007ffe
第一个00000000
是ab
,第二个00000000
是对齐的,00000001 00000000
是cd
,00000002
是{{1} }和de
再次对齐。
答案 2 :(得分:0)
一种选择是将std::tuple
用于您的结构。例如您的结构可以定义为:
typedef std::tuple< uint32_t, uint64_t, uint32_t > test;
然后您可以使用以下命令打印任何元组:
template<class Tuple, std::size_t N>
struct TuplePrinter {
static void print(const Tuple& t)
{
TuplePrinter<Tuple, N - 1>::print(t);
std::cout << ", " << std::get<N - 1>(t);
}
};
template<class Tuple>
struct TuplePrinter<Tuple, 1> {
static void print(const Tuple& t)
{
std::cout << std::get<0>(t);
}
};
template<class... Args>
void print(const std::tuple<Args...>& t)
{
std::cout << "(";
TuplePrinter<decltype(t), sizeof...(Args)>::print(t);
std::cout << ")\n";
}
如果您想找回命名的成员,则可以从元组派生一个结构,如下所示:
struct test : public std::tuple< uint32_t, uint64_t, uint32_t >
{
typedef std::tuple< uint32_t, uint64_t, uint32_t > base;
test()
: base( 0, 1, 2 ),
ab( std::get< 0 >( *this ) ),
cd( std::get< 1 >( *this ) ),
ef( std::get< 2 >( *this ) )
{
}
uint32_t& ab;
uint64_t& cd;
uint32_t& ef;
};
或者:
template<typename T>
void print(const T& t)
{
print(t.tuple);
}
struct test
{
uint32_t ab = 0;
uint64_t cd = 1;
uint32_t ef = 2;
typedef std::tuple< uint32_t&, uint64_t&, uint32_t& > tuple_t;
const tuple_t tuple = { ab, cd, ef };
};
或(由@ Jarod42建议):
void print(const test& t)
{
print(std::tie(t.ab, t.cd, t.ef));
}