Utility library for providing native functionality to the Squirrel environment.

Dependents:   Squirrel

Committer:
jhnwkmn
Date:
Tue Dec 16 11:38:54 2014 +0000
Revision:
4:13939f98fe5f
Parent:
0:a9a5c12f2d30
Another pointer initialized.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jhnwkmn 0:a9a5c12f2d30 1
jhnwkmn 0:a9a5c12f2d30 2 SQBind (c) 2009 Juan Linietsky
jhnwkmn 0:a9a5c12f2d30 3
jhnwkmn 0:a9a5c12f2d30 4 --Introduction:--
jhnwkmn 0:a9a5c12f2d30 5
jhnwkmn 0:a9a5c12f2d30 6 SQBind is a pretty simple, yet powerful binding library for the Squirrel
jhnwkmn 0:a9a5c12f2d30 7 language. It supports for binding classes, methods, static methods, member
jhnwkmn 0:a9a5c12f2d30 8 variables, functions, enums, C types. It can either manage pointers, or let
jhnwkmn 0:a9a5c12f2d30 9 them unmanaged. It also recognizes functions that receive pointers or
jhnwkmn 0:a9a5c12f2d30 10 references, and it passes values accordingly. On top of all that, SQBind
jhnwkmn 0:a9a5c12f2d30 11 easily lets you use your own types for taking care of native Squirrel types,
jhnwkmn 0:a9a5c12f2d30 12 such as your own (or std::) string class, table class, array class, etc.
jhnwkmn 0:a9a5c12f2d30 13 SQBind is _really_ simple. It consists of an only header file and a
jhnwkmn 0:a9a5c12f2d30 14 binders file, so you don't need to link against it. However, since SQBind
jhnwkmn 0:a9a5c12f2d30 15 uses C++ Partial Specialization very heavily, it will probably not work on
jhnwkmn 0:a9a5c12f2d30 16 older compilers. SQBind produces lightweight bindings, as it is designed
jhnwkmn 0:a9a5c12f2d30 17 to reuse template symbols as much as possible when binding methods/functions.
jhnwkmn 0:a9a5c12f2d30 18
jhnwkmn 0:a9a5c12f2d30 19 --Compilation:--
jhnwkmn 0:a9a5c12f2d30 20
jhnwkmn 0:a9a5c12f2d30 21 Just drop sbind.h and .inc in a directory and include it. Alternatively,
jhnwkmn 0:a9a5c12f2d30 22 you may wish to define a SQBIND_CUSTOMIZE header, to allow SQBind to adapt
jhnwkmn 0:a9a5c12f2d30 23 to your project better. You can configure the following macros:
jhnwkmn 0:a9a5c12f2d30 24
jhnwkmn 0:a9a5c12f2d30 25 SQBIND_NEW() - replaces new
jhnwkmn 0:a9a5c12f2d30 26 SQBIND_DELETE() - replaces delete
jhnwkmn 0:a9a5c12f2d30 27 SQBIND_INLINE - replaces inline
jhnwkmn 0:a9a5c12f2d30 28 SQBIND_DEBUG - catches/prints more errors, but becomes slower.
jhnwkmn 0:a9a5c12f2d30 29
jhnwkmn 0:a9a5c12f2d30 30 Customizing is done by defining SQBIND_CUSTOMIZE right before including
jhnwkmn 0:a9a5c12f2d30 31 sqbind.h, like this:
jhnwkmn 0:a9a5c12f2d30 32
jhnwkmn 0:a9a5c12f2d30 33 #define SQBIND_CUSTOMIZE "sqbind_custom.h"
jhnwkmn 0:a9a5c12f2d30 34 #include "sqbind.h"
jhnwkmn 0:a9a5c12f2d30 35
jhnwkmn 0:a9a5c12f2d30 36 --Usage:--
jhnwkmn 0:a9a5c12f2d30 37
jhnwkmn 0:a9a5c12f2d30 38 -Initializing-
jhnwkmn 0:a9a5c12f2d30 39
jhnwkmn 0:a9a5c12f2d30 40 Before a class is ready for usage, it must be initialized. This is done
jhnwkmn 0:a9a5c12f2d30 41 with a single line (note , 'vm' is the actual HSQUIRRELVM instance):
jhnwkmn 0:a9a5c12f2d30 42
jhnwkmn 0:a9a5c12f2d30 43 SqBind<MyClass>::init(vm,_SC("MyClass"));
jhnwkmn 0:a9a5c12f2d30 44
jhnwkmn 0:a9a5c12f2d30 45 //note that _SC is used because Squirrel may be in unicode mode.
jhnwkmn 0:a9a5c12f2d30 46
jhnwkmn 0:a9a5c12f2d30 47 Optionally, inheritance can be defined, as well as non-instantiation
jhnwkmn 0:a9a5c12f2d30 48 property. Full initialization lines are:
jhnwkmn 0:a9a5c12f2d30 49
jhnwkmn 0:a9a5c12f2d30 50 SqBind<T>::init(HSQUIRRELVM v,const SQChar * p_name,const SQChar
jhnwkmn 0:a9a5c12f2d30 51 *p_base_class_name, bool p_instantiable=true);
jhnwkmn 0:a9a5c12f2d30 52
jhnwkmn 0:a9a5c12f2d30 53 or
jhnwkmn 0:a9a5c12f2d30 54
jhnwkmn 0:a9a5c12f2d30 55 SqBind<T>::init(HSQUIRRELVM v,const SQChar * p_name,HSQOBJECT *p_base_class=NULL, bool p_instantiable=true)'
jhnwkmn 0:a9a5c12f2d30 56
jhnwkmn 0:a9a5c12f2d30 57 -Binding a Method-
jhnwkmn 0:a9a5c12f2d30 58
jhnwkmn 0:a9a5c12f2d30 59 Binding a method is done with the "sqbind_method" function. Just pass the
jhnwkmn 0:a9a5c12f2d30 60 method pointer (with up to seven parameters) and desired name:
jhnwkmn 0:a9a5c12f2d30 61
jhnwkmn 0:a9a5c12f2d30 62 sqbind_method( vm, "say_hello", &MyClass::say_hello );
jhnwkmn 0:a9a5c12f2d30 63
jhnwkmn 0:a9a5c12f2d30 64 SQBind will recognize the class and pointer from it and it will bind it
jhnwkmn 0:a9a5c12f2d30 65 automatically.
jhnwkmn 0:a9a5c12f2d30 66
jhnwkmn 0:a9a5c12f2d30 67 -Binding a Function/Static Method-
jhnwkmn 0:a9a5c12f2d30 68
jhnwkmn 0:a9a5c12f2d30 69 Binding a function or a static method is pretty much the same. The catch
jhnwkmn 0:a9a5c12f2d30 70 is that static methods need to be passed the class_id owner in the end
jhnwkmn 0:a9a5c12f2d30 71 (which can be obtained by calling to SqBind<MyClass>::get_id() )
jhnwkmn 0:a9a5c12f2d30 72
jhnwkmn 0:a9a5c12f2d30 73 :function:
jhnwkmn 0:a9a5c12f2d30 74
jhnwkmn 0:a9a5c12f2d30 75 sqbind_function( vm, "say_goodbye", my_function );
jhnwkmn 0:a9a5c12f2d30 76
jhnwkmn 0:a9a5c12f2d30 77 :static method:
jhnwkmn 0:a9a5c12f2d30 78
jhnwkmn 0:a9a5c12f2d30 79 sqbind_function( vm, "say_goodbye", &SqBind<MyClass>::get_id() );
jhnwkmn 0:a9a5c12f2d30 80
jhnwkmn 0:a9a5c12f2d30 81 -Binding a Squirrel-Native method-
jhnwkmn 0:a9a5c12f2d30 82
jhnwkmn 0:a9a5c12f2d30 83 Native Squirrel methods (SQFUNCTION) is done this way:
jhnwkmn 0:a9a5c12f2d30 84
jhnwkmn 0:a9a5c12f2d30 85 SqBind<MyClass>::bind_method( vm, _SC("difficult_to_bind"), difficult_method);
jhnwkmn 0:a9a5c12f2d30 86
jhnwkmn 0:a9a5c12f2d30 87 Optionally a "static" parameter can be passed to make it static.
jhnwkmn 0:a9a5c12f2d30 88
jhnwkmn 0:a9a5c12f2d30 89 -Binding a Member Variable-
jhnwkmn 0:a9a5c12f2d30 90
jhnwkmn 0:a9a5c12f2d30 91 Binding member variables gets a little trickier, because even if the usage
jhnwkmn 0:a9a5c12f2d30 92 of offsetof is valid for this case, C++ compilers advise against it. So, you
jhnwkmn 0:a9a5c12f2d30 93 either make your own (more compatible) offsetof, or bind your own _get /
jhnwkmn 0:a9a5c12f2d30 94 _set metamethods:
jhnwkmn 0:a9a5c12f2d30 95
jhnwkmn 0:a9a5c12f2d30 96 #define OFFSET_OF(st, m) \
jhnwkmn 0:a9a5c12f2d30 97 ((size_t) ( (char *)&(_nullptr<st>()->m) - (char *)0 ))
jhnwkmn 0:a9a5c12f2d30 98
jhnwkmn 0:a9a5c12f2d30 99 class MyClass {
jhnwkmn 0:a9a5c12f2d30 100 public:
jhnwkmn 0:a9a5c12f2d30 101
jhnwkmn 0:a9a5c12f2d30 102 int a;
jhnwkmn 0:a9a5c12f2d30 103 float b;
jhnwkmn 0:a9a5c12f2d30 104 };
jhnwkmn 0:a9a5c12f2d30 105
jhnwkmn 0:a9a5c12f2d30 106 SqBind<MyClass>::bind_member_variable<int>( vm, _SC("a"), OFFSET_OF( MyClass, a) );
jhnwkmn 0:a9a5c12f2d30 107 SqBind<MyClass>::bind_member_variable<float>( vm, _SC("b"), OFFSET_OF( MyClass, b) );
jhnwkmn 0:a9a5c12f2d30 108
jhnwkmn 0:a9a5c12f2d30 109 Note that it's very easy to make mistakes when using this helper, so be very careful
jhnwkmn 0:a9a5c12f2d30 110 with the types and offsets passed.
jhnwkmn 0:a9a5c12f2d30 111
jhnwkmn 0:a9a5c12f2d30 112 -Binding Enums-
jhnwkmn 0:a9a5c12f2d30 113
jhnwkmn 0:a9a5c12f2d30 114 Before binding any function that returns or takes an enum as parameter,
jhnwkmn 0:a9a5c12f2d30 115 SQBind must be told that we are dealing with an integer-like type. This is
jhnwkmn 0:a9a5c12f2d30 116 done by defining (globally) the following macro:
jhnwkmn 0:a9a5c12f2d30 117
jhnwkmn 0:a9a5c12f2d30 118 // not inside a function or method!
jhnwkmn 0:a9a5c12f2d30 119 // note that if vm is not passed, this is not "called"
jhnwkmn 0:a9a5c12f2d30 120 SQBIND_INTEGER( MyEnum );
jhnwkmn 0:a9a5c12f2d30 121
jhnwkmn 0:a9a5c12f2d30 122 -Binding Enum-Values and Constants-
jhnwkmn 0:a9a5c12f2d30 123
jhnwkmn 0:a9a5c12f2d30 124 Depending on wether we are dealing with global, or member enums or
jhnwkmn 0:a9a5c12f2d30 125 constants, there are 2 macros supplied for this:
jhnwkmn 0:a9a5c12f2d30 126
jhnwkmn 0:a9a5c12f2d30 127 // call to bind
jhnwkmn 0:a9a5c12f2d30 128 SQBIND_CLASS_CONSTANT( vm, MyClass, CONSTANT );
jhnwkmn 0:a9a5c12f2d30 129
jhnwkmn 0:a9a5c12f2d30 130 This will make MyClass::CONSTANT accesible to the script. For global
jhnwkmn 0:a9a5c12f2d30 131 constants:
jhnwkmn 0:a9a5c12f2d30 132
jhnwkmn 0:a9a5c12f2d30 133 SQBIND_CONSTANT( vm, CONSTANT );
jhnwkmn 0:a9a5c12f2d30 134
jhnwkmn 0:a9a5c12f2d30 135 -Constructing / Destructing-
jhnwkmn 0:a9a5c12f2d30 136
jhnwkmn 0:a9a5c12f2d30 137 SQBind assumes that the classes you ar binding have a default constructor,
jhnwkmn 0:a9a5c12f2d30 138 a default copy constructor, and that they can be deleted using "delete" (or
jhnwkmn 0:a9a5c12f2d30 139 wathever is supplied in SQBIND_CUSTOMIZE). This is not always the case,
jhnwkmn 0:a9a5c12f2d30 140 specially with classes that contain pure virtual methods, or that are just
jhnwkmn 0:a9a5c12f2d30 141 not designed for it. To solve this issue, am "SqBindAllocator" specialization for
jhnwkmn 0:a9a5c12f2d30 142 a given class must be provided like this:
jhnwkmn 0:a9a5c12f2d30 143
jhnwkmn 0:a9a5c12f2d30 144
jhnwkmn 0:a9a5c12f2d30 145 template<>
jhnwkmn 0:a9a5c12f2d30 146 struct SqBindAllocator<MyClass> {
jhnwkmn 0:a9a5c12f2d30 147
jhnwkmn 0:a9a5c12f2d30 148 static MyClass *construct() {
jhnwkmn 0:a9a5c12f2d30 149
jhnwkmn 0:a9a5c12f2d30 150 return NULL; // make it not able to construct
jhnwkmn 0:a9a5c12f2d30 151 }
jhnwkmn 0:a9a5c12f2d30 152 static SQBIND_INLINE MyClass *copy_construct(const MyClass* p_from) {
jhnwkmn 0:a9a5c12f2d30 153
jhnwkmn 0:a9a5c12f2d30 154 return NULL; // make it not able to copy-construct
jhnwkmn 0:a9a5c12f2d30 155 }
jhnwkmn 0:a9a5c12f2d30 156 static SQBIND_INLINE bool assign(MyClass* p_val, const MyClass* p_from) {
jhnwkmn 0:a9a5c12f2d30 157
jhnwkmn 0:a9a5c12f2d30 158 return false; // make it not able to assign
jhnwkmn 0:a9a5c12f2d30 159 }
jhnwkmn 0:a9a5c12f2d30 160 static SQBIND_INLINE void destruct(MyClass* p_instance) {
jhnwkmn 0:a9a5c12f2d30 161
jhnwkmn 0:a9a5c12f2d30 162 // make it ignore destruction
jhnwkmn 0:a9a5c12f2d30 163 }
jhnwkmn 0:a9a5c12f2d30 164
jhnwkmn 0:a9a5c12f2d30 165 static SQBIND_INLINE MyClass& get_empty() {
jhnwkmn 0:a9a5c12f2d30 166 // if someone tries to assign, this will crash.
jhnwkmn 0:a9a5c12f2d30 167 // however, this will likely never be called anyway.
jhnwkmn 0:a9a5c12f2d30 168 static MyClass *crashplease=NULL;
jhnwkmn 0:a9a5c12f2d30 169 return *crashplease;
jhnwkmn 0:a9a5c12f2d30 170 }
jhnwkmn 0:a9a5c12f2d30 171 }
jhnwkmn 0:a9a5c12f2d30 172
jhnwkmn 0:a9a5c12f2d30 173 By simply defining this allocator before using SqBind<MyClass>, the
jhnwkmn 0:a9a5c12f2d30 174 behavior for such operations will be customized.
jhnwkmn 0:a9a5c12f2d30 175
jhnwkmn 0:a9a5c12f2d30 176 -Custom Constructor-
jhnwkmn 0:a9a5c12f2d30 177
jhnwkmn 0:a9a5c12f2d30 178 By default, SQBind doesn't understand extra construction parameters. It
jhnwkmn 0:a9a5c12f2d30 179 is up to the programmer to create an instance from them. This is done by
jhnwkmn 0:a9a5c12f2d30 180 setting a custom constructor. SqBind will still ease the task of the
jhnwkmn 0:a9a5c12f2d30 181 programmer by not calling the custom constructor when no parameters or copy
jhnwkmn 0:a9a5c12f2d30 182 constructor is required. If you still want to take complete control of
jhnwkmn 0:a9a5c12f2d30 183 the construction stage, bind a "constructor" function manually.
jhnwkmn 0:a9a5c12f2d30 184
jhnwkmn 0:a9a5c12f2d30 185 // class to bind
jhnwkmn 0:a9a5c12f2d30 186
jhnwkmn 0:a9a5c12f2d30 187 class Vector3 {
jhnwkmn 0:a9a5c12f2d30 188 public:
jhnwkmn 0:a9a5c12f2d30 189
jhnwkmn 0:a9a5c12f2d30 190 float x;
jhnwkmn 0:a9a5c12f2d30 191 float y;
jhnwkmn 0:a9a5c12f2d30 192 float z;
jhnwkmn 0:a9a5c12f2d30 193
jhnwkmn 0:a9a5c12f2d30 194 Vector3(float _x=0, float _y=0, float _z=0) {
jhnwkmn 0:a9a5c12f2d30 195
jhnwkmn 0:a9a5c12f2d30 196 x=_x;
jhnwkmn 0:a9a5c12f2d30 197 y=_y;
jhnwkmn 0:a9a5c12f2d30 198 z=_z;
jhnwkmn 0:a9a5c12f2d30 199 }
jhnwkmn 0:a9a5c12f2d30 200 };
jhnwkmn 0:a9a5c12f2d30 201
jhnwkmn 0:a9a5c12f2d30 202 // custom constructor
jhnwkmn 0:a9a5c12f2d30 203
jhnwkmn 0:a9a5c12f2d30 204 static Vector3* vector3_constructor(HSQUIRRELVM v) {
jhnwkmn 0:a9a5c12f2d30 205
jhnwkmn 0:a9a5c12f2d30 206 // regular and copycon handled by sqbind
jhnwkmn 0:a9a5c12f2d30 207
jhnwkmn 0:a9a5c12f2d30 208 int params=sq_gettop(v);
jhnwkmn 0:a9a5c12f2d30 209
jhnwkmn 0:a9a5c12f2d30 210 if (params!=4)
jhnwkmn 0:a9a5c12f2d30 211 return NULL; // need 3 params
jhnwkmn 0:a9a5c12f2d30 212
jhnwkmn 0:a9a5c12f2d30 213 SQFloat x;
jhnwkmn 0:a9a5c12f2d30 214 SQFloat y;
jhnwkmn 0:a9a5c12f2d30 215 SQFloat z;
jhnwkmn 0:a9a5c12f2d30 216
jhnwkmn 0:a9a5c12f2d30 217 if (SQ_FAILED( sq_getfloat(v,2,&x) ) )
jhnwkmn 0:a9a5c12f2d30 218 return NULL;
jhnwkmn 0:a9a5c12f2d30 219 if (SQ_FAILED( sq_getfloat(v,3,&y) ) )
jhnwkmn 0:a9a5c12f2d30 220 return NULL;
jhnwkmn 0:a9a5c12f2d30 221 if (SQ_FAILED( sq_getfloat(v,4,&z) ) )
jhnwkmn 0:a9a5c12f2d30 222 return NULL;
jhnwkmn 0:a9a5c12f2d30 223
jhnwkmn 0:a9a5c12f2d30 224 return new Vector3(x,y,z);
jhnwkmn 0:a9a5c12f2d30 225
jhnwkmn 0:a9a5c12f2d30 226 }
jhnwkmn 0:a9a5c12f2d30 227
jhnwkmn 0:a9a5c12f2d30 228 ..
jhnwkmn 0:a9a5c12f2d30 229
jhnwkmn 0:a9a5c12f2d30 230 int main() {
jhnwkmn 0:a9a5c12f2d30 231
jhnwkmn 0:a9a5c12f2d30 232 SqBind<Vector3>::init(vm,_SC("Vector3"));
jhnwkmn 0:a9a5c12f2d30 233 SqBind<Vector3>::set_custom_constructor( vector3_constructor );
jhnwkmn 0:a9a5c12f2d30 234 }
jhnwkmn 0:a9a5c12f2d30 235
jhnwkmn 0:a9a5c12f2d30 236 -Creating Binders for Native Types-
jhnwkmn 0:a9a5c12f2d30 237
jhnwkmn 0:a9a5c12f2d30 238 Creating binders for C++ types that translate to nativetypes must be done
jhnwkmn 0:a9a5c12f2d30 239 by specializing SqBind. Following is an example for binding std::string with
jhnwkmn 0:a9a5c12f2d30 240 the native Squirrel string:
jhnwkmn 0:a9a5c12f2d30 241
jhnwkmn 0:a9a5c12f2d30 242 template<>
jhnwkmn 0:a9a5c12f2d30 243 class SqBind<std::string> {
jhnwkmn 0:a9a5c12f2d30 244 public:
jhnwkmn 0:a9a5c12f2d30 245 struct Getter {
jhnwkmn 0:a9a5c12f2d30 246 SQBIND_INLINE std::string get(HSQUIRRELVM v, int p_idx) {
jhnwkmn 0:a9a5c12f2d30 247 return SqBind<std::string>::get(v,2);
jhnwkmn 0:a9a5c12f2d30 248 }
jhnwkmn 0:a9a5c12f2d30 249 };
jhnwkmn 0:a9a5c12f2d30 250 struct GetterPtr {
jhnwkmn 0:a9a5c12f2d30 251 std::string temp;
jhnwkmn 0:a9a5c12f2d30 252 SQBIND_INLINE std::string* get(HSQUIRRELVM v, int p_idx) {
jhnwkmn 0:a9a5c12f2d30 253 temp=SqBind<std::string>::get(v,2);
jhnwkmn 0:a9a5c12f2d30 254 return &temp;
jhnwkmn 0:a9a5c12f2d30 255 }
jhnwkmn 0:a9a5c12f2d30 256 };
jhnwkmn 0:a9a5c12f2d30 257 static std::string get(HSQUIRRELVM v, int p_idx) {
jhnwkmn 0:a9a5c12f2d30 258 if (sq_gettype(v,p_idx)!=OT_STRING) {
jhnwkmn 0:a9a5c12f2d30 259 sqbind_throwerror(v,"Type is not string!");
jhnwkmn 0:a9a5c12f2d30 260 return std::string();
jhnwkmn 0:a9a5c12f2d30 261 }
jhnwkmn 0:a9a5c12f2d30 262 const SQChar * str;
jhnwkmn 0:a9a5c12f2d30 263 sq_getstring(v,p_idx,&str);
jhnwkmn 0:a9a5c12f2d30 264 return std::string(str);
jhnwkmn 0:a9a5c12f2d30 265 }
jhnwkmn 0:a9a5c12f2d30 266
jhnwkmn 0:a9a5c12f2d30 267 static void push(HSQUIRRELVM v, const std::string& p_value) {
jhnwkmn 0:a9a5c12f2d30 268 sq_pushstring(v,p_value.c_str(),-1);
jhnwkmn 0:a9a5c12f2d30 269 }
jhnwkmn 0:a9a5c12f2d30 270 };
jhnwkmn 0:a9a5c12f2d30 271
jhnwkmn 0:a9a5c12f2d30 272
jhnwkmn 0:a9a5c12f2d30 273 --License--
jhnwkmn 0:a9a5c12f2d30 274
jhnwkmn 0:a9a5c12f2d30 275 SQBind is provided under the MIT license:
jhnwkmn 0:a9a5c12f2d30 276
jhnwkmn 0:a9a5c12f2d30 277 Copyright (c) 2009 Juan Linietsky
jhnwkmn 0:a9a5c12f2d30 278
jhnwkmn 0:a9a5c12f2d30 279 Permission is hereby granted, free of charge, to any person
jhnwkmn 0:a9a5c12f2d30 280 obtaining a copy of this software and associated documentation
jhnwkmn 0:a9a5c12f2d30 281 files (the "Software"), to deal in the Software without
jhnwkmn 0:a9a5c12f2d30 282 restriction, including without limitation the rights to use,
jhnwkmn 0:a9a5c12f2d30 283 copy, modify, merge, publish, distribute, sublicense, and/or sell
jhnwkmn 0:a9a5c12f2d30 284 copies of the Software, and to permit persons to whom the
jhnwkmn 0:a9a5c12f2d30 285 Software is furnished to do so, subject to the following
jhnwkmn 0:a9a5c12f2d30 286 conditions:
jhnwkmn 0:a9a5c12f2d30 287
jhnwkmn 0:a9a5c12f2d30 288 The above copyright notice and this permission notice shall be
jhnwkmn 0:a9a5c12f2d30 289 included in all copies or substantial portions of the Software.
jhnwkmn 0:a9a5c12f2d30 290
jhnwkmn 0:a9a5c12f2d30 291 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
jhnwkmn 0:a9a5c12f2d30 292 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
jhnwkmn 0:a9a5c12f2d30 293 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
jhnwkmn 0:a9a5c12f2d30 294 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
jhnwkmn 0:a9a5c12f2d30 295 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
jhnwkmn 0:a9a5c12f2d30 296 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
jhnwkmn 0:a9a5c12f2d30 297 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
jhnwkmn 0:a9a5c12f2d30 298 OTHER DEALINGS IN THE SOFTWARE.
jhnwkmn 0:a9a5c12f2d30 299
jhnwkmn 0:a9a5c12f2d30 300 --Contact--
jhnwkmn 0:a9a5c12f2d30 301
jhnwkmn 0:a9a5c12f2d30 302 You can contact the author (Juan Linietsky) via e-mail at:
jhnwkmn 0:a9a5c12f2d30 303 reduzio@gmail.com
jhnwkmn 0:a9a5c12f2d30 304