Skip to content

Latest commit

 

History

History
321 lines (207 loc) · 8.03 KB

README.org

File metadata and controls

321 lines (207 loc) · 8.03 KB

libtweak

Abstract

libtweak lets you define parameters that can be adjusted while your C++ program is running. It is particularly suited to realtime applications like games, where you want instant feedback when making changes.

  • Supported languages: C++11
  • Supported platforms
    • Full Support: Linux, Mac
    • Production mode only: All other platfoms
  • Extra Requirements: Python 3

Compiling

Run make. Find libtweak.a in build/ and link your program with it. Add libtweak’s include directory to your header file search path.

The examples below will also assume that libtweak’s bin directory is on your PATH.

Usage

Declare tweakable parameters in your program:

#include <tweak.h>

TWEAK(float, sausage_length)
TWEAK(float, temperature)

Use them like ordinary variables:

int main()
{
  tweak::init();
  // ...
}

void do_frame()
{
  tweak::process();
  printf("The sausages are %fcm long and %f°C!", sausage_length, temperature);
}

While your program is running, adjust parameters using the GUI:

tweak

Production Mode

When distributing your program, you can build a version where libtweak is not linked in, and your parameters compile to ordinary constants.

To do this:

  1. Generate a header file with the constants baked in:
    tweakc > myconstants.h
    
        
  2. Include this file before <tweak.h>.
  3. Define TWEAK_BAKE before including <tweak.h>. This tells tweak.h to use the constants from your generated header file.
  4. No need to link to libtweak.a

For example:

#ifdef MY_PRODUCTION_FLAG
#  include "myconstants.h"
#  define TWEAK_BAKE
#endif

#include <tweak.h>

Test Program

As well as compiling libtweak itself, make will also compile a test program called test. This program will define some parameters and continuously output them to the terminal.

To try out libtweak, run this program and, while it’s running, use tweak to adjust its parameters and see them update live.

Internals

When your program calls tweak::init, a file named Tweakfile is written to the working directory, detailing the available parameters and their values. A Unix FIFO called Tweakfifo is also created.

The next time your program starts, values are read from the existing Tweakfile.

Additionally, when you call tweak::process, commands are read from the Tweakfifo and used to update parameter values.

Tools that update values do two things:

  1. Update the Tweakfile so the program will see the value when it is next started.
  2. Write a value change command to the Tweakfifo so the program can instantly update that value on its next call to tweak::process.

The tweakc tool simply reads parameter values from the Tweakfile and writes them out in the form of a C++ header.

API Reference

tweak.h

#define TWEAK(type, name)

Define a tweakable parameter. If TWEAK_BAKE is defined this simply defines a constant. Otherwise, it defines a const reference to a value, which may change when you call tweak::process.

#define EXTERN_TWEAK(type, name)

Use this to refer to a tweakable parameter defined in another translation unit.

void tweak::init()

Sets all parameters to their initial values. Reads the Tweakfile or creates one if not present. Sets up the mechanism for live updating (Tweakfifo).

You’ll usually want to call this early in your program’s startup, or at least before you call tweak::process for the first time.

If TWEAK_BAKE is defined, tweak::init is a no-op and your compiler will optimize out any calls to it.

void tweak::process()

Check for new live-update messages and update any parameters. You’ll want to call this frequently during execution of your program.

In games, you can afford to call this every frame; if there’s nothing to update it is very cheap.

If TWEAK_BAKE is defined, tweak::process is a no-op and your compiler will optimize out any calls to it.

Tools Reference

All of these tools operate on the Tweakfile and Tweakfifo in the working directory.

tweak

Starts a GUI for tweaking the program’s parameters.

tweakc

Writes the current values of all parameters, formatted as a C++ header file, to standard output. Use this file in conjunction with the TWEAK_BAKE option to optimize out all of libtweak for production builds.

File Format

Both Tweakfile and Tweakfifo have the same format: a list of lines where each line is a command name followed by a space-separated list of arguments. Possible commands are:

type NAME TYPE

Specifies the type of a parameter. Every parameter must have a type. By default, the available types are int, float, string and bool. You can also add your own types; see Extending.

set NAME VALUE

Specifies a value for a paramater. See Value Format.

range NAME MIN MAX

Specifies the range for a parameter. Only applies to int and float parameters. This is only used by the GUI tool to display an appropriate GUI; libtweak itself ignores range commands.

Value Format

Values are converted to and from strings for storage in Tweakfile and transmission through Tweakfifo. How a value is converted to and from a string depends on its type:

int, float

These are read by the C++ formatted extraction operators and so are dependent on your locale.

bool

0 or 1.

string

Strings begin with a literal $ character, and end at the end of the line. They may contain any characters except newlines.

(This is a strange format, but it’s very easy to parse and leaves the door open to implement proper delimited strings later.)

Extending

You can add support for your own custom types to libtweak. To do so, you need to:

  1. Specialize the type_name struct so libtweak knows what your type is called:
    namespace tweak
    {
        template<> struct type_name<MyType>
        {
            static std::string get() { return "mytype"; }
        }
    }
    
        

    This is the name that will be used in type commands in the Tweakfile.

  2. If necessary, specialize the io struct so libtweak knows how to load and save your type. If you don’t do this, the standard iostream ‘<<’ and ‘>>’ operators will be used. You can overload these instead of specializing io.
    namespace tweak
    {
      template<> struct io<MyType>
      {
        static void load(MyType &value, std::istream &is) { /* ... */ }
        static void save(const MyType &value, std::ostream &os) { /* ... */ }
      }
    }
    
        

That’s enough for libtweak to handle your type. You can now use it in TWEAK declarations.

However, the tools tweak and tweakc will not yet be able to intelligently handle your type.

tweak, tweakc and custom types

By default, tweak will show a text entry field for any type it doesn’t know about, allowing you to modify the raw textual representation of the parameter’s value (as used by load and save).

tweakc will take a shot at emitting C++ code for your type, but don’t hold your breath.

You can override these default behaviours by putting some code in a tweak_ext.py file alongside your Tweakfile:

import tweak

class MyWidget(tweak.Widget):
    "..."

class MyType(tweak.Param):
    widget = MyWidget

    def cname(self):
        return "c++ type name here"

    def cvalue(self):
        return "c++ value of self.value here"

tweak.types['mytypename'] = MyType

For guidance on implementing your own type and widget classes, see the existing classes in bin/tweak.

Further Work

Windows Support

This library uses FIFOs which don’t exist on Windows. The code is organized for easy porting, and full Windows support shouldn’t be too much work.

In production mode (with TWEAK_BAKE defined), Windows is supported, as libtweak becomes a trivial header-only library in that case. So, it’s still possible to develop your program on Unix and support Windows as a release target.