Wrapping enumerations¶
The problem¶
Ecto cells that take parameters that are enumerations should wrap those enumerations using boost::python, this vastly improves use/readability.
For instance, this cell takes its parameter strategy
as an integer:
#include <ecto/ecto.hpp>
#include <iostream>
using ecto::tendrils;
struct EnumAsInt
{
enum Strategy {
FAST, SLOW, ADAPTIVE
};
static void declare_params(tendrils& p)
{
p.declare<int>("strategy", "How to run our algorithm");
}
void configure(const tendrils& p, const tendrils& i, const tendrils& o)
{
int n = p.get<int>("strategy");
switch (n) {
case FAST:
std::cout << "running FAST\n"; break;
case SLOW:
std::cout << "running SLOW\n"; break;
case ADAPTIVE:
std::cout << "running ADAPTIVE\n"; break;
default:
std::cout << "BAD! Unhandled value of enum parameter";
}
}
};
ECTO_CELL(ecto_examples, EnumAsInt, "EnumAsInt",
"Example of how *not* to handle parameters that are enums");
Which is suboptimal in several ways. First, the documentation doesn’t say what the legal values are for this parameter:
EnumAsInt¶
Brief doc
Example of how *not* to handle parameters that are enums
Parameters
strategy
int
How to run our algorithm
And though the cell functions correctly if used correctly, (note here
that the direct call to process()
is only for convenience, you’ll
typically be adding the cell to a plasm and letting a scheduler do
this),
#!/usr/bin/python
import ecto.ecto_examples as ecto_examples
cell = ecto_examples.EnumAsInt(strategy=1)
cell.process()
output:
No module named PySide.QtCore
running SLOW
it is easy to pass invalid values to the cell from python, and easy to forget to handle out-of-range values:
#!/usr/bin/python
import ecto.ecto_examples as ecto_examples
cell = ecto_examples.EnumAsInt(strategy=17)
cell.process()
output:
No module named PySide.QtCore
BAD! Unhandled value of enum parameter
Solution: enum parameters as enums, and wrap your enumerations¶
Make sure that the parameters are of the enum type itself. In the cell, declare and get the parameter as being of the enum type,
#include <iostream>
#include <ecto/ecto.hpp>
using ecto::tendrils;
enum Strategy {
FAST, SLOW, ADAPTIVE
};
struct EnumAsEnum
{
static void declare_params(tendrils& p)
{
p.declare<Strategy>("strategy", "How to run our algorithm");
}
void configure(const tendrils& p, const tendrils& i, const tendrils& o)
{
Strategy s = p.get<Strategy>("strategy");
switch (s) {
case FAST:
std::cout << "running FAST\n"; break;
case SLOW:
std::cout << "running SLOW\n"; break;
case ADAPTIVE:
std::cout << "running ADAPTIVE\n"; break;
default:
std::cout << "BAD! Unhandled value of enum parameter";
}
}
};
ECTO_CELL(ecto_examples, EnumAsEnum, "EnumAsEnum",
"This is how to defined enum parameters. Like this.");
And wrap the enum in the ECTO_DEFINE_MODULE section like so:
#include <ecto/ecto.hpp>
#include <ecto/python.hpp>
#include <boost/python.hpp>
enum Strategy {
FAST, SLOW, ADAPTIVE
};
ECTO_DEFINE_MODULE(ecto_examples)
{
boost::python::enum_<Strategy>("Strategy")
.value("FAST", FAST)
.value("SLOW", SLOW)
.value("ADAPTIVE", ADAPTIVE)
.export_values()
;
}
Of course, this enumeration should be in a header file so that you don’t have multiple copies floating around. Now a great many things will be more usable. First of all, the documentation for the module will show the correct type (and therefore values) for the parameter:
EnumAsEnum¶
Brief doc
This is how to defined enum parameters. Like this.
Parameters
strategy
Strategy
Legal Values:
FAST (0)
SLOW (1)
ADAPTIVE (2)
How to run our algorithm
And if you try to pass magic numbers to cells, you get a reasonable error:
#!/usr/bin/python
import ecto_examples
cell = ecto_examples.EnumAsEnum(strategy=17)
cell.process()
output:
$ /home/vrabaud/workspace/recognition_kitchen/src/ecto/doc/kitchen/ecto/usage/advanced/../../src/enumasenum_bad.py
Traceback (most recent call last):
File "/home/vrabaud/workspace/recognition_kitchen/src/ecto/doc/kitchen/ecto/usage/advanced/../../src/enumasenum_bad.py", line 3, in <module>
import ecto_examples
ImportError: No module named ecto_examples
The enum type gets a reasonable docstring,
No module named PySide.QtCore
Help on class Strategy in module ecto.ecto_examples:
class Strategy(Boost.Python.enum)
| Method resolution order:
| Strategy
| Boost.Python.enum
| __builtin__.int
| __builtin__.object
|
| Data and other attributes defined here:
|
| ADAPTIVE = ecto.ecto_examples.Strategy.ADAPTIVE
|
| FAST = ecto.ecto_examples.Strategy.FAST
|
| SLOW = ecto.ecto_examples.Strategy.SLOW
|
| names = {'ADAPTIVE': ecto.ecto_examples.Strategy.ADAPTIVE, 'FAST': ect...
|
| values = {0: ecto.ecto_examples.Strategy.FAST, 1: ecto.ecto_examples.S...
|
| ----------------------------------------------------------------------
|
And the normal way to use this kind of thing would be:
#!/usr/bin/python
import ecto.ecto_examples as ecto_examples
cell = ecto_examples.EnumAsEnum(strategy=ecto_examples.ADAPTIVE)
cell.process()
No module named PySide.QtCore
running ADAPTIVE