web-dev-qa-db-fra.com

Itération sur une structure en C ++

J'ai une structure

typedef struct A
{
    int a;
    int b;
    char * c;
}aA;

Je veux parcourir chaque membre de la structure et imprimer sa valeur. Quelque chose comme:

void print_struct_value(struct *A)
{
    for each member of struct A
    cout << "struct name . member name" << "value";
}

Comment cela peut-il être fait en C++ ??

27
msd_2

Peut-être que vous pouvez enchaîner quelque chose ensemble en utilisant Boost Fusion/Phoenix:

Voir en direct sur Colir!

#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");
}

Production:

1
42
The Answer To LtUaE
25
sehe

Vous ne pouvez pas parcourir les membres de données d'un objet. Cependant, vous pouvez utiliser l'opérateur d'insertion de flux de std::ostream Pour imprimer individuellement:

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';
    }
};

Et à l'intérieur principal:

int main()
{
    A a = {5, 10, "Apple sauce"};

    std::cout << a;
}

Production:

5
dix
Compote de pommes

Voici une démo.

17
0x499602D2

Il existe plusieurs façons de le faire, mais vous devez utiliser certaines macros pour définir ou adapter la structure.

Vous pouvez utiliser la macro REFLECTABLE donnée dans cette réponse pour définir la structure comme ceci:

struct A
{
    REFLECTABLE
    (
        (int) a,
        (int) b,
        (const char *) c
    )
};

Et puis vous pouvez parcourir les champs et imprimer chaque valeur comme ceci:

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

Une autre façon est d'adapter la structure comme une séquence de fusion (voir la documentation ). Voici un exemple:

struct A
{
    int a;
    int b;
    const char * c;
};

BOOST_FUSION_ADAPT_STRUCT
(
    A,
    (int, a)
    (int, b)
    (const char *, c)
)

Ensuite, vous pouvez également imprimer les champs en utilisant ceci:

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)));
}
14
Paul Fultz II

C++ ne prend pas en charge la réflexion hors de la boîte, donc ce que vous demandez est impossible dans le langage de base.

Diverses bibliothèques essaient de fournir une telle fonctionnalité, généralement en vous faisant enregistrer vos champs via certaines méthodes ou macros, qui en arrière-plan les enregistreront dans une collection de quelque sorte, sur laquelle vous pourrez itérer.

9
small_duck

puis-je parcourir les membres d'une structure en c ++ standard?

Non, le standard c ++ ne fournit pas de méthode pour accomplir ce que vous demandez, vous "itérez" les éléments d'un conteneur - vous ne pouvez pas parcourir les membres d'un certain type.

" réflexion " (car ce type de fonctionnalité est le plus souvent mentionné ne fait pas partie de C++).


dans c ++ 11 vous pouvez utiliser un std::Tuple<int,int,char*> au lieu de votre struct A, il stockera le même type de éléments et est beaucoup plus facile à parcourir (en utilisant un modèle magique).

les éléments n'auront pas de noms allant de 'a' à 'c' mais si vous souhaitez l'imprimer de cette façon, cela peut bien sûr être accompli par quelques lignes de code supplémentaires.

Pour accéder à un élément spécifique, vous utiliserez std::get<N> (your_Tuple)N est une intégrale allant de 0 à std::Tuple_size<std::Tuple<int,int,char*>>::value - 1 (C'est-à-dire 2).


4

Comme suggéré par @Paul, j'utilise BOOST_FUSION_ADAPT_STRUCT avec une fonction for_each_member auto-écrite:

/**
 * \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 
1
daminetreg

J'ai écrit une version sans Boost ou autre bibliothèque tierce, qui a été testée en utilisant 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
*/

BTW, le code suivant est utilisé pour générer tous les _PP_MAP_? macros (écrites en JS, vous pouvez donc les exécuter dans un navigateur 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);
})();
0
Pamela

Si tous les champs de la structure sont identiques, vous pouvez le faire

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);
0
Larytet