LINUX.ORG.RU

Как вывести содержимое C-структуры?


2

2

После нескольких лет джаваскрипта осваиваю сишечку. Не хватает аналога console.dir(object), который выводит на экран содержимое объекта в виде:

поле: значение
... 

Возможно ли такое в си для структур?

★★★★★

Последнее исправление: makoven (всего исправлений: 2)

Возможно ли такое в си для структур?

Да.

ymn ★★★★★
()
Ответ на: комментарий от gh0stwizard

Значит-таки придется осваивать gdb)

makoven ★★★★★
() автор топика

ОП, Си — язык со строгой типизацией

Если у тебя есть структура, то ты наверняка знаешь её тип, вот и выводи её содержимое printf'ом

Northsoft ★★
()
Ответ на: ОП, Си — язык со строгой типизацией от Northsoft

Если структуру задавал не ТС, можно сделать какую-нибудь фигню вроде

typedef enum{
  TYPE_END = 0,
  TYPE_CHAR,
  TYPE_SHORT,
  TYPE_INT,
  TYPE_LONG,
  ...
} struct_types;

typedef struct{
  char name[128];
  struct_types type;
} struct_entry;
потом для нужных структур задать
struct_entry some_struct_def[size] = {
  {"device", TYPE_LONG},
  {"name", TYPE_STRING},
...
  {NULL, TYPE_END}
};
и выводить их как-то так (правда, могут быть косяки, если у тебя часть структур с выравниванием, а часть — без):
void printstruct(struct_entry *E, void *struct_ptr){
  while(*E.name && *E.type){
    switch(E.type){
      case TYPE_INT:
        printf("%s: %d\n", E.name, (int)*struct_ptr);
        struct_ptr += 4;
      break;
      ...
    }
    E++;
  }

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от anonymous

Используй toString.

В сишечке? Ну толсто же.

UVV ★★★★★
()

а чего без плюсиков? там можно для структур определить оператор << в исходящий поточек.

Sanik
()
Ответ на: комментарий от Sanik

там можно для структур определить оператор << в исходящий поточек

Человек хочет рефлексию, то, что ты ему предлагаешь, — это просто другой способ сделать то же самое, что и с принтфом

Gvidon ★★★★
()
Ответ на: комментарий от Eddy_Em

а есть нормальные парсеры для стандартного Си?

эту хреноту можно было бы генерить совершенно автоматически

stevejobs ★★★★☆
()
Ответ на: комментарий от stevejobs

Что ты подразумеваешь под "нормальными парсерами"?

эту хреноту можно было бы генерить совершенно автоматически

Т.к. это никому нахрен не нужно, то "стандартных" библиотек для этих извращений наверняка нет!

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Sanik

так для плюсиков-то есть решение

откуда-то с просторов интернета, не мое

Сначала, для чистоты кода, будем юзать типизированные выражения. Т.е. вместо int x будем писать (int) x. Вот нужный макрос:

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

Предполагается что мы будем юзать макрос REFLECTABLE, используемый вот так:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

Используя Boost.PP будем проходить по аргументам и генерировать данные:

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
    typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};


#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

Чтобы получить доступ к полям, которые приватные, этим кодом добавляется во френды вот такой класс:

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }

    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

Теперь пройдемся по полям, и отдадим эти данные в visitor:

struct field_visitor
{
    template<class C, class Visitor, class I>
    void operator()(C& c, Visitor v, I)
    {
        v(reflector::get_field_data<I::value>(c));
    }
};


template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

ну и наконец можно использовать!!!

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

И наконец, чтобы распечатать поля как хочется ТСу, можно написать такую штуку:

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());
}

Пример использования:

int main()
{
    Person p("sin_a", "ватник", "язабан");
    print_fields(p);
    return 0;
}

и выхлоп:

name= sin_a
credo=«ватник»
action=«язабан»

ура, мы надругались на C++, теперь в нём всё работает!

теперь нужно всё это стереть и перейти на нормальный язык, т.е. на Java или C#

stevejobs ★★★★☆
()

Если запилишь свою рефлексию (через DWARF или что там на венде) - да.

tailgunner ★★★★★
()
Ответ на: комментарий от Eddy_Em

Что ты подразумеваешь под «нормальными парсерами»?

ну например генератор парсеров AntLR автор в оригинале написал как раз для парсанья Си-кода. Он сдавал заказчику огромный кусок работы, написанный на Си. А заказчик сказал, что в комментариях нужно к вообще всему коду, над каждой функцией надписать, какие переменные какого типа в этом коде используются и сколько раз. Автор был достаточно ленив, чтобы ручками всё это выщитывать, поэтому он написал парсер, который раскладывал Си-код в AST, добывал из него нужную информацию и сваливал ее в комментарии.

Вот пример грамматики для современного AntLR: https://github.com/antlr/grammars-v4/blob/master/c/C.g4

Хотелось бы что-то такое, только реально использующееся в продакшене, гарантированно хорошо работающее =)

stevejobs ★★★★☆
()
Ответ на: комментарий от stevejobs

Всё делается ещё проще простым препроцессором

yoghurt ★★★★★
()

Как уже говорили, GDB умеет для дебаг-сборок такое. Общего решения для абсолютно всех типов нет - придется городить свой огород.

NegatiV
()
Ответ на: комментарий от NegatiV

Как уже говорили, GDB умеет для дебаг-сборок такое

GDB умеет это, когда у него есть доступ к DWARF, но наличие DWARF не означает «отладочную сборку».

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Не знаю как у вас, но у нас принято в дебаг-сборке передавать в gcc ключ -g, который включает генерацию DWARF/COFF/пр. То что там могут добавляться/изменяться дефайны и некоторые ключи компилятора - дело второе, хотя видимо лоровские самураи собирают дебаг-сборки без DDF - браво!

NegatiV
()
Последнее исправление: NegatiV (всего исправлений: 1)

Си - современный язык, в нем отладка делается отладчиком, а не отладочными print'ами, как в древних языках типа js.

unsigned ★★★★
()
Ответ на: комментарий от NegatiV

у нас принято в дебаг-сборке передавать в gcc ключ -g, который включает генерацию DWARF/COFF/п

А в релизной сборке у вас это не принято?

хотя видимо лоровские самураи собирают дебаг-сборки без DDF - браво!

Даже не стану спрашивать, какими путями эта мысль пришла к тебе в голову.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

А в релизной сборке у вас это не принято?

Не принято, т.к. место на sd-карте в поставляемых изделиях не резиновое - приходится экономить.

NegatiV
()
Ответ на: комментарий от NegatiV

А в релизной сборке у вас это не принято?

Не принято, т.к. место на sd-карте в поставляемых изделиях не резиновое - приходится экономить.

Похоже, делать отдельные пакеты с debug-инфой у вас тоже не принято. Ну что ж, бывает.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Похоже, делать отдельные пакеты с debug-инфой у вас тоже не принято. Ну что ж, бывает.

Кому делать? Внутри отдела нам это не нужно, тестировщики используют релиз-сборку с билдсервера; после того как софт с изделием ушел в поставку и если вдруг там что-то не так работает, то пользователи никуда не лезут руками и ничего не ставят, а вызывают специалиста к себе.

NegatiV
()

либо свой велосипед либо какую либу которая будет тебе макрой задавать твои структуры и сопровождать тем или иным путём через теги или ещё как

qulinxao ★★☆
()
Ответ на: комментарий от Eddy_Em

если самому менеджить динамику то можно по областям разделить где какие структуры , и тогда по указателю из «таблицы типов» вынимать представление.

qulinxao ★★☆
()
Ответ на: комментарий от unsigned

у js есть годные VM для отладки , однако среди лемингов эти VM ещё не достигли положения GDB(и прочих adb) в среде Слюбов.

qulinxao ★★☆
()

О, быдло рефлексирует!

Алсо, си после js? Нуну. Не трать силы)

anonymous
()
Ответ на: комментарий от stevejobs

Ты, жопс, своим передай, чтоб говнокодить перестали. Стыдно за iOS 8.0.1 и за ойфон новый стыдно.

anonymous
()
Ответ на: комментарий от NegatiV

Похоже, делать отдельные пакеты с debug-инфой у вас тоже не принято. Ну что ж, бывает.

Кому делать? Внутри отдела нам это не нужно, тестировщики используют релиз-сборку с билдсервера

Ну я и говорю - не принято, бывает.

tailgunner ★★★★★
()
Ответ на: комментарий от stevejobs

Passed

Тест пройден, всё работает правильно.

sin_a ★★★★★
()
Ответ на: комментарий от unsigned

Си - современный язык, в нем отладка делается отладчиком, а не отладочными print'ами, как в древних языках типа js.

Гыгыгы! Ты думаешь, таких, как я, — не умеющих пользоваться ни валгриндом, ни даже gdb, — мало? Да нас — легион!

А уж среди железячников тем паче!

// не вижу ничего плохого в использовании printf, у меня даже макросы есть отладочные, которые сыплют в консольку все, что нужно, если -DEBUG указать

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от stevejobs

Есть генераторы биндингов, для различных языков, в них зачастую есть и парсер. Например, есть SWIG. Можно подобрать подходящий для тебя язык с биндингами и из его биндингов нагенерить функцию print для твоей структуры. Или поправить SWIG, чтобы он сразу генерил функцию принт.

Расплатой за это будет поддержание двух версий заголовочных файлов, один для С, другой для SWIG. А может быть, и удастся обойтись одним.

А вообще, переходи на нормальный язык (например, на лисп).

den73 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.