{"id":107,"date":"2009-04-19T11:00:49","date_gmt":"2009-04-19T18:00:49","guid":{"rendered":"http:\/\/gameangst.com\/?p=107"},"modified":"2009-04-19T11:06:12","modified_gmt":"2009-04-19T18:06:12","slug":"class-reflection-in-the-despair-engine","status":"publish","type":"post","link":"http:\/\/gameangst.com\/?p=107","title":{"rendered":"Class Reflection in the Despair Engine"},"content":{"rendered":"<p>Reflection is, without a doubt, my favorite computer programming language feature. \u00a0It is unfortunate therefore that I&#8217;ve spent most of my professional life programming in C++, a language that has stubbornly refused to adopt a mechanism for class reflection. \u00a0There is a reason why I&#8217;ve stuck with C++ however; it is the highest level language available today that still provides unobstructed access to the hardware platform on which it sits. \u00a0Whatever its faults, it never\u00a0prevents me doing what I have to do.<\/p>\n<p>When <a href=\"http:\/\/gamearchitect.net\/about\/\">Kyle Wilson<\/a> and I were laying out the design for Day 1&#8217;s <a href=\"http:\/\/gamearchitect.net\/category\/despair-engine\/\">Despair Engine<\/a>, low-level support for reflection was a must-have on the list of engine features. \u00a0We were designing the largest, most ambitious engine either of us had ever worked on, and we didn&#8217;t want it to be clogged with thousands of lines of boilerplate code. \u00a0We wanted a simple, easy to use mechanism by which common libraries for disk serialization, UI generation, network synchronization, script integration, and debugging could operate intelligently on a diverse field of heterogeneous objects. \u00a0We&#8217;d both been down the route of virtual &#8220;Save&#8221; and &#8220;Load&#8221; calls implemented on every object as well as the route of <em>UIVehicle<\/em>\u00a0and <em>NetVehicle<\/em>\u00a0proxy objects, and we were intent on finding a better way. \u00a0At the same time, however, we were keenly aware of the dangers of over-generalizing.<\/p>\n<p>We knew that the engine we were designing would have to grow to support dozens of engineers and multiple simultaneous projects, and that it would have to be flexible enough to adapt to the unpredictable requirements of future games and hardware. \u00a0Taking all this into consideration, here is the list of goals we drafted for our reflection system:<\/p>\n<ul>\n<li>Zero reliance on custom compiler tools or non-standard compiler extensions. \u00a0100% portability was essential because we expected to support platforms whose compilers hadn&#8217;t even been written yet.<\/li>\n<li>Zero reliance on RTTI or exceptions. \u00a0Both of these features come with a cost and are regularly disabled or broken on consoles.<\/li>\n<li>No header pollution. \u00a0Reflection definitions had to be confined to cpp files so that compile time costs could be minimized and so that modifying a reflection definition didn&#8217;t require a full rebuild.<\/li>\n<li>Minimal reliance on macros. \u00a0Provide type safety and lexical scoping whenever possible.<\/li>\n<li>Zero reliance on external data. \u00a0Reflection metadata had to be available at static initialization time, tightly bound to class definitions.<\/li>\n<li>Efficient support for native types like __m128 and custom low-level types like struct Vector2 { };<\/li>\n<li>Support for sophisticated but non-polymorphic types like standard library containers.<\/li>\n<li>Support for polymorphic classes and multiple inheritance of abstract interfaces. \u00a0We use a COM-like architecture in our engine which clearly separates objects from interfaces. \u00a0We deliberately chose not to tackle virtual inheritance with this system.<\/li>\n<li>External extensibility. \u00a0Reflection definitions had to support metadata specific to systems that the reflection library itself had no knowledge of. \u00a0Adding new types or attributes to the system shouldn&#8217;t require a massive rebuild.<\/li>\n<li>Support for overriding or circumventing the system for special cases.<\/li>\n<\/ul>\n<p>In addition to these general requirements for the system, we had several specific uses for reflection already in mind which added their own requirements:<\/p>\n<ul>\n<li>Disk serialization using the reflection system had to support backward and, to some degree, forward compatibility.<\/li>\n<li>Network synchronization using the reflection system had to be able to operate on a subset of the class data and to efficiently extract deltas from class state.<\/li>\n<li>UI generation from reflection definitions had to provide context-specific as well as type-specific widgets. \u00a0A &#8220;radius&#8221; property for a volume trigger, for example, should be able to provide a 3-D visualization as well as a range-limited edit box, context-sensitive help, and notifications to other UI elements when it changes.<\/li>\n<li>Scripting languages like Lua and Python had to be able to dynamically bind to objects using their reflection definitions, but once bound they should be able to operate on the objects efficiently without requiring table lookups or string comparisons.<\/li>\n<\/ul>\n<p>It fell on my shoulders to provide the initial design and implementation of Despair&#8217;s reflection library, dsReflection. \u00a0Luckily reflection is nothing new to games, so I had a lot of inspiration to draw upon. \u00a0One of my favorite early implementations of reflection in a game engine is in the <a href=\"http:\/\/nebuladevice.cubik.org\/\">Nebula Device<\/a>. \u00a0The Nebula Device&#8217;s approach to reflection was fairly straightforward&#8211;hard coded companion functions for every object populated tables with metadata like command names and function pointers&#8211;but it was used to great effect to expose a great deal of the engine to Tcl and from there to perform serialization and network synchronization.<\/p>\n<p>Although the Nebula Device was a great proof-of-concept for me, it didn&#8217;t meet many of the requirements Kyle and I had put forth. \u00a0It was very limited in the kind of data it could reflect and syntactically it was messy and required a lot of boilerplate code. \u00a0The inspiration for the syntax of Despair&#8217;s reflection definitions came instead from C# and <a href=\"http:\/\/www.rasterbar.com\/products\/luabind.html\">luabind<\/a>. \u00a0We hoped to be writing a lot of tool and support code for the engine in C# so it made sense to try to match its syntax, and luabind is an ingenious example of how C++ compilers can be manipulated into parsing very unC++like code.<\/p>\n<p>What I ended up with is an embedded, domain-specific language implemented through a combination of clever operator overloading and template metaprogramming. \u00a0Many of the core concepts in dsReflection are enabled by features of <a href=\"http:\/\/www.boost.org\/doc\/libs\/1_38_0?view=category_Metaprogramming\">Boost&#8217;s metaprogramming tools<\/a>. \u00a0The reflection definition language is used to construct metadata for classes. \u00a0The metadata holds references to fields and methods of a class. \u00a0Fields can be raw member pointers as well as combinations of getters and setters. \u00a0Methods can be member functions with variable numbers of parameters of mixed types. \u00a0Methods can expose concrete, virtual, and even pure virtual functions. \u00a0All reflected elements can be tagged with user-defined properties similar to <em>Attributes<\/em>\u00a0in C#. \u00a0These properties are how external systems annotate classes, fields, and methods with information specific to their needs. \u00a0Reflection definitions can be applied to non-polymorphic classes, but to take advantage of the full feature set reflected classes must derive from <em>Reflection::Object<\/em> which introduces a single virtual function. \u00a0In its initial implementation dsReflection supported global and file static variables and functions through reflection namespace definitions. \u00a0This capability still exists, but we&#8217;ve found little use for it and I&#8217;ve often considered removing it entirely.<\/p>\n<p>At run time the reflection system provides a wide range of services. \u00a0It allows for RTTI-like queries for comparing object types and inheritance hierarchies and it allows for type-safe dynamic casts between reflection objects. \u00a0Class definitions for objects can be inspected at run time, and their component elements can be operated on in a variety of ways. \u00a0Fields can be read from and written to, and they can also be bound to strongly-typed, templatized wrapper objects called <em>Reflection::Variable&lt;T&gt;<\/em>. \u00a0Similarly, methods can be called with parameters provided through an abstract interface or they can be bound directly to <a href=\"http:\/\/www.boost.org\/doc\/libs\/1_38_0\/doc\/html\/function.html\">boost::function<\/a> objects. \u00a0Property objects can be extracted from all reflected elements and the properties themselves are reflection objects so they support the same rich level of introspection.<\/p>\n<p>The Despair Engine is about four years old now and it has the battle scars to prove it. \u00a0The reflection library is one of the foundational elements of the engine, and it has held up remarkably well under the strain. \u00a0As we hoped, dsReflection is leveraged to support all the systems describe above, as well as several others that we never anticipated. \u00a0It is the kind of feature that once you have it, you can&#8217;t imagine life without it.<\/p>\n<p>That said, there have been some struggles along the way. \u00a0The biggest struggle with the reflection library has been keeping compiler performance and executable code bloat under control. \u00a0These were both serious concerns going into the system which we attempted to address in the design. \u00a0The system does meet the requirement that class reflection definitions are specified in cpp files and not in headers, but nevertheless the reflection definitions for classes can be big (<em>really big!<\/em>) and there is a lot for the compiler to wade through. \u00a0A bigger problem for us than the compile time has been the amount of code generated by the compiler to instantiate the reflection definitions. \u00a0Reflection definitions are built automatically at static initialization time, so run time performance and memory consumption aren&#8217;t an issue, but I&#8217;ve twice had to revisit the system&#8217;s internals to optimize its code gen and bring our link times and final executable size back to within reasonable limits. \u00a0Both Sony and Microsoft have shared in this pain; few engines out there put pressure on a linker like ours.<\/p>\n<p>Another struggle with the reflection system has been training and documentation. \u00a0It is a powerful and flexible language which looks only vaguely like C++, and learning all the ways in which one can and, perhaps more importantly, should use the system is a challenge. \u00a0Perhaps the best decision we made in this regard was to provide a comprehensive set of unit tests for the library. \u00a0Unit tests aren&#8217;t the best form of documentation, but they can be invaluable at enforcing undocumented and poorly specified behaviors in systems for which stability and backward compatibility are essential. \u00a0Our unit tests provide an answer of last resort to questions like, &#8220;can I reflect a std::map&lt;enum, object&gt; and have the serialization maintain backward compatibility as enum values are changed, added, and removed?&#8221; \u00a0The magic unit test says &#8220;yes!&#8221;<\/p>\n<p>Detailing the exact workings of dsReflection is way beyond the scope of this article, but I can provide a sample reflection definition that gives a pretty clear picture of how the system plays out in practice. \u00a0This sample isn&#8217;t something that actually exists in any game, but it is a completely valid reflection definition that would compile with our code. \u00a0I haven&#8217;t tried to hide any of the messiness. \u00a0Most of the complexity comes from specifying a pretty sophisticated user interface with custom widgets and data-dependent views, but that&#8217;s hardly unusual. \u00a0When our components expose a UI, most of the time we&#8217;re pushing for <em>more customized, more specialized<\/em>\u00a0behavior, even though it makes the reflection definitions tricky.<\/p>\n<blockquote>\n<div class=\"dean_ch\" style=\"white-space: nowrap;\"><span class=\"co1\">\/\/ DecalObject.h<\/span><\/p>\n<p><span class=\"kw2\">class<\/span> IDecalObject<br \/>\n<span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"co1\">\/\/ abstract interfaces can carry reflection definitions<\/span><br \/>\n&nbsp; &nbsp; REFLECTION_CLASS_DECLARE<span class=\"br0\">&#40;<\/span>IDecalObject<span class=\"br0\">&#41;<\/span>;<br \/>\n<span class=\"br0\">&#125;<\/span>;<\/p>\n<p><span class=\"kw2\">class<\/span> DecalObject : <span class=\"kw2\">public<\/span> Reflection::<span class=\"me2\">Object<\/span>, <span class=\"kw2\">public<\/span> IDecalObject<br \/>\n<span class=\"br0\">&#123;<\/span><br \/>\n<span class=\"kw2\">public<\/span>:<br \/>\n&nbsp; &nbsp; <span class=\"kw4\">const<\/span> string&amp; GetTextureFilename<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"kw4\">const<\/span>;<br \/>\n&nbsp; &nbsp; <span class=\"kw4\">void<\/span> SetTextureFilenameBlocking<span class=\"br0\">&#40;<\/span><span class=\"kw4\">const<\/span> string&amp; filename<span class=\"br0\">&#41;<\/span>;<\/p>\n<p>&nbsp; &nbsp; <span class=\"kw2\">enum<\/span> DecalType<br \/>\n&nbsp; &nbsp; <span class=\"br0\">&#123;<\/span><br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; kDecalType_Static,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; kDecalType_Random,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; kDecalType_Special<br \/>\n&nbsp; &nbsp; <span class=\"br0\">&#125;<\/span>;<\/p>\n<p>&nbsp; &nbsp; ResultCode StartLoggingDebugStats<span class=\"br0\">&#40;<\/span><span class=\"kw4\">const<\/span> <span class=\"kw4\">char<\/span>* filename, <span class=\"kw4\">bool<\/span> echoToScreen<span class=\"br0\">&#41;<\/span>;<br \/>\n&nbsp; &nbsp; <span class=\"kw4\">void<\/span> StopLoggingDebugStats<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span>;<\/p>\n<p><span class=\"kw2\">private<\/span>:<br \/>\n&nbsp; &nbsp; string GetAspectRatio<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"kw4\">const<\/span>; &nbsp;<span class=\"co1\">\/\/ returns aspect ratio of currently loaded texture<\/span><br \/>\n&nbsp; &nbsp; <span class=\"kw4\">bool<\/span> IsRandomType<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"kw4\">const<\/span>; &nbsp; &nbsp; &nbsp;<span class=\"co1\">\/\/ helper checks if m_decalType is random<\/span><\/p>\n<p>&nbsp; &nbsp; ResourceKey &nbsp; &nbsp; &nbsp; &nbsp; m_textureResource;<br \/>\n&nbsp; &nbsp; DecalType &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m_decalType;<br \/>\n&nbsp; &nbsp; <span class=\"kw4\">float<\/span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; m_maximumImpactAngle;<br \/>\n&nbsp; &nbsp; std::<span class=\"me2\">vector<\/span>&lt;Color&gt; &nbsp;m_randomColorPalette;<br \/>\n&nbsp; &nbsp; <span class=\"kw4\">bool<\/span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;m_applyToSkinnedObjects;<\/p>\n<p>&nbsp; &nbsp; REFLECTION_CLASS_DECLARE<span class=\"br0\">&#40;<\/span>DecalObject<span class=\"br0\">&#41;<\/span>;<br \/>\n<span class=\"br0\">&#125;<\/span>;<\/p>\n<p>REFLECTION_ENUM_DECLARE<span class=\"br0\">&#40;<\/span>DecalObject::<span class=\"me2\">DecalType<\/span><span class=\"br0\">&#41;<\/span>;<\/div>\n<\/blockquote>\n<blockquote>\n<div class=\"dean_ch\" style=\"white-space: nowrap;\"><span class=\"co1\">\/\/ DecalObject.cpp<\/span><\/p>\n<p><span class=\"co2\">#include &quot;DecalObject.h&quot;<\/span><\/p>\n<p><span class=\"co1\">\/\/ empty reflection definition for IDecalObject<\/span><br \/>\nREFLECTION_CLASS_DEFINE<span class=\"br0\">&#40;<\/span>IDecalObject<span class=\"br0\">&#41;<\/span>;<\/p>\n<p>REFLECTION_ENUM_DEFINE<span class=\"br0\">&#40;<\/span>DecalObject::<span class=\"me2\">DecalType<\/span><span class=\"br0\">&#41;<\/span><br \/>\n.<span class=\"me1\">name<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;static&quot;<\/span>, &nbsp; &nbsp; DecalObject::<span class=\"me2\">kDecalType_Static<\/span><span class=\"br0\">&#41;<\/span><br \/>\n.<span class=\"me1\">name<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;random&quot;<\/span>, &nbsp; &nbsp; DecalObject::<span class=\"me2\">kDecalType_Random<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Description<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;A randomly tinted decal.&quot;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"co1\">\/\/ custom help for UI \/ debug console<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">name<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;special&quot;<\/span>, &nbsp; &nbsp;DecalObject::<span class=\"me2\">kDecalType_Special<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">EditEnumPrivate<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> <span class=\"co1\">\/\/ code only, not to be exposed to UI<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n;<\/p>\n<p>REFLECTION_CLASS_DEFINE<span class=\"br0\">&#40;<\/span>DecalObject<span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">GroupProperty<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Graphics&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">FriendlyName<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Decal Component&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Description<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Creates a decal at the current location.&quot;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">field<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;textureResource&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">m_textureResource<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span> &nbsp; <span class=\"co1\">\/\/ direct serialization of a custom ResourceKey type<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Save<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> + Prop::<span class=\"me2\">Copy<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">field<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;textureFilename&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">GetTextureFilename<\/span>,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &amp;DecalObject::<span class=\"me2\">SetTextureFilenameBlocking<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span> &nbsp; <span class=\"co1\">\/\/ redundant exposure of m_textureResource but through more UI convenient,<\/span><br \/>\n&nbsp; &nbsp; <span class=\"co1\">\/\/ blocking member functions<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">FriendlyName<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Texture&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Description<\/span><span class=\"br0\">&#40;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"st0\">&quot;Specifies the texture that will be applied by the decal. &nbsp;The aspect ratio of the &quot;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"st0\">&quot;texture will define the aspect ratio of the decal.&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">EditResourcePicker<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;tif;png&quot;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">field<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;aspectRatio&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">GetAspectRatio<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span> &nbsp; <span class=\"co1\">\/\/ read-only exposure of a fake, UI only field<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">FriendlyName<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Aspect Ratio&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Description<\/span><span class=\"br0\">&#40;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"st0\">&quot;The aspect ratio of the decal. &nbsp;This is determined by the texture selected for &quot;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"st0\">&quot;the decal.&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">EditText<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">field<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;decalType&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">m_decalType<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">FriendlyName<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Type&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">EditEnumCombo<\/span>&lt;DecalObject::<span class=\"me2\">DecalType<\/span>&gt;<span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> + <span class=\"co1\">\/\/ provide a combo box tailored to <\/span><br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class=\"co1\">\/\/ this enum<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Save<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> + Prop::<span class=\"me2\">Copy<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">field<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;maxImpactAngle&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">m_maximumImpactAngle<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">FriendlyName<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Maximum Impact Angle&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">EditAngle<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> + <span class=\"co1\">\/\/ field holds radians, UI presents as degrees<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Save<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> + Prop::<span class=\"me2\">Copy<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">field<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;paletteSize&quot;<\/span>,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;Reflection::<span class=\"me2\">GetSize<\/span><span class=\"br0\">&#40;<\/span>&amp;DecalObject::<span class=\"me2\">m_randomColorPalette<\/span><span class=\"br0\">&#41;<\/span>,<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp;Reflection::<span class=\"me2\">SetSize<\/span><span class=\"br0\">&#40;<\/span>&amp;DecalObject::<span class=\"me2\">m_randomColorPalette<\/span><span class=\"br0\">&#41;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span> &nbsp; <span class=\"co1\">\/\/ Helpers expose a std::vector as resizeable within the UI. Fancier definitions can<\/span><br \/>\n&nbsp; &nbsp; <span class=\"co1\">\/\/ provide custom dialogs for modifying container types<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">FriendlyName<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Random Palette Size&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Filter<\/span>&lt;DecalObject&gt;<span class=\"br0\">&#40;<\/span>&amp;DecalObject::<span class=\"me2\">IsRandomType<\/span><span class=\"br0\">&#41;<\/span> + <span class=\"co1\">\/\/ only random decals expose<\/span><br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class=\"co1\">\/\/ a color palette<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">EditNumeric<\/span><span class=\"br0\">&#40;<\/span><span class=\"nu0\">0<\/span>, <span class=\"nu0\">100<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Save<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> + Prop::<span class=\"me2\">Copy<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">field<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;palette&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">m_randomColorPalette<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span> &nbsp; <span class=\"co1\">\/\/ Type traits allow the reflection system to operate on templatized standard library<\/span><br \/>\n&nbsp; &nbsp; <span class=\"co1\">\/\/ and custom container types, including associative containers like std::map<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">FriendlyName<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;Random Palette&quot;<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Filter<\/span>&lt;DecalObject&gt;<span class=\"br0\">&#40;<\/span>&amp;DecalObject::<span class=\"me2\">IsRandomType<\/span><span class=\"br0\">&#41;<\/span> +<br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">EditContainer<\/span><span class=\"br0\">&#40;<\/span> Prop::<span class=\"me2\">EditColorPicker<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span>, <span class=\"st0\">&quot;Color Entry&quot;<\/span> <span class=\"br0\">&#41;<\/span> + &nbsp;<span class=\"co1\">\/\/ The container<\/span><br \/>\n&nbsp; &nbsp; <span class=\"co1\">\/\/ holds a simple type so addition properties are specified for it. &nbsp;Containers can<\/span><br \/>\n&nbsp; &nbsp; <span class=\"co1\">\/\/ also hold full reflection objects with their own reflection definitions.<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Save<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span> + Prop::<span class=\"me2\">Copy<\/span><span class=\"br0\">&#40;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n<span class=\"co2\">#ifdef _DEBUG<\/span><br \/>\n<span class=\"co1\">\/\/ a few fields and methods for debug console<\/span><br \/>\n.<span class=\"me1\">method<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;StartLoggingDebugStats&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">StartLoggingDebugStats<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#91;<\/span><br \/>\n&nbsp; &nbsp; Prop::<span class=\"me2\">Description<\/span><span class=\"br0\">&#40;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"st0\">&quot;StartLoggingDebugStats log_file_name echo_to_screen<span class=\"es0\">\\n<\/span>&quot;<\/span><br \/>\n&nbsp; &nbsp; <span class=\"st0\">&quot; &nbsp;Logs memory and performance stats to a file and, optionally, the screen.&quot;<\/span><span class=\"br0\">&#41;<\/span><br \/>\n<span class=\"br0\">&#93;<\/span><br \/>\n.<span class=\"me1\">method<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;StopLoggingDebugStats&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">StopLoggingDebugStats<\/span><span class=\"br0\">&#41;<\/span><br \/>\n.<span class=\"me1\">field<\/span><span class=\"br0\">&#40;<\/span><span class=\"st0\">&quot;applyToSkinnedObjects&quot;<\/span>, &amp;DecalObject::<span class=\"me2\">m_applyToSkinnedObjects<\/span><span class=\"br0\">&#41;<\/span> <span class=\"co1\">\/\/ No props, debug<\/span><br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class=\"co1\">\/\/ console only<\/span><br \/>\n<span class=\"co2\">#endif<\/span><br \/>\n;<\/div>\n<\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Reflection is, without a doubt, my favorite computer programming language feature. \u00a0It is unfortunate therefore that I&#8217;ve spent most of my professional life programming in C++, a language that has stubbornly refused to adopt a mechanism for class reflection. \u00a0There is a reason why I&#8217;ve stuck with C++ however; it is the highest level language [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[8,11],"_links":{"self":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/107"}],"collection":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=107"}],"version-history":[{"count":24,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/107\/revisions"}],"predecessor-version":[{"id":109,"href":"http:\/\/gameangst.com\/index.php?rest_route=\/wp\/v2\/posts\/107\/revisions\/109"}],"wp:attachment":[{"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=107"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=107"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/gameangst.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=107"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}