From bf48768e04e014d1d795ddaea8a53c5576171c43 Mon Sep 17 00:00:00 2001 From: William S Fulton Date: Tue, 20 Sep 2005 20:57:30 +0000 Subject: [PATCH] Lua improvements - Mark Gossage patch #1295168 git-svn-id: https://swig.svn.sourceforge.net/svnroot/swig/trunk/SWIG@7476 626c5289-ae23-0410-ae9c-e8d60b6d4f22 --- .../test-suite/lua/li_std_string_runme.lua | 104 ++++++++++++++ .../test-suite/lua/li_std_vector_runme.lua | 66 +++++++++ Lib/lua/std_except.i | 17 +++ Lib/lua/std_string.i | 122 ++++++++++++++++ Lib/lua/std_vector.i | 133 ++++++++++++++++++ 5 files changed, 442 insertions(+) create mode 100644 Examples/test-suite/lua/li_std_string_runme.lua create mode 100644 Examples/test-suite/lua/li_std_vector_runme.lua create mode 100644 Lib/lua/std_except.i create mode 100644 Lib/lua/std_string.i create mode 100644 Lib/lua/std_vector.i diff --git a/Examples/test-suite/lua/li_std_string_runme.lua b/Examples/test-suite/lua/li_std_string_runme.lua new file mode 100644 index 000000000..f1a871fa8 --- /dev/null +++ b/Examples/test-suite/lua/li_std_string_runme.lua @@ -0,0 +1,104 @@ +require("import") -- the import fn +import("li_std_string") -- import lib + +for k,v in pairs(li_std_string) do _G[k]=v end -- move to global + +-- catch "undefined" global variables +setmetatable(getfenv(),{__index=function (t,i) error("undefined global variable `"..i.."'",2) end}) + +-- helper to check type +function is_std_string(s) + return type(s)=='userdata' and swig_type(s)=='_p_std__string' +end + +-- std::string by value is just a Lua string +s=test_value("foo") +assert(type(s)=="string" and s=="foo") + +-- std::string by const ref is also just a Lua string +s=test_const_reference("foo") +assert(type(s)=="string" and s=="foo") + +-- std:string* is an object +obj=test_pointer_out() +assert(is_std_string(obj) and obj:c_str()=="x") -- check type & value + +test_pointer(obj) -- this wants an object + +cobj=test_const_pointer_out() +assert(is_std_string(cobj) and cobj:c_str()=="x") -- check type & value + +test_const_pointer(cobj) + +-- this shouldnt work, but it does +-- swig doesnt appear to diff between const object ptrs & object ptrs very well +test_pointer(cobj) -- this wants an non const object (give it a const one!) + +-- refs are also wrappered as ptrs (unless the correct typemaps are applied) +robj=test_reference_out() +assert(is_std_string(robj) and robj:c_str()=="test_reference_out message") -- check type & value + +test_reference(robj) +test_reference(obj) -- object ptr is ok +test_reference(cobj) -- obj const ptr is also ok + +-- throwing string +ok,ex=pcall(test_throw) +assert(ok==false and type(ex)=="string") -- failed & threw string + +ok,ex=pcall(test_const_reference_throw) +assert(ok==false and type(ex)=="string") -- failed & threw string + +-- const ptrs are thrown as str::string** +-- not quite right +ok,ex=pcall(test_const_pointer_throw) +assert(ok==false and type(ex)=="userdata") -- failed & threw object + +-- ditto non const ptrs +ok,ex=pcall(test_pointer_throw) +assert(ok==false and type(ex)=="userdata") -- failed & threw object + +-- testing the structure: + +s=Structure() + +-- because of the SWIG's 'everything else is a ptr' +-- member strings are a little unusal +assert(is_std_string(s.MemberString)) -- std::string* +assert(type(s.MemberString2)=="string") -- typemaps make this a string +assert(is_std_string(s.ConstMemberString)) -- std::string* + +-- set them +s.MemberString:assign("a") -- as its std::string* must use assign +s.MemberString2="b" -- typemaps ok +s.ConstMemberString="c" -- silently ignored +s.ConstMemberString:assign("c") -- works (oops!!!) +--print(s.MemberString:data(),s.MemberString2,s.ConstMemberString:data()) + +--check type again +assert(is_std_string(s.MemberString)) -- std::string* +assert(type(s.MemberString2)=="string") -- typemaps make this a string +assert(is_std_string(s.ConstMemberString)) -- std::string* + +-- for static types: they are really variables, +-- so we must still use the module name + +-- check static type +assert(is_std_string(li_std_string.Structure_StaticMemberString)) +assert(type(li_std_string.Structure_StaticMemberString2)=="string") +assert(is_std_string(li_std_string.Structure_ConstStaticMemberString)) + +-- try setting +li_std_string.Structure_StaticMemberString:assign('d') +li_std_string.Structure_StaticMemberString2='e' +li_std_string.Structure_ConstStaticMemberString='f' -- silently ignored +li_std_string.Structure_ConstStaticMemberString:assign('f') -- works (oops!!!) +--[[print(li_std_string.Structure_StaticMemberString:data(), + li_std_string.Structure_StaticMemberString2, + li_std_string.Structure_ConstStaticMemberString:data())]] + +-- check static type again +assert(is_std_string(li_std_string.Structure_StaticMemberString)) +assert(type(li_std_string.Structure_StaticMemberString2)=="string") +assert(is_std_string(li_std_string.Structure_ConstStaticMemberString)) + diff --git a/Examples/test-suite/lua/li_std_vector_runme.lua b/Examples/test-suite/lua/li_std_vector_runme.lua new file mode 100644 index 000000000..37e268cd9 --- /dev/null +++ b/Examples/test-suite/lua/li_std_vector_runme.lua @@ -0,0 +1,66 @@ +require("import") -- the import fn +import("li_std_vector") -- import code + +for k,v in pairs(li_std_vector) do _G[k]=v end -- move to global + +iv = IntVector(4) +for i=0,3 do + iv[i] = i +end + +for i=0,3 do assert(iv[i]==i) end + +x = average(iv) + +function near(x,y) return math.abs(x-y)<0.001 end + +assert(near(x,1.5)) + +rv = RealVector() +rv:push_back(10) +rv:push_back(10.5) +rv:push_back(11) +rv:push_back(11.5) + +a=half(rv) +for i=0,rv:size()-1 do + assert(near(a[i],rv[i]/2)) +end + +dv = DoubleVector(10) +for i=0,9 do dv[i] = i/2.0 end + +halve_in_place(dv) + +for i=0,9 do + assert(near(dv[i],i/4)) +end + +sv=StructVector(4) + +for i=0,3 do + sv[i]=Struct(i) +end + +for i=0,3 do + assert( swig_type(sv[i]) =='_p_Struct' and sv[i].num==i) +end + +-- range checking +idx=0 +function test_set() iv[idx]=0 end +function test_get() iv[idx]=0 end + +idx=0 --ok +assert(pcall(test_get)==true) +assert(pcall(test_set)==true) +idx=-1 --should error +assert(pcall(test_get)==false) +assert(pcall(test_set)==false) +idx=3 --ok +assert(pcall(test_get)==true) +assert(pcall(test_set)==true) +idx=4 --should error +assert(pcall(test_get)==false) +assert(pcall(test_set)==false) + diff --git a/Lib/lua/std_except.i b/Lib/lua/std_except.i new file mode 100644 index 000000000..3425bd152 --- /dev/null +++ b/Lib/lua/std_except.i @@ -0,0 +1,17 @@ +// Typemaps used by the STL wrappers that throw exceptions. +// These typemaps are used when methods are declared with an STL exception specification, such as +// size_t at() const throw (std::out_of_range); + +%{ +#include +%} +%include "exception.i" + +%typemap(throws) std::out_of_range %{ +SWIG_exception(SWIG_IndexError, $1.what()); %} + +%typemap(throws) std::exception %{ +SWIG_exception(SWIG_SystemError, $1.what()); %} + +%typemap(throws) std::exception& %{ +SWIG_exception(SWIG_SystemError, ($1)->what()); %} diff --git a/Lib/lua/std_string.i b/Lib/lua/std_string.i new file mode 100644 index 000000000..69df787fb --- /dev/null +++ b/Lib/lua/std_string.i @@ -0,0 +1,122 @@ +/*********************************************************************** + * std_string.i + * + * std::string typemaps for LUA + * + * Author : Mark Gossage (mark@gossage.cjb.net) + ************************************************************************/ + +%{ + #include +%} +/* +Only std::string and const std::string& are typemaped +they are converted to the Lua strings automatically + +eg. + +std::string test_value(std::string x) { + return x; +} + +can be used as + +s="hello world" +s2=test_value(s) +assert(s==s2) + +*/ +%typemap(in,checkfn="lua_isstring") std::string +%{$1 = (char*)lua_tostring(L, $input);%} + +%typemap(out) std::string +%{ lua_pushstring(L,$1.c_str()); SWIG_arg++;%} + +%typemap(in,checkfn="lua_isstring") const std::string& (std::string temp) +%{temp=(char*)lua_tostring(L, $input); $1=&temp;%} +%typemap(out) const std::string& +%{ lua_pushstring(L,$1->c_str()); SWIG_arg++;%} + +%typemap(throws) std::string,const std::string& +%{lua_pushstring(L,$1.c_str()); +SWIG_fail; %} + +/* +std::string& can be wrappered, but you must inform SWIG if it is in or out + +eg: +void fn(std::string& str); +Is this an in/out/inout value? + +Therefore you need the usual +%apply (std::string& INOUT) {(std::string& str)}; +or +%apply std::string& INOUT {std::string& str}; +typemaps to tell SWIG what to do. +*/ + +%typemap(in) std::string &INPUT=const std::string &; +%typemap(in, numinputs=0) std::string &OUTPUT (std::string temp) +%{ $1 = &temp; %} +%typemap(argout) std::string &OUTPUT +%{ lua_pushstring(L,$1.c_str()); SWIG_arg++;%} +%typemap(in) std::string &INOUT =const std::string &; +%typemap(argout) std::string &INOUT = std::string &OUTPUT; + +/* +For const std::string* and std::string* is not clear +is this a pointer or an array? + +Therefore just leaving it as is +(there is some rough code below which could be used if needed + +// SWIG wraps const ref's as pointer +// typemaps to deal with this and const ptrs +%typemap(in,checkfn="lua_isstring") + const std::string& INPUT(std::string temp), + const std::string* INPUT(std::string temp) +%{temp=(char*)lua_tostring(L, $input); $1=&temp;%} +%typemap(out) const std::string&, const std::string* +%{ lua_pushstring(L,$1->c_str()); SWIG_arg++;%} + +// the non-const pointer version +%typemap(in) std::string *INPUT=const std::string *INPUT; +%typemap(in, numinputs=0) std::string *OUTPUT (std::string temp) +%{ $1 = &temp; %} +%typemap(argout) std::string *OUTPUT +%{ lua_pushstring(L,$1->c_str()); SWIG_arg++;%} +%typemap(in) std::string *INOUT = std::string *INPUT; +%typemap(argout) std::string *INOUT = std::string *OUTPUT; + +*/ + +/* +A really cut down version of the string class + +This provides basic mapping of lua strings <-> std::string +and little else +(the std::string has a lot of unneeded functions anyway) + +*/ +namespace std { + + class string { + public: + string(); + string(const char*); + string(const string&); + unsigned int size() const; + unsigned int length() const; + bool empty() const; + // no support for operator[] + const char* c_str()const; + const char* data()const; + // assign does not return a copy of this object + // (no point in a scripting language) + void assign(const char*); + void assign(const string&); + // no support for all the other features + // its probably better to do it in lua + }; +} + diff --git a/Lib/lua/std_vector.i b/Lib/lua/std_vector.i new file mode 100644 index 000000000..e6e589f90 --- /dev/null +++ b/Lib/lua/std_vector.i @@ -0,0 +1,133 @@ +/*********************************************************************** + * std_vector.i + * + * std::vector typemaps for LUA + * + * Author : Mark Gossage (mark@gossage.cjb.net) + ************************************************************************/ + +%{ +#include +%} +%include // the general exepctions +/* +A really cut down version of the vector class. + +Note: this does not match the true std::vector class +but instead is an approximate, so that SWIG knows how to wrapper it. +(Eg, all access is by value, not ref, as SWIG turns refs to pointers) + +And no support for iterators & insert/erase + +It would be useful to have a vector<->Lua table conversion routine + +*/ +namespace std { + + template + class vector { + public: + vector(); + vector(unsigned int); + vector(const vector&); + vector(unsigned int,T); + unsigned int size() const; + unsigned int max_size() const; + bool empty() const; + void clear(); + void push_back(T val); + void pop_back(); + T front()const; // only read front & back + T back()const; // not write to them + // operator [] given later: + + %extend // this is a extra bit of SWIG code + { + // [] is replaced by __getitem__ & __setitem__ + // simply throws a string, which causes a lua error + T __getitem__(unsigned int idx) throw (std::out_of_range) + { + if (idx>=self->size()) + throw std::out_of_range("in vector::__getitem__()"); + return (*self)[idx]; + } + void __setitem__(unsigned int idx,T val) throw (std::out_of_range) + { + if (idx>=self->size()) + throw std::out_of_range("in vector::__setitem__()"); + (*self)[idx]=val; + } + }; + }; + +} + +/* +Vector<->LuaTable fns +These look a bit like the array<->LuaTable fns +but are templated, not %defined +(you must have template support for STL) + +*/ +/* +%{ +// reads a table into a vector of numbers +// lua numbers will be cast into the type required (rounding may occur) +// return 0 if non numbers found in the table +// returns new'ed ptr if ok +template +std::vector* SWIG_read_number_vector(lua_State* L,int index) +{ + int i=0; + std::vector* vec=new std::vector(); + while(1) + { + lua_rawgeti(L,index,i+1); + if (!lua_isnil(L,-1)) + { + lua_pop(L,1); + break; // finished + } + if (!lua_isnumber(L,-1)) + { + lua_pop(L,1); + delete vec; + return 0; // error + } + vec->push_back((T)lua_tonumber(L,-1)); + lua_pop(L,1); + ++i; + } + return vec; // ok +} +// writes a vector of numbers out as a lua table +template +int SWIG_write_number_vector(lua_State* L,std::vector *vec) +{ + lua_newtable(L); + for(int i=0;isize();++i) + { + lua_pushnumber(L,(double)((*vec)[i])); + lua_rawseti(L,-2,i+1);// -1 is the number, -2 is the table + } +} +%} + +// then the typemaps + +%define SWIG_TYPEMAP_NUM_VECTOR(T) + +// in +%typemap(in) std::vector *INPUT +%{ $1 = SWIG_read_number_vector(L,$input); + if (!$1) SWIG_fail;%} + +%typemap(freearg) std::vector *INPUT +%{ delete $1;%} + +// out +%typemap(argout) std::vector *OUTPUT +%{ SWIG_write_number_vector(L,$1); SWIG_arg++; %} + +%enddef +*/ \ No newline at end of file