在C++中遍历结构体

60

我有一个结构。

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

我想遍历结构体的每个成员并打印它的值。类似于:

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

如何用C++实现这个?


先从简单的开始:您如何打印该Aa字段? - Beta
你的代码示例看起来像过时的 [tag:c] 代码。在我的答案中,我在旁注中已经修复了这些问题。 - sehe
我认为除非你的数据结构的字段在运行时才能确定,否则这个没有用处。而在这种情况下,typedef结构体并不是你尝试实现任何目标的正确方式。 - Jamin King
5
在C++14中,这变得可能了,可以参考这个讲座: CppCon 2016: C++14 Reflections Without Macros, Markup nor External Tooling.. https://www.youtube.com/watch?v=abdeAew3gmQ - Nir
1
请查看:相关的问题,我的示例解决方案以及令人惊叹的Boost.PFR - user1095108
这篇帖子值得根据反思TS给出答案。 - madhur4127
8个回答

40

也许您可以使用Boost Fusion/Phoenix拼接一些东西:

Coliru上实时查看!

#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");
}
更新:最新版本的boost可以使用C++11类型推导。
BOOST_FUSION_ADAPT_STRUCT(A,a,b,c);
1
42
The Answer To LtUaE

11
如果 Boost 能做到,那么我们在这里也可以做到,而且不需要 Boost... - Mikhail T.

24

您无法迭代访问对象的数据成员。但是,您可以使用std::ostream的流插入运算符逐个打印它们:

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

在 main 函数内部:

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

    std::cout << a;
}

输出:

5
10
apple sauce

这里是演示。


1
这使得我的调试工作更容易,帮助我立即发现了几个错误。 - Lorien Brune
请注意,将输出流函数添加到结构体中需要以特定方式声明该结构体,因此如果有人尝试实现此操作时出现错误,则结构体名称的位置(在本例中为 A)是一个很好的首要查找位置。 struct A { ... };typedef struct A { ... } A; 都可以,但是 typedef struct { ... } A; 会让你陷入麻烦。 - Lorien Brune
这并没有回答如何打印成员的名称,这是问题的一部分。 - Ð..
@Wand return os << "a: " << a.a << '\n' << ... - Tᴏᴍᴇʀ Wᴏʟʙᴇʀɢ
@TᴏᴍᴇʀWᴏʟʙᴇʀɢ 是的,那样做可以。我希望有一些更“反射性”的东西,不需要硬编码任何内容,但是我过去几个月一直在使用像Boost.Hana这样的“反射性”元编程库,它确实提供了对成员名称的迭代,结果发现像BOOST_HANA_DEFINE_STRUCT(请参见Hana中的结构概念)这样的宏最终仍然使用预处理器从其参数生成这些硬编码函数... - Ð..
@TᴏᴍᴇʀWᴏʟʙᴇʀɢ 在这个问题上类似于sehe的答案,与我使用Boost.Hana实现的“反射”类似,但是使用了Hana的BOOST_HANA_DEFINE_STRUCT宏而不是BOOST_FUSION_ADAPT_STRUCT。我想它们的实现方式也很相似... - Ð..

21
有几种方法可以实现这个目标,但是你需要使用一些宏来定义或调整结构体。
你可以使用在这个答案中给出的REFLECTABLE宏来像这样定义结构体:
struct A
{
    REFLECTABLE
    (
        (int) a,
        (int) b,
        (const char *) c
    )
};

然后您可以遍历字段并像这样打印每个值:

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

另一种方法是将结构体适应为融合序列(参见文档)。以下是一个示例:

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

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

那么您可以使用以下方式打印字段内容:

然后,您可以使用以下方法将字段打印出来:

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

17

C++本身不支持反射,因此您所要求的在核心语言中是不可能实现的。

一些库尝试提供这种功能,通常通过让您通过某些方法或宏注册字段,将它们保存到某种集合中,在该集合上可以进行迭代。


8

我能在标准c++中迭代结构体的成员吗?

不行,标准没有提供一种实现你所要求的功能的方法,你只能“迭代”容器的元素——无法迭代某种类型的成员。

反射”(此类功能通常被称为)不是C++的一部分。


在中,您可以使用std::tuple<int, int, char *>代替您的struct A,它将存储相同类型的元素,并且更容易迭代(使用一些模板魔术)。

这些元素将不会具有从'a''c'的名称,但如果您想以这种方式打印它,则可以通过一些额外的代码来完成。

要访问特定元素,您将使用std::get<N>(your_tuple),其中N是从0到std::tuple_size<std::tuple<int,int,char*>> ::value-1 (即2)的整数。



3
如果结构体中的所有字段都相同,您可以执行以下操作:
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);

这假设结构体成员之间没有填充。 - Zuzu Corneliu

3

根据 @Paul 的建议,我使用 BOOST_FUSION_ADAPT_STRUCT 与自己编写的 for_each_member 函数:

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

3

我写了一个版本,没有使用Boost或其他第三方库,已经使用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
*/

顺便提一下,下面的代码用于生成所有的_PP_MAP_? 宏 (用 JavaScript 写的,所以你可以在 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);
})();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接