Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/rogual/libtweak

Runtime-editable variables for C++
https://github.com/rogual/libtweak

Last synced: 3 months ago
JSON representation

Runtime-editable variables for C++

Awesome Lists containing this project

README

        

#+TITLE: 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:

#+BEGIN_SRC c++

#include

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

#+END_SRC

Use them like ordinary variables:

#+BEGIN_SRC c++

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

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

#+END_SRC

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

#+BEGIN_SRC sh
tweak
#+END_SRC

* 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:

#+BEGIN_SRC sh

tweakc > myconstants.h

#+END_SRC

2. Include this file before ==.

3. Define =TWEAK_BAKE= before including ==. This tells
=tweak.h= to use the constants from your generated header file.

4. No need to link to =libtweak.a=

For example:

#+BEGIN_SRC c++

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

#include

#+END_SRC

* 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][Extending]].

** =set NAME VALUE=

Specifies a value for a paramater. See [[Value Format][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:

#+BEGIN_SRC C++

namespace tweak
{
template<> struct type_name
{
static std::string get() { return "mytype"; }
}
}

#+END_SRC

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=.

#+BEGIN_SRC C++

namespace tweak
{
template<> struct io
{
static void load(MyType &value, std::istream &is) { /* ... */ }
static void save(const MyType &value, std::ostream &os) { /* ... */ }
}
}

#+END_SRC

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=:

#+BEGIN_SRC python

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

#+END_SRC

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.