Распечатать структуру с известным размером - PullRequest
0 голосов
/ 16 апреля 2019

У меня есть некоторые структуры, которые могут содержать определенное количество целых чисел без знака (это либо uint32_t, либо uint64_t). Я хочу распечатать значение этой структуры в понятной форме. Например, у меня может быть структура как ниже.

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

Я думаю, у меня мог бы быть метод, передающий адрес этой структуры и распечатавший его, используя размер этой структуры. Но не уверен, как написать код.

Ответы [ 3 ]

3 голосов
/ 16 апреля 2019

Как уже упоминалось, 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
    };
};

И оператор потока, написанный ранее, будет работать и для нее.

2 голосов
/ 16 апреля 2019

Если вам нужно просто базовое понимание того, что находится внутри, вы можете переосмыслить структуру как массив 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 - de и 00007ffe - выравнивание снова.

0 голосов
/ 16 апреля 2019

Одним из вариантов будет использование 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));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...