About

ACE is a model-based configuration authoring and validation environment. It aims at bringing determinism to tool configuration by externalizing the configuration definition into a separate model file. The model file is used to check the validity of a configuration instance, browse available options, and generate the necessary source code to access the options values.

Installation

Prepare

Grab the source code. ACE has several dependencies, listed below.

Building

ACE uses CMake as its build system. To build the project, simply run the following commands:

cd ace
mkdir build
cd build
cmake ..
make
make install

Tweaking the build process

The cmake command accepts the following variables (default is shown bold):

The standard cmake variable CMAKE_INSTALL_PREFIX shall be used to alter the installation prefix:

cd ace
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=/opt ..
make
make install

Getting started

Model definition

The first step is to define a model for our example configuration, saved into a Model.json file:

{
  "header": {
    "author": { "name": "John Doe", "email": "jdoe@acme.com" },
    "version": "1.0",
    "namespace": [ "my", "ns" ],
    "doc": "ACME configuration model"
  },
  "body": {
    "intItem": {
      "kind": "integer", "arity": "?",
      "doc": "an integer option"
    },
    "stringItem": {
      "kind": "string", "arity": "1",
      "doc": "a string option"
    },
    "floatItem": {
      "kind": "float", "arity": "*",
      "doc": "a float option"
    },
    "booleanItem": {
      "kind": "boolean", "arity": "+",
      "doc": "a boolean option"
    }
  }
}

This model defines 4 configuration items: a integer item intItem, a string item stringItem, a float item floatItem, and a boolean item booleanItem. Beside its kind, each item declares its arity and documnetation.

Model transpilation

The second step is to transpile that model into a set of helper classes that can be used in a C++ application.

ace-compile Model.json

The ace-compile command produces 3 files: an interface header file IModel.ac.h, an implementation header file Model.ac.h, and an implementation source file Model.ac.cpp.

Code integration

The third step is to make use of the generated files in our example application. ACE provides a set of helper functions that simplify that task:

#include <Model.ac.h>
#include <ace/engine/Master.h>
#include <ace/model/Helper.h>
#include <tclap/CmdLine.h>
#include <string>

using namespace ace::model::Helper;

int main(int argc, char *argv[]) {
  // Define CLI arguments

  TCLAP::CmdLine cmd("ACME Tool", ' ', "0.1");
  TCLAP::ValueArg<std::string> cfgPath("c", "config", "Configuration file",
                                       true, "", "string");
  cmd.add(cfgPath);
  cmd.parse(argc, argv);

  // Instantiate the configuration
  //
  auto cfg = parseFile<my::ns::Model>(cfgPath.getValue(), false, argc, argv);
  if (cfg == nullptr) return -1;

  // Use it !
  //
  auto const & acme = *cfg;
  if (acme.has_intItem()) {
    std::cout << "int = " << acme.intItem() << std::endl;
  }
  for (auto const & f : acme.floatItem()) {
    std::cout << "float = " << f << std::endl;
  }
  for (auto const & b : acme.booleanItem()) {
    std::cout << "bool = " << b << std::endl;
  }
  std::cout << "str = " << acme.stringItem() << std::endl;

  return 0;
}

Please note that the code above makes use of TCLAP to parse the command line arguments. This is an arbitrary choice and you are free to use whichever CLI option parser you wish.

Testing

Finally, we are ready to test our application. First, let's compile it using the following Makefile:

CFLAGS = -g3 -x c++ -std=c++11 -I . -I $(ACE_INCLUDE_PATH) 
LFLAGS = -L $(ACE_LIB_PATH) -rpath $(ACE_LIB_PATH) 

all: clean ace bin

ace:
    ace-compile -I . Model.json

bin:
    c++ $(CFLAGS) -c main.cpp
    c++ $(CFLAGS) -c Model.ac.cpp
    c++ $(LFLAGS) -o main main.o Model.ac.o -lace

clean:
    rm -f *.ac.* *.o main

Next, let's define a configuration file, in TOML, called config.toml:

intItem = 17
stringItem = "Hello!"
floatItem = 3.14
booleanItem = [ true, false, false ]

Finally, let's run our application:

./main -c config.toml

The expected output it:

int = 17
float = 3.14
bool = 1
bool = 0
bool = 0
str = Hello!

Since ACE also supports JSON, Python and Lua, we can also use these configuration files:

{
  "intItem" : 17,
  "stringItem" : "Hello!",
  "floatItem" : 3.14,
  "booleanItem" : [ true, false, false ],
  "selectItem" : "hello",
  "hello" : "world"
}

a Python configuration file:

config = {
  'intItem' : 17,
  'stringItem' : 'Hello!',
  'floatItem' : 3.14,
  'booleanItem' : [ True, False, False ],
  'selectItem': 'hello',
  'hello': 'world'
}

or a Lua configuration file:

config = {
  intItem = 17,
  stringItem = 'Hello!',
  floatItem = 3.14,
  booleanItem = { true, false, false }
  selectItem = 'hello',
  hello = 'world'
}