Home > Coding > And now for something completely similar.

And now for something completely similar.

So I’m back on the job market. Lots of ideas and inspiration from my time at Blizzard, a really great place to work. No, I won’t tell you. I already have a few job apps out, and I’m trying to stick to the US west coast and gaming.

I’ve been in the games industry proper for near 12 years now, and while that’s where I’m going to look first, I’m going to be open to possibilities back in the real world.

Right now I’m toying with the idea of a C++-centric code templator I’m thinking of calling “C[]” (C-squared); something that can handle trivial cases like “take this list of words and generate an enum statement and an array of strings” to the ability to generate multiple complex code modules from shared definitions.

For example, many client-server systems forward database queries through a dedicated thread/process.

To do this fully asynchronously you have to marshal the inputs for the query, serialize the request, forward to the proxy, receive on the proxy, deserialize, create the SQL query and a callback capture to handle the response, dispatch the query, receive the response and match it to the callback capture, process the SQL response, translate the result set into your internal representation, serialize, send back to the originator, deserialize the response, look up the original query and check it’s still valid, and you’re about done.

And that’s just for a simple server query. If the data is going to the client to the ui, there’s probably a server->client message to be formatted, there’s probably a different in-memory layout needed for the client, and potentially some lookups, maybe some client scripting to map the fields in that layout to widgets on the screen…

Unfortunately, C++ translates this into a metric hell of boiler-plate cruft.

You could write some really fancy abstract, maybe templated, systems for dealing with the majority of this – still a lot of boiler plate (“I wish to make a Thing. It will not catch fire, nor shall it explode, nor shall have the might of three men. But the thing shall haveth a widget and the widget shall make it fine;” vs “fine: Thing + widget;”)

IMHO, at this point code generation is a better approach.

Code gens let you encode both big and little picture optimization logic at a high level, essentially combining the fact you’re using low-level C++ with the benefits of a scripting language.

Trouble is, codegens can be messy, and so people often create overly specific codegens. One for the serialize/deserialize. Another for describing marshalling structures. The more you create, the more overhead the next one introduces and so people wind up with gaping holes in their otherwise nicely generated infrastructure and bad things begin to happen (omg, you didn’t initialize Foo::Bar::Its23::Interface::Callback in the wibble instantiator of the reverse-proxy-dialback-charging refribrilatrilizor to 17, 16 just is wrong).

The working idea behind C[] will be to let you re-use definitions and then employ templates to create customized code for each case.

This is a very preliminary brain dump of where I’ve thought-to so far.

The basic syntax for C[] is simple.

	definition	:- value [ ':' [ value | array | hash ] ] ';'
	value		:- token [ '(' value [ ',' ... value ] ')' ]
	array		:- '[' [ value [ ',' ... value ] ] ']'
	hash		:- '{' [ value ':' value [ ',' ... value ':' value ] ] '}'
	token		:- <previously defined symbol> | '"' new term '"'

The parenthesized values of a value are attributes, and yes they can be nested.

I’m thinking I might be flexible and allow ‘:’, ‘=’, ‘->’ where ‘:’ is used.

You declare something by introducing it as the left side of a value:

	"i"(int);

declares an integer called i.

Everything is scoped, so for example the keywords used to describe options won’t collide with other keywords.

    // limit input files to being an instance of the
    // first object we define. limit definition to one item.
    options(singledef);

    // foreach input file, do this.
    foreach {
    	// UpperCamelCase name converts 'abCdEF', 'ab cd ef' and 'ab_cd_ef'
    	// to 'AbCdEf'
    	"ClassName"(string, UpperCamelCase(${sourceFileName});
    	"className"(string, lowerCamelCase(${sourceFileName});
    	"headerFile"(string, "${sourceFileName}.enum.h");
     }

    // specify which templates to apply.
    // create a header-per-input file.
    // note 'header' file is defined above so it's not in-scope,
    // so we need to tell C[] to save the name until we use it.
    template("enum-enum.tplt", "${headerFile}", options("pragmaguard"));
    // create a single cpp file with all of the enum names.
    template("enum-name.tplt", "enumnames.cpp");

    // declare an element called "enums" which is an array of strings.
    // disallow the word 'MAX'
    "enums" : [ "enum"(string, excluded("MAX")) ];

enum-enum.tplt:
Note: Header guards are automatically added, and one .h file is produced per source file.

	// lines beginning with a '>' are output, first \t after '>' is skipped.

	// if the source file was "pizzaToppings.in", the following will generate
	// "enum class PizzaToppings {"

	>	enum class ${"ClassName"}$ {

	foreach("enums") {	// iterate over the elements of enums
		// we didn't specify a variable name, so the 'this'
		// pointer for each iteration is set to the instance
		// of 'enums' we are looking at.

		// e.g. for 'green peppers', 'greenPeppers' or 'green_peppers'
		// output '\tGreenPeppers'.
	>		${UpperCamelCase(${enum})},
	}

	// End the list with one called 'MAX', end the enum, add a blank line.
	>		MAX
	>	};
	>

	// Include an extern def for the names list.
	extern const char* g_${"className"}_Names}[${"ClassName"}::MAX];

enum-names.tplt:
Note: Each enum file we parse will add to a single output .cpp file.

	>	#include "${headerFile}"
	>	const char* const g_${"className"}_Names[${"ClassName"}::MAX] = {
	foreach("enums") {
	>		"${UpperCamelCase(${enum})}",
	}
	>		"MAX"
	>	};
	>

As I said; very preliminary. One of the goals is to make it so that the input files can share structure with the definition language – to minimize the learning curve and maximize the reusability of definitions.

 

The actual input file for the above might look like this:

pizzaToppings.enums:

    cheese,
    pepperoni,
    "green peppers",
    beef,

Which should generate pizzaToppings.h:

#pragma once 

enum class PizzaToppings {
    Cheese,
    Pepperoni,
    GreenPeppers,
    Beef,
    MAX
};

and


#include "pizzaToppings.enum.h"
const char* const g_pizzaTopping_Names[PizzaToppings::Max] = {
    "Cheese",
    "Pepperoni",
    "GreenPeppers",
    "Beef",
    "MAX"
};

A more complex input file would look a lot more like json:

message("LoginRequest") : {
    type : client-server,
    members : [
        "accountID"(int),
        "passwordHash"(byte(256)),
        "gameVersion"(int),
    ];
};

// expose the enum so it can be seen elsewhere
enum("LoginResult") : [
    "Success",
    "ServiceUnavailable",
    "InvalidAccount",
    "IncorrectPassword",
    "IncorrectGameVersion",
];

message("LoginReply") : {
    type : server-client,
    members : [
        "result"(LoginResult),
        when("result == LoginResult::IncorrectGameVersion") : [
            "versionRequired"(int) 
        ]
    ]
};
Categories: Coding Tags: ,
  1. pitt
    May 23, 2014 at 4:30 pm

    This is our most desperate hour. Help me, Obi-Wan Kenobi. You’re my only hope.
    CRS needs you

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 216 other followers

%d bloggers like this: