Example checkout using command line client:
svn co https://eval.svn.sourceforge.net/svnroot/eval/trunk trunk
// Point.hpp // An extremely simple point class. class Point { public: Point() : _x(0), _y(0) { } Point(int x, int y) : _x(x), _y(y) { } int get_x() const { return _x; } int get_y() const { return _y; } void set_x(int x) { _x = x; } void set_y(int y) { _y = y; } void add_to_this(const Point& other) { _x += other._x; _y += other._y; } // A somewhat artificial overload; note the corresponding // eval header syntax. void add_to_this(int x, int y) { _x += x; _y += y; } private: int _x; int _y; friend Point add_points(Point& a, Point& b); }; inline Point add_points(Point& a, Point& b) { return Point(a._x + b._x, a._y + b._y); } // A somewhat artificial inheritance structure; note the corresponding // eval header syntax. class ThreeDimPoint : public Point { public: ThreeDimPoint(int x, int y, int z) : Point(x, y), _z(z) { } ThreeDimPoint() : Point(), _z(0) { } int get_z() const { return _z; } void set_z(int z) { _z = z; } private: int _z; };
Next, we show the corresponding eval header (which also includes some other standard eval headers for additional functionality):
// eval_example_header.ehpp // Defines the basic types (e.g. int, double, ...) #include "eval_headers/basic_types.ehpp" // These headers are nowhere near complete. #include "eval_headers/string.ehpp" #include "eval_headers/ostream.ehpp" EVAL_REGISTER_TYPE(Point) EVAL_REGISTER_CONSTRUCTOR(()) // default constructor EVAL_REGISTER_CONSTRUCTOR((int, int)) EVAL_REGISTER_CONSTRUCTOR((const Point&)) // copy constructor EVAL_REGISTER_METHOD(get_x) EVAL_REGISTER_METHOD(get_y) EVAL_REGISTER_METHOD(set_x) EVAL_REGISTER_METHOD(set_y) EVAL_REGISTER_METHOD_OVERLOADED(void, (const Point&), add_to_this) EVAL_REGISTER_METHOD_OVERLOADED(void, (int, int), add_to_this) EVAL_END_TYPE EVAL_REGISTER_FUNCTION(add_points) EVAL_REGISTER_TYPE(ThreeDimPoint) // Note: EVAL_INHERIT_FROM semantically really means "implements". // Inheritance hierarchy is irrelevant; however, this class must // implement/inherit all the methods (excluding constructors) in Point, or // compilation will fail (in C++ it is possible to create a subclass which // does not implemented all the methods of its superclass exactly). // If this class overwrites methods from the base class, you can list // them explicitly, but that's not necessary. EVAL_INHERIT_FROM(Point) EVAL_REGISTER_CONSTRUCTOR(()) EVAL_REGISTER_CONSTRUCTOR((int, int, int)) EVAL_REGISTER_CONSTRUCTOR((const ThreeDimPoint&)) EVAL_REGISTER_METHOD(get_z) EVAL_REGISTER_METHOD(set_z) EVAL_END_TYPE // Note that it is possible to register functions/methods/types // with a different name than in their C++ source (so that the // scripts would be able to refer to an alias name).
We can now use these files in our example program:
// eval_example.cpp #include <string> #include <iostream> #include <assert.h> // The eval header defines 2 functions, eval_file and eval (takes const char *) #include "eval.hpp" // The val class is a generic class for wrapping arbitary instances/primitives. #include "val.hpp" // A plain old c++ class, serving as an example. #include "Point.hpp" // The EVAL_INCLUDE_FILE1 is the eval header that determines which // functions/methods/constructors the scripts have access to. #define EVAL_INCLUDE_FILE1 "eval_example_header.ehpp" // Some preprocessor magic happens at this point. Suffices to say that // you now have a class deriving from eval::execution_context, called // MyExecContext. This custom excecution context knows about // functions/methods/constructors as per EVAL_INCLUDE_FILE1. // (You can create more than 1 execution context.) #define EVAL_EXECUTION_CONTEXT_NAME MyExecContext #include "create_execution_context_definition.hpp" int main() { MyExecContext e; // Create our custom execution context. e.register_all(); // Register all functions/methods/constructors. In some // circumstances, the execution context can be used // without this call. // Make std::cout available to the script (in future library versions, // this may be done in the appropriate eval standard header). e["std::cout"] = eval::val(&std::cout, eval::UNMANAGED_PTR); // make some Points available to the script e["point1"] = eval::val(new Point(4, 5), eval::TRANSFER_OWNERSHIP); Point point2(6, 7); e["point2"] = eval::val(&point2, eval::UNMANAGED_PTR); // execute our example script (the script is in the same directory; // specifying a more complete path just helps the automated testing system) eval_file(e, "../examples/eval_example_script.epp"); // We have access to variables defined in the script. Point *point_rv = e.extended_val_cast_ptr<Point>(e["point_rv"]); assert(point_rv->get_x() == 12); assert(point_rv->get_y() == 14); // The script also modified existing objects (arguments) assert(point2.get_x() == 11); assert(point2.get_y() == 16); // Note that various casting methods exist, allowing you to attempt // to convert return values to a particular type (or just get a string // or int representation, if possible). return 0; }
Finally, here's the script referred to by the main program; this can be written after compiling the above:
// eval_example_script.epp // std::cout, point1 and point2 come in as arguments // call operator<< function std::cout << "Hello world from an eval script" << "\n"; val point3; // Declare new variable (accessible from calling program). // All variables have type val; declarations go on their own line. point3 = Point(point1.get_x(), point2.get_y()); // modify an argument passed in point2.add_to_this(point3); point2.add_to_this(1, 2); val point_rv; point_rv = add_points(point1, ThreeDimPoint(8, 9, 6)); // adding in 2D only! // Note: val temp; temp = 3; // const int temp = int(3); // int temp = Point(point3); // make a copy
g++ -Wall -I. -I.. eval_example.cpp ./a.out
On msvc 8, a very small subset of the tests may not compile, see Known Bugs and Limitations.
./bjam_linux_x68 memcheck=1
doxygen doxygen.config
// val_example.cpp #include "assert.h" #include <iostream> #include "val.hpp" int main() { eval::val v1(new std::string("hello world"), eval::TRANSFER_OWNERSHIP); eval::val v2(v1); // make a shallow copy - reference counted int a = 8; eval::val v3(a, eval::COPY_VALUE); eval::val v4 = v3; // make a real copy int *the_value = eval::val_cast_ptr<int>(v3); assert(*the_value == 8); assert(v3.coerce_string() == std::string("8")); // works with ostreamable types assert(v3.coerce_int() == 8); // works with numeric types (e.g. int, double, ...) try { eval::val v5(8, eval::COPY_VALUE); int *x; x = eval::val_cast_ptr<int>(v5); // const int, so can only cast to const int assert(0); } catch(eval::bad_val_cast& e) { // val_cast_ptr can only succeed if the types match _exactly_. // eval::execution_context provides conversion casts. } // For globals or local variables declared on the stack: eval::val v6(&std::cout, eval::UNMANAGED_PTR); // Other variants exist, taking arrays or shared pointer arguments. return 0; }
For implementation details, see also eval::detail::storage_base_common and eval::detail::storage_base_nonconst.