Live data from Hacker News

Struct Iteration Through Abuse of the C Preprocessor

natecraun.net

21–27 of 27 posts

Re: Struct Iteration Through Abuse of the C Preprocessor

#21
post #15

Earlier quoted context omitted.

You can use e.g. cog which puts the generated code into the file itself: http://nedbatchelder.com/code/cog/ Then all you need to do is to remember to run the preprocessor after you've made a change to the generator code.

Python is definitely a better syntax to write meta-programs than the pre-processor, but you still have the issue of the additional step/tool. Anything that depends on the generated code depends on the step which, if it's missed, can lead to non-good things.

Sure. But you do test your changes, don't you? ;-) So if you forget to run cog after making a change to the meta-program, you should notice right away.

But it still is a cludge. It would be better if C/C++ had a better preprocessor.

Re: Struct Iteration Through Abuse of the C Preprocessor

#22
post #16

Earlier quoted context omitted.

I'm doing something similar in C++ for vertex attributes, integration with Lua et.al. First I define the struct: INTROSPECTION_DEFINE_STRUCT(S, (int, anInt), (bool, aBool), (float, aFloat), (double, aDouble), (std::string, aString) ); Then I create an instance of it: S s{ 23, true, 37.5f, 42.25, "String" }; And finally I can e.g. iterate over all fields and output their value using a generic lambda: forEachField(s, […

Is the code for this available somewhere?

Not at the moment, no. But it's not rocket science. The macro essentially creates a struct like this one:

  struct S {
    // How many fields are in this struct?
    static constexpr unsigned int FIELD_COUNT = 5;

    // Full specializations are not allowed inside classes, so add a dummy parameter  that is equivalent to S
    template 
    struct FieldTraits;

    // Partial specialization for the first field
    template 
    struct FieldTraits {
      using type = int;
      static constexpr std::size_t OFFSET = offsetof(S, anInt);
      static constexpr const char* name() { return "anInt"; }
      static constexpr type& value(S& self) { return self.anInt; }
    };
    // The first field itself
    int anInt;

    // Partial specialization for the second field
    template 
    struct FieldTraits {
      using type = bool;
      static constexpr std::size_t OFFSET = offsetof(S, aBool);
      static constexpr const char* name() { return "aBool"; }
      static constexpr type& value(S& self) { return self.aBool; }
    };
    // The second field itself
    bool aBool;
    ...
  };
Now that we have the meta-information about the struct's fields, we can process them:

  // Meta-function to process a struct's fields
  template 
  struct ForEach {
     template 
     static void apply(T& object, Functor f)
     {
        // Get the field's value and call the functor with it
        auto value = T::FieldTraits::value(object);
        f(value);

        // Next field
        ForEach::apply(object, f);
     };
  };

  // Partial specialization to end iteration
  template 
  struct ForEach {
     template 
     static void apply(T& object, Functor f)
     {
     };
  };
And finally a bit of syntactic sugar:

  template 
  void forEachField(T& object, Functor f)
  {
     ForEach::apply(object, f);
  }

*(omitted for the sake of clarity: dealing with const objects, using rvalue references and std::forward for the functor to make the code work with non-copyable functors)

Re: Struct Iteration Through Abuse of the C Preprocessor

#24
post #16

Earlier quoted context omitted.

Is the code for this available somewhere?

Not at the moment, no. But it's not rocket science. The macro essentially creates a struct like this one: struct S { // How many fields are in this struct? static constexpr unsigned int FIELD_COUNT = 5; // Full specializations are not allowed inside classes, so add a dummy parameter that is equivalent to S template struct FieldTraits; // Partial specialization for the first field template struct FieldTraits { using t…

Thanks for taking the time to write this!

Re: Struct Iteration Through Abuse of the C Preprocessor

#26
post #15

Earlier quoted context omitted.

Python is definitely a better syntax to write meta-programs than the pre-processor, but you still have the issue of the additional step/tool. Anything that depends on the generated code depends on the step which, if it's missed, can lead to non-good things.

Sure. But you do test your changes, don't you? ;-) So if you forget to run cog after making a change to the meta-program, you should notice right away. But it still is a cludge. It would be better if C/C++ had a better preprocessor.

It isn't a question of testing changes. The more people working on the codebase, the more people it impacts. Adding manual steps is tempting the fates.

Re: Struct Iteration Through Abuse of the C Preprocessor

#27

You can do some interesting things using __COUNTER__ to make code using this less ugly. And if you're willing to use a small amount of C++ templates, you can make it possible to resolve all the iteration/member info at compile time to eliminate runtime overhead. I did both of these things for Grim Fandango for vertex/uniform buffers, so that the game could use buffers without caring about whether it was using OpenGL…

I'm doing something similar in C++ for vertex attributes, integration with Lua et.al. First I define the struct: INTROSPECTION_DEFINE_STRUCT(S, (int, anInt), (bool, aBool), (float, aFloat), (double, aDouble), (std::string, aString) ); Then I create an instance of it: S s{ 23, true, 37.5f, 42.25, "String" }; And finally I can e.g. iterate over all fields and output their value using a generic lambda: forEachField(s, […

Have you used Boost.Fusion? This looks very much like BOOST_FUSION_ADAPT_STRUCT and seems to have similar design goals
Post reply on HN