📄 class_rep.cpp
字号:
// Copyright (c) 2003 Daniel Wallin and Arvid Norberg
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
// OR OTHER DEALINGS IN THE SOFTWARE.
extern "C"
{
#include "lua.h"
}
#include <luabind/luabind.hpp>
#include <utility>
using namespace luabind::detail;
#ifndef LUABIND_NO_ERROR_CHECKING
std::string luabind::detail::get_overload_signatures_candidates(lua_State* L, std::vector<const overload_rep_base*>::iterator start, std::vector<const overload_rep_base*>::iterator end, std::string name)
{
std::string s;
for (; start != end; ++start)
{
s += name;
(*start)->get_signature(L, s);
s += "\n";
}
return s;
}
#endif
luabind::detail::class_rep::class_rep(LUABIND_TYPE_INFO type
, const char* name
, lua_State* L
, void(*destructor)(void*)
, void(*const_holder_destructor)(void*)
, LUABIND_TYPE_INFO holder_type
, LUABIND_TYPE_INFO const_holder_type
, void*(*extractor)(void*)
, const void*(*const_extractor)(void*)
, void(*const_converter)(void*,void*)
, void(*construct_holder)(void*,void*)
, void(*construct_const_holder)(void*,void*)
, int holder_size
, int holder_alignment)
: m_type(type)
, m_holder_type(holder_type)
, m_const_holder_type(const_holder_type)
, m_extractor(extractor)
, m_const_extractor(const_extractor)
, m_const_converter(const_converter)
, m_construct_holder(construct_holder)
, m_construct_const_holder(construct_const_holder)
, m_holder_size(holder_size)
, m_holder_alignment(holder_alignment)
, m_name(name)
, m_table_ref(LUA_NOREF)
, m_class_type(cpp_class)
, m_destructor(destructor)
, m_const_holder_destructor(const_holder_destructor)
, m_operator_cache(0)
{
assert(m_holder_alignment >= 1 && "internal error");
class_registry* r = class_registry::get_registry(L);
assert((r->cpp_class() != LUA_NOREF) && "you must call luabind::open()");
detail::getref(L, r->cpp_class());
lua_setmetatable(L, -2);
lua_pushvalue(L, -1); // duplicate our user data
m_self_ref = detail::ref(L); // pop one of them
m_instance_metatable = r->cpp_instance();
}
luabind::detail::class_rep::class_rep(lua_State* L, const char* name)
: m_type(LUABIND_INVALID_TYPE_INFO)
, m_holder_type(LUABIND_INVALID_TYPE_INFO)
, m_const_holder_type(LUABIND_INVALID_TYPE_INFO)
, m_extractor(0)
, m_const_extractor(0)
, m_const_converter(0)
, m_construct_holder(0)
, m_construct_const_holder(0)
, m_holder_size(0)
, m_holder_alignment(1)
, m_class_type(lua_class)
, m_destructor(0)
, m_const_holder_destructor(0)
, m_operator_cache(0)
{
#ifndef LUABIND_DONT_COPY_STRINGS
m_strings.push_back(detail::dup_string(name));
m_name = m_strings.back();
#else
m_name = name;
#endif
lua_newtable(L);
m_table_ref = detail::ref(L);
class_registry* r = class_registry::get_registry(L);
assert((r->cpp_class() != LUA_NOREF) && "you must call luabind::open()");
detail::getref(L, r->lua_class());
lua_setmetatable(L, -2);
lua_pushvalue(L, -1); // duplicate our user data
m_self_ref = detail::ref(L); // pop one of them
m_instance_metatable = r->lua_instance();
}
luabind::detail::class_rep::~class_rep()
{
#ifndef LUABIND_DONT_COPY_STRINGS
for (std::vector<char*>::iterator i = m_strings.begin();
i != m_strings.end(); ++i)
{
delete[] *i;
}
#endif
}
std::pair<void*,void*>
luabind::detail::class_rep::allocate(lua_State* L) const
{
const int overlap = sizeof(object_rep)&(m_holder_alignment-1);
const int padding = overlap==0?0:m_holder_alignment-overlap;
const int size = sizeof(object_rep) + padding + m_holder_size;
char* mem = static_cast<char*>(lua_newuserdata(L, size));
char* ptr = mem + sizeof(object_rep) + padding;
return std::pair<void*,void*>(mem,ptr);
}
int luabind::detail::class_rep::gettable(lua_State* L)
{
if (lua_isnil(L, 2))
{
lua_pushnil(L);
return 1;
}
object_rep* obj = static_cast<object_rep*>(lua_touserdata(L, 1));
// we have to ignore the first argument since this may point to
// a method that is not present in this class (but in a subclass)
const char* key = lua_tostring(L, 2);
if (key && !strcmp(key, "__ok"))
{
class_rep* crep = obj->crep();
void* p = crep->extractor() ? crep->extractor()(obj->ptr())
: obj->ptr();
lua_pushboolean(L, p != 0);
return 1;
}
std::map<const char*, method_rep, ltstr>::iterator i = m_methods.find(key);
if (i != m_methods.end())
{
// the name is a method, return it
lua_pushlightuserdata(L, &i->second);
lua_pushcclosure(L, function_dispatcher, 1);
return 1;
}
std::map<const char*, callback, ltstr>::iterator j = m_getters.find(key);
if (j != m_getters.end())
{
// the name is a data member
return j->second.func(L, j->second.pointer_offset);
}
lua_pushnil(L);
return 1;
}
// called from the metamethod for __newindex
// the object pointer is passed on the lua stack
bool luabind::detail::class_rep::settable(lua_State* L)
{
if (lua_isnil(L, 2))
{
return false;
}
// we have to ignore the first argument since this may point to
// a method that is not present in this class (but in a subclass)
const char* key = lua_tostring(L, 2);
std::map<const char*, callback, ltstr>::iterator j = m_setters.find(key);
if (j != m_setters.end())
{
// the name is a data member
j->second.func(L, j->second.pointer_offset);
return true;
}
return false; // false means that we don't have a member with the given name
}
int class_rep::gettable_dispatcher(lua_State* L)
{
object_rep* obj = static_cast<object_rep*>(lua_touserdata(L, 1));
return obj->crep()->gettable(L);
}
// this is called as __newindex metamethod on every instance of this class
int luabind::detail::class_rep::settable_dispatcher(lua_State* L)
{
object_rep* obj = static_cast<object_rep*>(lua_touserdata(L, 1));
bool success = obj->crep()->settable(L);
#ifndef LUABIND_NO_ERROR_CHECKING
if (!success)
{
// this block is needed to make sure the std::string is destructed before
// lua_error() is called
#ifdef BOOST_MSVC
{
// msvc has a bug which deletes the string twice, that's
// why we have to create it on the heap
std::string* msg = new std::string("cannot set attribute '");
*msg += obj->crep()->m_name;
*msg += ".";
*msg += lua_tostring(L, -2);
*msg += "'";
lua_pushstring(L, msg->c_str());
delete msg;
}
#else
{
std::string msg = "cannot set attribute '";
msg += obj->crep()->m_name;
msg += ".";
msg += lua_tostring(L, -2);
msg += "'";
lua_pushstring(L, msg.c_str());
}
#endif
lua_error(L);
}
#endif
return 0;
}
int luabind::detail::class_rep::operator_dispatcher(lua_State* L)
{
int id = static_cast<int>(lua_tonumber(L, lua_upvalueindex(1)));
int operand_id = 0;
object_rep* operand[2];
for (int i = 0; i < 2; ++i)
operand[i] = detail::is_class_object(L, i + 1);
// we cannot compare the types here, we have to compare the pointers of the class_reps
// since all lua-classes have the same type (LUABIND_INVALID_TYPE_INFO)
if (operand[0] && operand[1])
if (operand[0]->crep() == operand[1]->crep()) operand[1] = 0;
std::vector<operator_callback>* overloads[2];
for (int i = 0; i < 2; ++i)
if (operand[i]) overloads[i] = &operand[i]->crep()->m_operators[id]; else overloads[i] = 0;
std::size_t num_overloads[2];
for (int i = 0; i < 2; ++i)
if (overloads[i]) num_overloads[i] = overloads[i]->size(); else num_overloads[i] = 0;
for (int i = 0; i < 2; ++i)
if (operand[i] && operand[i]->crep()->get_class_type() == class_rep::lua_class)
{
/* // if this is a lua class we have to
// look in its table to see if there's
// any overload of this operator
detail::getref(L, operand[i]->crep()->table_ref());
lua_pushstring(L, get_operator_name(id));
lua_rawget(L, -2);
// if we have tha operator, set num_overloads to 1
if (lua_isfunction(L, -1)) num_overloads[i] = 1;
lua_pop(L, 2);*/
if (operand[i]->crep()->has_operator_in_lua(L, id))
num_overloads[i] = 1;
}
bool ambiguous = false;
int match_index = -1;
int min_match = std::numeric_limits<int>::max();
#ifdef LUABIND_NO_ERROR_CHECKING
if (num_overloads[0] == 1 && num_overloads[1] == 0)
{
operand_id = 0;
match_index = 0;
}
else if (num_overloads[0] == 0 && num_overloads[1] == 1)
{
operand_id = 1;
match_index = 0;
}
else
{
#endif
int num_params = lua_gettop(L);
// have look at the right operand.
// if the right operand is a class and
// not the same class as this, we have to
// try to match it's operators too
for (int i = 0; i < 2; ++i)
{
if (num_overloads[i])
{
if (operand[i]->crep()->get_class_type() == class_rep::lua_class)
{
// if this is a lua class
// and num_overloads is > 0
// it means that it has implemented
// this operator. Set match_index to
// 0 to signal that this operand has
// an overload, but leave the min_match
// at int-max to mark it as a last resort
operand_id = i;
if (match_index == -1) match_index = 0;
}
else if (find_best_match(
L
, &overloads[i]->front()
, overloads[i]->size()
, sizeof(operator_callback)
, ambiguous
, min_match
, match_index
, num_params))
{
operand_id = i;
}
}
}
#ifdef LUABIND_NO_ERROR_CHECKING
}
#else
if (match_index == -1)
{
// this block is needed to make sure the std::string is destructed before
// lua_error() is called
{
std::string msg = "no operator ";
msg += get_operator_symbol(id);
msg += " matched the arguments (";
msg += stack_content_by_name(L, 1);
msg += ")\ncandidates are:\n";
for (int i = 0; i < 2; ++i)
{
if (overloads[i])
{
msg += get_overload_signatures(
L
, overloads[i]->begin()
, overloads[i]->end()
, get_operator_symbol(id));
}
else
{
// if num_overloads is > 0 it would mean that this is
// a lua class with this operator overloaded. And if
// that's the case, it should always match (and if an
// operator matches, we never come here).
assert(num_overloads[i] == 0 && "internal error");
}
}
lua_pushstring(L, msg.c_str());
}
lua_error(L);
}
else if (ambiguous)
{
// this block is needed to make sure the std::string is destructed before
// lua_error() is called
{
std::string msg = "call of overloaded operator ";
msg += get_operator_symbol(id);
msg += " (";
msg += stack_content_by_name(L, 1);
msg += ")' is ambiguous\nnone of the overloads have a best conversion:\n";
std::vector<const overload_rep_base*> candidates;
if (overloads[0])
find_exact_match(L, &overloads[0]->front(), overloads[0]->size(), sizeof(operator_callback), min_match, num_params, candidates);
if (overloads[1])
find_exact_match(L, &overloads[1]->front(), overloads[1]->size(), sizeof(operator_callback), min_match, num_params, candidates);
msg += get_overload_signatures_candidates(L, candidates.begin(), candidates.end(), get_operator_symbol(id));
lua_pushstring(L, msg.c_str());
}
lua_error(L);
}
#endif
if (operand[operand_id]->crep()->get_class_type() == class_rep::lua_class)
{
detail::getref(L, operand[operand_id]->crep()->table_ref());
lua_pushstring(L, get_operator_name(id));
lua_rawget(L, -2);
lua_insert(L, -4); // move the function to the bottom
lua_pop(L, 1); // remove the table
lua_call(L, 2, 1);
return 1;
}
else
{
return (*overloads[operand_id])[match_index].call(L);
}
}
// this is called as metamethod __call on the class_rep.
int luabind::detail::class_rep::constructor_dispatcher(lua_State* L)
{
class_rep* crep = static_cast<class_rep*>(lua_touserdata(L, 1));
construct_rep* rep = &crep->m_constructor;
bool ambiguous = false;
int match_index = -1;
int min_match = std::numeric_limits<int>::max();
bool found;
#ifdef LUABIND_NO_ERROR_CHECKING
if (rep->overloads.size() == 1)
{
match_index = 0;
}
else
{
#endif
int num_params = lua_gettop(L) - 1;
found = find_best_match(L, &rep->overloads.front(), rep->overloads.size(), sizeof(construct_rep::overload_t), ambiguous, min_match, match_index, num_params);
#ifdef LUABIND_NO_ERROR_CHECKING
}
#else
if (!found)
{
// this block is needed to make sure the std::string is destructed before
// lua_error() is called
{
std::string msg = "no constructor of '";
msg += crep->name();
msg += "' matched the arguments (";
msg += stack_content_by_name(L, 2);
msg += ")\n candidates are:\n";
msg += get_overload_signatures(L, rep->overloads.begin(), rep->overloads.end(), crep->name());
lua_pushstring(L, msg.c_str());
}
lua_error(L);
}
else if (ambiguous)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -