Autoreflect is a small C++ code generator developed for a yet unreleased project called Schneeflocke (so the executable is called sfautoreflect). It was created in a christmas bet (who can build the coolest program within 2 weeks without actually having much time). I decided to release it as It could be usable in a broader context.

You will find on Github as part of the sfserialization package.

What does it do?
It parses a C++ Header File and tries to filter out usable information. In the moment it can parse Struct/Class Members (including their inheritance) and Enums. Templates and Member functions are not yet supported. With the information it feeds so called Generators for various issues (in the moment two systems are implemented).

  • The EnumGenerator¬†creates toString() and fromString() functions. These functions translate the actual enum value to its String representation.
  • SerializationGenerator. This generate generates serialize() and deserialize() methods for marked structs

Why could this be useful? It's possible to write that by Hand!

Indeed it is possible - and for very small programs I would also write this functions by hand. But if you have often chaning data structures, writing to/from-Enum-Functions and serializers/deserializers by hand is a violation of the DRY-principle. Also writing these functions by hand is more error prone than using a code generator.

Ok, I would like to try, how can I build it?

Building should be easy. As autoreflect has the dependency to sfserialization (although its shipped in the same package) it does need the Boost C++-Library headers. I choose cmake as a build system as it provides a minimal testing environment and can generate project files for Make, Eclipse CDT and Visual Studio. For Win32-Users: please follow the instructions of the CMake GUI to create project files. Unix users can simply check out the repository (e.g. in SRC/) and create a binary directory (BIN/). Then they start

cmake ../path/to/source/directory

Cmake should find boost automatically and will set up Makefiles for you. Just enter "make" and it builds. "make install" will install autoreflect binaries in /usr/local/bin, but you can also start it from its build-bin directory (in BIN/sfautoreflect), or choose another installation directory (via "-DCMAKE_INSTALL_PREFIX=$DIR" argument). If you want to try out the testcases, just enter "make test" after building. If they fail, please let me know.

I got trough the building process, how can I try it?

Let's create a header file enumtest.h

#include <sfserialization/autoreflect.h>
enum Week {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
};
SF_AUTOREFLECT_ENUM (Week);

SF_AUTOREFLECT_ENUM in fact is a macro defined in the autoreflect.h header which creates the function declaration for you

const char * toString (Week v);
bool fromString (const char* s, Week & v);

The fromString returns whether it succeded to convert the string s to a Weekvalue.

Now let's generate the code, call:
sfautoreflect week.h -o week_gen.cpp
and you will get:

// *****************************************************************************
// This file is auto generated using sfautoreflect (http://cgvis.de/autoreflect)
// All changes will probably overwritten
// Resistance is futile
// *****************************************************************************
#include "week.h"
#include <assert.h>
#include <string.h>
const char* toString (Week e){
	const char* values[] = {
		 "Monday"
		, "Tuesday"
		, "Wednesday"
		, "Thursday"
		, "Friday"
		, "Saturday"
		, "Sunday"
	};
	int x = (int) e;
	if (x < 0 || x >= 7){
		assert (false &amp;&amp; "undefined enum value");
		return "UNDEFINED";
	}
	return values[x];
}

bool fromString (const char* s, Week &amp;e){
	// using a hash table
	// lookup structure
	const int hashTable[] = {0, 2, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 18, 19, 20, 23, 24};
	// hash table
	struct HashEntry { const char * name; Week value; };
	const HashEntry entries[] = {
		{"Wednesday", Wednesday}
		,{0, Week()}
		,{"Monday", Monday}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{"Tuesday", Tuesday}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{"Sunday", Sunday}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{0, Week()}
		,{"Thursday", Thursday}
		,{"Saturday", Saturday}
		,{0, Week()}
		,{0, Week()}
		,{"Friday", Friday}
		,{0, Week()}
	};
	// lookup
	int key = sf::hash ((unsigned char*)s) % 19;
	const HashEntry * entry = entries + hashTable[key];
	while (entry->name != 0){
		if(strcmp (entry->name, s) == 0) {e = entry->value; return true; }
		entry++;
	}
	// not found
	return false;
}

The fromString-Function is using an hash table for the conversion from Strings back to the enum. Thats why it looks a bit complex. In contrast to just comparing each string it performs in O(1).

How to integrate this into a build system?

For Make it's easy: Just add a specific rule to the Make system, for which you want to build it. Suppose you have a program "week" which contains week.cpp and week.h. For week.h you want sfautoreflect support, then you have to adjust your Makefile for something like this:

# Path where you installed sfserialization
SFDIR=/home/nos/opt/sfserialization
AUTOREFLECT=${SFDIR}/bin/sfautoreflect
INCLUDES=-I${SFDIR}/include
	
week: week.cpp week_reflect.cpp week.h
	g++ ${INCLUDES} week_reflect.cpp week.cpp -o week
week_reflect.cpp: week.h
	${AUTOREFLECT} week.h -o week_reflect.cpp

For cmake I used another approach. One process scans all files for the magic SF_AUTOREFLECT_-Macro and adds this file to be compiled automatically with autoreflect. Details can be found in the CMakeLists.txt in the testcases folder.

And so whats with the serialization?
Oh, automatic serialization is the main function of sfserialization. It consists of a JSON-Parser (wrapped by a class Deserializer) and a Serializer (class Serialization). For documentation how to use it without autoreflect I would suggest you to the documentation in the header files and README file. With autoreflect its too use like this:

Suppose an object Greeter in hello.h

#include <sfserialization/autoreflect.h>
#include <string>
		
struct Greeter {
	int x;
	int y;
	std::string part1;
	std::string part2;
	SF_AUTOREFLECT_SD;
};
and a hello.cpp:
#include <iostream>
#include <sfserialization/Serialization.h>
#include "hello.h"
int main (int argc, char * argv[]){
	Greeter g;
	g.x = 5;
	g.y = 3;
	g.part1 = "Hello";
	g.part2 = "World";
	std::cout << sf::toJSON (g) << std::endl;
	return 0;
}

You can build it with this Makefile:

# Modified Makefile which also links to libsfserialization.a
SFDIR=/home/nos/opt/sfserialization
AUTOREFLECT=${SFDIR}/bin/sfautoreflect
INCLUDES=-I${SFDIR}/include
LIBS=${SFDIR}/lib/libsfserialization.a
	hello: hello.cpp hello_reflect.cpp hello.h
	g++ ${INCLUDES} hello_reflect.cpp hello.cpp ${LIBS} -o hello
hello_reflect.cpp: hello.h
	${AUTOREFLECT} hello.h -o hello_reflect.cpp
The SF_AUTOREFLECT_SD macro will generate the (member) function declarations used for the (de-)serialization. When you the final program, you will get the following:
{"x":5, "y":3, "part1":"Hello", "part2":"World"}
The SF_AUTOREFLECT_SD macro has to be put inside the class as it shall define member functions. The serializers will also serialize parent classes and embedded enums (when they are reflected using SF_AUTOREFLECT_ENUM ;)).