-- flags local disable_virtual_hooks = true local enable_pure_virtual = true local default_private_access = false local access = {public = 0, protected = 1, private = 2} function preparse_hook(p) if default_private_access then -- we need to make all structs 'public' by default p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n") end end function parser_hook(s) local container = classContainer.curr -- get the current container if default_private_access then if not container.curr_member_access and container.classtype == 'class' then -- default access for classes is private container.curr_member_access = access.private end end -- try labels (public, private, etc) do local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type' if b then -- found a label, get the new access value from the global 'access' table if access[label] then container.curr_member_access = access[label] end -- else ? return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:] end end local ret = nil if disable_virtual_hooks then return ret end local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())") local const if b then local ret = string.sub(s, e+1) if string.find(ret, "^%s*const") then const = "const" ret = string.gsub(ret, "^%s*const", "") end local purev = false if string.find(ret, "^%s*=%s*0") then purev = true ret = string.gsub(ret, "^%s*=%s*0", "") end ret = string.gsub(ret, "^%s*%b{}", "") local func = Function(decl, arg, const) func.pure_virtual = purev --func.access = access func.original_sig = decl local curflags = classContainer.curr.flags if not curflags.virtual_class then curflags.virtual_class = VirtualClass() end curflags.virtual_class:add(func) curflags.pure_virtual = curflags.pure_virtual or purev return ret end return ret end -- class VirtualClass classVirtualClass = { classtype = 'class', name = '', base = '', type = '', btype = '', ctype = '', } classVirtualClass.__index = classVirtualClass setmetatable(classVirtualClass,classClass) function classVirtualClass:add(f) local parent = classContainer.curr pop() table.insert(self.methods, {f=f}) local name,sig -- doble negative means positive if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then name = self.original_name elseif f.name == 'delete' then name = '~'..self.original_name else if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name end end if name then sig = name..self:get_arg_list(f, true)..";\n" push(self) sig = preprocess(sig) self:parse(sig) pop() end push(parent) end function preprocess(sig) sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*' return sig end function classVirtualClass:get_arg_list(f, decl) local ret = "" local sep = "" local i=1 while f.args[i] do local arg = f.args[i] if decl then local ptr if arg.ret ~= '' then ptr = arg.ret else ptr = arg.ptr end local def = "" if arg.def and arg.def ~= "" then def = " = "..arg.def end ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def else ret = ret..sep..arg.name end sep = "," i = i+1 end return "("..ret..")" end function classVirtualClass:add_parent_virtual_methods(parent) parent = parent or _global_classes[self.flags.parent_object.btype] if not parent then return end if parent.flags.virtual_class then local vclass = parent.flags.virtual_class for k,v in ipairs(vclass.methods) do if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then table.insert(self.methods, {f=v.f}) end end end parent = _global_classes[parent.btype] if parent then self:add_parent_virtual_methods(parent) end end function classVirtualClass:has_method(f) for k,v in pairs(self.methods) do -- just match name for now if v.f.name == f.name then return true end end return false end function classVirtualClass:add_constructors() local i=1 while self.flags.parent_object[i] do local v = self.flags.parent_object[i] if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then self:add(v) end i = i+1 end end --[[ function classVirtualClass:requirecollection(t) self:add_constructors() local req = classClass.requirecollection(self, t) if req then output('class ',self.name,";") end return req end --]] function classVirtualClass:supcode() -- pure virtual classes can have no default constructors on gcc 4 if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n') end local ns if self.prox.classtype == 'namespace' then output('namespace ',self.prox.name, " {") ns = true end output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {") output("public:\n") self:add_parent_virtual_methods() self:output_methods(self.btype) self:output_parent_methods() self:add_constructors() -- no constructor for pure virtual classes if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then self:output_constructors() end output("};\n\n") if ns then output("};") end classClass.supcode(self) if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then output('#endif // __GNUC__ >= 4\n') end -- output collector for custom class if required if self:requirecollection(_collect) and _collect[self.type] then output('\n') output('/* function to release collected object via destructor */') output('#ifdef __cplusplus\n') --for i,v in pairs(collect) do i,v = self.type, _collect[self.type] output('\nstatic int '..v..' (lua_State* tolua_S)') output('{') output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);') output(' delete self;') output(' return 0;') output('}') --end output('#endif\n\n') end end function classVirtualClass:register(pre) -- pure virtual classes can have no default constructors on gcc 4 if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n') end classClass.register(self, pre) if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then output('#endif // __GNUC__ >= 4\n') end end --function classVirtualClass:requirecollection(_c) -- if self.flags.parent_object.flags.pure_virtual then -- return false -- end -- return classClass.requirecollection(self, _c) --end function classVirtualClass:output_parent_methods() for k,v in ipairs(self.methods) do if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." " local parent_name = rettype..self.btype.."__"..v.f.name local par_list = self:get_arg_list(v.f, true) local var_list = self:get_arg_list(v.f, false) -- the parent's virtual function output("\t"..parent_name..par_list.." {") output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";") output("\t};") end end end function classVirtualClass:output_methods(btype) for k,v in ipairs(self.methods) do if v.f.name ~= 'new' and v.f.name ~= 'delete' then self:output_method(v.f, btype) end end output("\n") end function classVirtualClass:output_constructors() for k,v in ipairs(self.methods) do if v.f.name == 'new' then local par_list = self:get_arg_list(v.f, true) local var_list = self:get_arg_list(v.f, false) output("\t",self.original_name,par_list,":",self.btype,var_list,"{};") end end end function classVirtualClass:output_method(f, btype) if f.access == 2 then -- private return end local ptr if f.ret ~= '' then ptr = f.ret else ptr = f.ptr end local rettype = f.mod.." "..f.type..f.ptr.." " local par_list = self:get_arg_list(f, true) local var_list = self:get_arg_list(f, false) if string.find(rettype, "%s*LuaQtGenericFlags%s*") then _,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+") end -- the caller of the lua method output("\t"..rettype.." "..f.name..par_list..f.const.." {") local fn = f.cname if f.access == 1 then fn = "NULL" end output('\t\tif (push_method("',f.lname,'", ',fn,')) {') --if f.type ~= 'void' then -- output("\t\t\tint top = lua_gettop(lua_state)-1;") --end -- push the parameters local argn = 0 for i,arg in ipairs(f.args) do if arg.type ~= 'void' then local t,ct = isbasic(arg.type) if t and t ~= '' then if arg.ret == "*" then t = 'userdata' ct = 'void*' end output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");"); else local m = arg.ptr if m and m~= "" then if m == "*" then m = "" end output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");") else output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n") output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n') end end argn = argn+1 end end -- call the function output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ") -- return value if f.type ~= 'void' then output("1);") local t,ct = isbasic(f.type) if t and t ~= '' then --output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);") output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);") else local mod = "" if f.ptr ~= "*" then mod = "*("..f.type.."*)" end --output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);") output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);") end output("\t\t\tlua_pop(lua_state, 1);") output("\t\t\treturn tolua_ret;") else output("0);") end -- handle non-implemeted function output("\t\t} else {") if f.pure_virtual then output('\t\t\tif (lua_state)') --output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);') output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");') output('\t\t\telse {') output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");') output('\t\t\t\t::abort();') output('\t\t\t};') if( rettype == " std::string " ) then output('\t\t\treturn "";') else output('\t\t\treturn (',rettype,')0;') end else output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';') end output("\t\t};") output("\t};") end function VirtualClass() local parent = classContainer.curr pop() local name = "Lua__"..parent.original_name local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil}) setmetatable(c, classVirtualClass) local ft = getnamespace(c.parent)..c.original_name append_global_type(ft, c) push(parent) c.flags.parent_object = parent c.methods = {} push(c) c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n") pop() return c end function post_output_hook() print("Bindings have been generated.") end