打印已知大小的结构

时间:2019-04-16 06:25:25

标签: c++ c++11

我有一些结构,其中可能包含一定数量的无符号整数(它们是uint32_t或uint64_t)。我想以一种易于理解的方式打印出这些结构的值。 例如,我可能具有以下结构。

struct test {
    uint32_t ab = 0;
    uint64_t cd = 1;
    uint32_t ef = 2;
};

我想我可以有一个方法传递此结构的地址,并使用此结构的大小将其打印出来。但不确定如何编写代码。

3 个答案:

答案 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

第一个00000000ab,第二个00000000是对齐的,00000001 00000000cd00000002是{{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));
}