Skip to content

Latest commit

 

History

History

pythongen

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

kdb-gen(1) -- code generation

This tutorial serves as a guide for how you can use the code generator and contextual values. For more background read this paper.

Motivation

Writing your own conversion of the strings parsed from commandline arguments and configuration files to native variables is error-prone and time consuming to code, e.g.:

k = ksLookupByName(ks, "user:/app/current/number");
int i = 20;
if (k) i = atoi(keyString(k));

We see already multiple problems with this code:

  • Name of key encoded in program (could be wrong/undocumented)
  • Default value (20) is encoded in application (could be undocumented)
  • No error handling for null/binary keys
  • No error handling when string is not an integer

Our approach avoids all such errors and saves time for more important implementation tasks.

Specification

To avoid the problems we initially stated, we use a specification. Using the specification, we can generate code similar to the code above, but without any of the errors. To generate the code we use:

kdb gen specification.ini template_context.hpp -o lift_context.hpp

Using the generated code is very easy, we just create a parameter object:

kdb::KeySet ks;
kdb::Context c;
kdb::Parameters par(ks,c);

and access the keys as if they were variables:

std::cout << "delay: " << par.test.lift.emergency.delay << std::endl;

For a full example, see here, or here for a thread-safe version.

Contextual Values

The value of a key often depends on a context. E.g. if an application is started with another profile:

firefox -P anonymous

we want different configuration values, e.g. the value of the key /username should certainly not give hints of the user. In Elektra such problems are solved by introduction of an additional level in the hierarchy. So instead of:

user:/firefox/username = Max Mustermann

We can have multiple values for the same key:

user:/firefox/default/username = Max Mustermann
user:/firefox/anonymous/username = Anonymous User

To define such a contextual value, we change the specification to contain placeholders, e.g.:

[/firefox/%profile%/username]
type=string
default=none

Note that the current implementation always requires a default value to be present which will be used when no configuration could be loaded.

The placeholder %profile% will be replaced by the current profile. Now we need a so-called layer to actually switch the profile:

class ProfileLayer : public kdb::Layer
{
public:
	ProfileLayer(std::string const & profile) :
		m_profile(profile) {};
	std::string id() const { return "profile"; }
	std::string operator()() const { return m_profile; }
private:
	std::string m_profile;
};

The id() has to match the placeholder we saw before. Whatever operator() yields will be used instead of this placeholder. Now we have everything ready to actually switch profiles:

par.activate<ProfileLayer>("anonymous");

The library function activate makes sure that all contextual values that contain the placeholder %profile% will use "anonymous" instead of the placeholder afterwards. If no placeholder exists % will be used.

Command-line Options

Now if we want to implement the -P commandline option, we can do so very easily using Elektra’s code generator. We simply add another item in the specification:

[/firefox/profile]
type=string
opt=P
opt/long=profile
default=none

The specification entries "opt" and "opt/long" will generate, next to the contextual value firefox.profile additional code parsing can be done via the commandline:

kdb gen specification.ini template_genopt.c -o genopt.c

To parse all arguments as defined in the specification, we simply use the generated function:

kdb::ksGetOpt(argc, argv, ks);

Then we can implement firefox-like profiles as shown here by activating what we got from commandline:

par.activate<ProfileLayer>(par.profile);

Limitations

This is an experimental prototype. You should have a look at the test cases to see what is actually working.

The code generator has little (none) checks for valid specifications. For problems in the specification you usually get hard-to-interpret compile errors.

Not all of Elektra's types are supported, for example, char and octet do not work.

Please open an issue if you plan to use the code.

See Also

If you want to know more read: