CodingTricks - xlgames-inc/XLE GitHub Wiki

#Coding tricks

Here are some cool coding tricks using the XLE libraries.

XLE is designed to support experimentation. It's helpful to have a few simple syntax tricks to make every-day coding tasks easier.

StringMeld

There is a function called XlDynFormatString(). This can be used to initialise an std::string with a some dynamic text using printf style formatting. Eg:

auto windowTitle = XlDynFormatString("XLE sample [RenderCore: %s : %s]", version, versionDate);

It's pretty useful. However, it has many disadvantages:

  • there is a hidden max string length within the function
  • printf formatting isn't type safe. Above, we're assuming that version and versionDate are strings. But what happens if someone changes their type (eg, to a std::string?)
  • all parameters get passed as 32 bit values in Win32. 64bit values won't work correctly.
  • there will always be at least one allocation from the heap, and that allocation usually feels redundant.

StringMeld provides a solution!

const char* windowTitle = StringMeld<128>() << "XLE sample [RenderCore: " << v.first << ", " << v.second << "]";
  • There is an explicit max string length
  • it's type safe
  • no allocation

StringMeld() can typically fit into many places and is easy and convenient to use.

Tweakable

Very frequently we want to expose a variable from the code that can be dynamically changed by developers (but only developers).

For example, imagine we're writing some code for a camera. We want to put in a maximum pitch of somewhere between 85 and 90 degrees. We're not sure yet what the best value is, so we want to try a few different values. But once it's finished, that value should just become hard coded value, we don't want it do be exposed to end users.

We can use Tweakable, like this:

const auto maxPitch = Tweakable("MaxPitch", 88.f) * gPI / 180.f;  // (degrees to radians conversion)

Tweakable creates a console variable called "cv.MaxPitch". It can be modified at runtime by typing "cv.MaxPitch = 85" into the console.

Tweakable supports any types supported by LuaBridge. There's no further code required, just wrap the value you want to expose. For release builds, we can burn that code down to just a hard-coded value.

Having a convenient way to do this just makes every day a little bit easier, makes every difficult task feel a step closer.

Multi-purpose asset system

The asset system can be used for constructing and managing anything that can be created by one or more strings.

Generally, when constructing an asset, we use something like the following:

auto& asset = GetAsset<SomeClass>(initialiserString);

(This may change at some point to return custom smart pointer. In Visual Studio 2012, with it's more complete C++11 support, it should also be possible to use any type of constructor parameters, not just strings)

Normally the initialiserString will be a filename, but it doesn't have to be. It can contain any information required. A simple example is Shaders, which are constructed as assets, but encode extra information in their string names, such as the entry point and shader model.

For example, game/xleres/basic.psh:copy:ps_* contains a filename, entry point name ("copy") and shader model ("ps_*", which means the default shader model for pixel shaders). Shaders are actually constructed with 2 strings, but those 2 strings are all that are needed to uniquely identify that particular shader.

The asset system intentially imposes few requirements on asset classes. It's designed to work with as wide a range of different objects as possible.

By using the asset system, you also get access to the management functionality within that system. For example, the system will log the all assets of each type and provides some handy debugging functionality. Furthermore, it makes it easy to monitor and reload assets if they change on disk.

See RenderOverlays/DebugHotKeys.cpp for a good simple example of using this library to get really useful functionality with minimal effort.

Handy Functions

There are a few handy functions that I use very frequently in XLE:

Function Purpose Header
PtrAdd add or subtract a value from a pointer, returning a new pointer to the same type. Constantly useful! Utility\PtrUtils.h
AsPointer Converts a standard library iterator to a pointer. Doesn't create an assert error when used with vector.end() Utility\PtrUtils.h
dimof() this a macro, but it returns the number of elements in an array Core\Prefix.h
LowerBound Performs a lower_bound binary search on vector of pairs sorted by "first" Utility\IteratorUtils.h

Constant expression

This class defeats the "constant expression" warning in Visual Studio, and explicitly declares that a given expression must be available at compile time:

const bool useHighPrecisionDepths = false;
if (constant_expression<useHighPrecisionDepths>::result()) {
...
} else {
...
}

Without this, we get an annoying warning. Also, if someone changes "useHighPrecisionDepths" to equal some dynamic expression, we will get a compile error.

⚠️ **GitHub.com Fallback** ⚠️