Click here to Skip to main content
15,892,059 members
Articles / Database Development / SQL Server

A scripted SQL query generation framework with IDE: SQLpp (v1.4)

Rate me:
Please Sign up or sign in to vote.
4.98/5 (47 votes)
12 Sep 200311 min read 413.6K   5.4K   133  
A helper framework for generation of SQL queries in C++ and Lua
// 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.


#ifndef LUABIND_CLASS_REP_HPP_INCLUDED
#define LUABIND_CLASS_REP_HPP_INCLUDED

//#include <cstdlib>

#include <boost/limits.hpp>
#include <boost/preprocessor/repetition/enum_params_with_a_default.hpp>

#include <luabind/config.hpp>
#include <luabind/detail/object_rep.hpp>
#include <luabind/detail/construct_rep.hpp>
#include <luabind/detail/method_rep.hpp>
#include <luabind/detail/garbage_collector.hpp>
#include <luabind/detail/operator_id.hpp>
#include <luabind/detail/signature_match.hpp>
#include <luabind/detail/class_registry.hpp>
#include <luabind/detail/find_best_match.hpp>
#include <luabind/detail/get_overload_signature.hpp>
#include <luabind/detail/error.hpp>

namespace luabind
{

	template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(LUABIND_MAX_BASES, class A, detail::null_type)>
	struct bases {};
	typedef bases<detail::null_type> no_bases;

	struct class_base;

}

namespace luabind { namespace detail
{
	std::string stack_content_by_name(lua_State* L, int start_index);
	int construct_lua_class_callback(lua_State* L);

	// this is class-specific information, poor man's vtable
	// this is allocated statically (removed by the compiler)
	// a pointer to this structure is stored in the lua tables'
	// metatable with the name __classrep
	// it is used when matching parameters to function calls
	// to determine possible implicit casts
	// it is also used when finding the best match for overloaded
	// methods
	class class_rep
	{
	friend struct luabind::class_base;
	friend int super_callback(lua_State*);
//TODO: avoid the lua-prefix
	friend int lua_class_gettable(lua_State*);
	friend int lua_class_settable(lua_State*);
	friend int static_class_gettable(lua_State*);
	public:

		enum class_type
		{
			cpp_class = 0,
			lua_class = 1
		};

		// destructor is a lua callback function that is hooked as garbage collector event on every instance
		// of this class (including those that is not owned by lua). It gets an object_rep as argument
		// on the lua stack. It should delete the object pointed to by object_rep::ptr if object_pre::flags
		// is object_rep::owner (which means that lua owns the object)

		// EXPECTS THE TOP VALUE ON THE LUA STACK TO
		// BE THE USER DATA WHERE THIS CLASS IS BEING
		// INSTANTIATED!
		class_rep(LUABIND_TYPE_INFO t, const char* name, lua_State* L, void(*destructor)(void*), LUABIND_TYPE_INFO held_t, void*(*extractor)(void*))
			: m_type(t)
			, m_held_type(held_t)
			, m_extract_underlying_fun(extractor)
			, m_name(name)
			, m_class_type(cpp_class)
			, m_destructor(destructor)
		{

			// TODO: don't we need to copy the name?
			class_registry* r = class_registry::get_registry(L);
			assert((r->cpp_class() != LUA_NOREF) && "you must call luabind::open()"); // 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();
		}

		// used when creating a lua class
		// EXPECTS THE TOP VALUE ON THE LUA STACK TO
		// BE THE USER DATA WHERE THIS CLASS IS BEING
		// INSTANTIATED!
		class_rep(lua_State* L, const char* name)
			: m_type(LUABIND_TYPEID(int))
			, m_held_type(0)
			, m_extract_underlying_fun(0)
			, m_name(name)
			, m_class_type(lua_class)
		{
			// TODO: don't we need to copy the name?
			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()"); // 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();
		}

		~class_rep()
		{
#ifndef LUABIND_DONT_COPY_STRINGS
			for (std::vector<char*>::iterator i = m_strings.begin(); i != m_strings.end(); ++i)
			{
				delete[] *i;
			}
#endif
		}


		// called from the metamethod for __index
		// the object pointer is passed on the lua stack
		int gettable(lua_State* L)
		{
			if (lua_isnil(L, 2))
			{
				lua_pushnil(L);
				return 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);
			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 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
		}

		// this is called as __index metamethod on every instance of this class
		static int 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
		static int 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;
		}

		static int 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);

			if (operand[0] && operand[1])
				if (LUABIND_TYPE_INFO_EQUAL(operand[0]->crep()->type(), operand[1]->crep()->type())) 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;

			bool ambiguous = false;
			int match_index = -1;
			int min_match = std::numeric_limits<int>::max();

//			std::cout << "operator_dispatcher\n";
//			std::cout << "num overloads: " << num_overloads[0] + num_overloads[1] << "\n";
//			std::cout << "operator: " << id << "\n";

#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);
				if (overloads[0])
				{
					if (find_best_match(L, overloads[0]->begin(), overloads[0]->end(), ambiguous, min_match, match_index, num_params))
						operand_id = 0;
				}

				// 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	

				if (overloads[1])
				{
					if(find_best_match(L, overloads[1]->begin(), overloads[1]->end(), ambiguous, min_match, match_index, num_params))
						operand_id = 1;
				}

#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";

					if (overloads[0])
						msg += get_overload_signatures(L, overloads[0]->begin(), overloads[0]->end(), get_operator_symbol(id));

					if (overloads[1])
						msg += get_overload_signatures(L, overloads[1]->begin(), overloads[1]->end(), get_operator_symbol(id));
					
					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<operator_callback> candidates;
					if (overloads[0])
						find_exact_match(L, overloads[0]->begin(), overloads[0]->end(), min_match, num_params, candidates);

					if (overloads[1])
						find_exact_match(L, overloads[1]->begin(), overloads[1]->end(), min_match, num_params, candidates);

					msg += get_overload_signatures(L, candidates.begin(), candidates.end(), get_operator_symbol(id));


					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}

#endif

			return (*overloads[operand_id])[match_index].call(L);
		}


		// this is called as metamethod __call on the class_rep.
		static int 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.begin(), rep->overloads.end(), 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)
			{
				// this block is needed to make sure the std::string is destructed before
				// lua_error() is called
				{
					std::string msg = "call of overloaded constructor '";
					msg += crep->m_name;
					msg +=  "(";
					msg += stack_content_by_name(L, 2);
					msg += ")' is ambiguous\nnone of the overloads have a best conversion:\n";

					std::vector<construct_rep::overload_t> candidates;
					find_exact_match(L, rep->overloads.begin(), rep->overloads.end(), min_match, num_params, candidates);
					msg += get_overload_signatures(L, candidates.begin(), candidates.end(), crep->name());

					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}

#endif

#ifndef LUABIND_NO_EXCEPTIONS

			try
			{

#endif

				void* object_ptr = rep->overloads[match_index].construct(L);

				void* obj_rep = lua_newuserdata(L, sizeof(object_rep));
				new(obj_rep) object_rep(object_ptr, crep, object_rep::owner, crep->destructor());

				detail::getref(L, crep->m_instance_metatable);
				lua_setmetatable(L, -2);
				return 1;

#ifndef LUABIND_NO_EXCEPTIONS

			}

			catch(const std::exception& e)
			{
				lua_pushstring(L, e.what());
			}
			catch(const char* s)
			{
				lua_pushstring(L, s);
			}
			catch(...)
			{
				{
					std::string msg = crep->name();
					msg += "() threw an exception";
					lua_pushstring(L, msg.c_str());
				}
			}

			// we can only reach this line if an exception was thrown
			lua_error(L);
			return 0; // will never be reached

#endif

		}

		static int implicit_cast(const class_rep* from, const class_rep* to, int& pointer_offset)
		{
			int offset = 0;
			if (LUABIND_TYPE_INFO_EQUAL(from->type(), to->type())) return 0;

			for (std::vector<class_rep::base_info>::const_iterator i = from->bases().begin(); i != from->bases().end(); ++i)
			{
				int steps = implicit_cast(i->base, to, offset);
				pointer_offset = offset + i->pointer_offset;
				if (steps >= 0) return steps + 2;
			}
			return -1;
		}

		// the functions dispatcher assumes the following:
		// there is one upvalue that points to the method_rep that this dispatcher is to call
		// the first parameter on the lua stack is an object_rep that points to the object the
		// call is being made on
		static int function_dispatcher(lua_State* L)
		{
			method_rep* rep = static_cast<method_rep*>(lua_touserdata(L, lua_upvalueindex(1)));
			object_rep* obj = reinterpret_cast<object_rep*>(lua_touserdata(L, 1));

#ifndef LUABIND_NO_ERROR_CHECKING

			if (is_class_object(L, 1) == 0)
			{
				{
					std::string msg = "No self reference given as first parameter to member function '";
					msg += rep->crep->name();
					msg += ":";
					msg += rep->name;
					msg += "'. Have you used '.' instead of ':'?";

					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}

			int p;
			if (implicit_cast(obj->crep(), rep->crep, p) < 0)
			{
				{
					std::string msg = "invalid self reference given to '";
					msg += rep->crep->name();
					msg += ":";
					msg += rep->name;
					msg += "'";
					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}

#endif

			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().begin(), rep->overloads().end(), ambiguous, min_match, match_index, num_params);

#ifdef LUABIND_NO_ERROR_CHECKING

			}

#else

			if (!found)
			{
				{
					std::string msg = "no overload of  '";
					msg += rep->crep->name();
					msg += ":";
					msg += rep->name;
					msg += "' matched the arguments (";
					msg += stack_content_by_name(L, 2);
					msg += ")\ncandidates are:\n";

					std::string function_name;
					function_name += rep->crep->name();
					function_name += ":";
					function_name += rep->name;

					msg += get_overload_signatures(L, rep->overloads().begin(), rep->overloads().end(), function_name);

					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}
			else if (ambiguous)
			{
				{
					std::string msg = "call of overloaded  '";
					msg += rep->crep->name();
					msg += ":";
					msg += rep->name;
					msg += "(";
					msg += stack_content_by_name(L, 2);
					msg += ")' is ambiguous\nnone of the overloads have a best conversion:\n";

					std::vector<overload_rep> candidates;
					find_exact_match(L, rep->overloads().begin(), rep->overloads().end(), min_match, num_params, candidates);

					std::string function_name;
					function_name += rep->crep->name();
					function_name += ":";
					function_name += rep->name;

					msg += get_overload_signatures(L, candidates.begin(), candidates.end(), function_name);

					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}

#endif

#ifndef LUABIND_NO_EXCEPTIONS

			try
			{

#endif

				const overload_rep& o = rep->overloads()[match_index];
				return o.call(L, *obj);

#ifndef LUABIND_NO_EXCEPTIONS

			}
			catch(const std::exception& e)
			{
				lua_pushstring(L, e.what());
			}
			catch (const char* s)
			{
				lua_pushstring(L, s);
			}
			catch(...)
			{
				std::string msg = rep->crep->name();
				msg += ":";
				msg += rep->name;
				msg += "() threw an exception";
				lua_pushstring(L, msg.c_str());
			}
			// we can only reach this line if an exception was thrown
			lua_error(L);
			return 0; // will never be reached

#endif
			
		}






		struct base_info
		{
			int pointer_offset; // the offset added to the pointer to obtain a basepointer (due to multiple-inheritance)
			class_rep* base;
		};


		inline void add_base_class(const base_info& binfo)
		{
			// If you hit this assert you are deriving from a type that is not registered
			// in lua. That is, in the class_<> you are giving a baseclass that isn't registered.
			// Please note that if you don't need to have access to the base class or the
			// conversion from the derived class to the base class, you don't need
			// to tell luabind that it derives.
			assert(binfo.base && "You cannot derive from an unregistered type");

			class_rep* bcrep = binfo.base;

			// import all functions from the base
			for (std::map<const char*, method_rep, ltstr>::const_iterator i = bcrep->m_methods.begin(); i != bcrep->m_methods.end(); ++i)
			{
#ifndef LUABIND_DONT_COPY_STRINGS
				m_strings.push_back(dup_string(i->first));
				method_rep& m = m_methods[m_strings.back()];
#else
				method_rep& m = m_methods[i->first];
#endif
				m.name = i->first;
				m.crep = this;

				for (std::vector<overload_rep>::const_iterator j = i->second.overloads().begin(); j != i->second.overloads().end(); ++j)
				{
					overload_rep o = *j;
					o.add_offset(binfo.pointer_offset);
					m.add_overload(o);
				}
			}

			// import all getters from the base
			for (std::map<const char*, callback, ltstr>::const_iterator i = bcrep->m_getters.begin(); i != bcrep->m_getters.end(); ++i)
			{
#ifndef LUABIND_DONT_COPY_STRINGS
				m_strings.push_back(dup_string(i->first));
				callback& m = m_getters[m_strings.back()];
#else
				callback& m = m_getters[i->first];
#endif
				m.pointer_offset = i->second.pointer_offset + binfo.pointer_offset;
				m.func = i->second.func;
			}

			// import all setters from the base
			for (std::map<const char*, callback, ltstr>::const_iterator i = bcrep->m_setters.begin(); i != bcrep->m_setters.end(); ++i)
			{
#ifndef LUABIND_DONT_COPY_STRINGS
				// TODO: optimize this by not copying the string if it already exists in m_setters.
				// This goes for m_getters, m_static_constants and m_functions too. Both here
				// in add_base() and in the add_function(), add_getter() ... functions.
				m_strings.push_back(dup_string(i->first));
				callback& m = m_setters[m_strings.back()];
#else
				callback& m = m_setters[i->first];
#endif
				m.pointer_offset = i->second.pointer_offset + binfo.pointer_offset;
				m.func = i->second.func;
			}

			// import all static constants
			for (std::map<const char*, int, ltstr>::const_iterator i = bcrep->m_static_constants.begin(); i != bcrep->m_static_constants.end(); ++i)
			{
#ifndef LUABIND_DONT_COPY_STRINGS
				m_strings.push_back(dup_string(i->first));
				int& v = m_static_constants[m_strings.back()];
#else
				int& v = m_static_constants[i->first];
#endif
				v = i->second;
			}

			// import all operators
			for (int i = 0; i < number_of_operators; ++i)
			{
				for (std::vector<operator_callback>::const_iterator j = bcrep->m_operators[i].begin(); j != bcrep->m_operators[i].end(); ++j)
					m_operators[i].push_back(*j);
			}

			// also, save the baseclass info to be used for typecasts
			m_bases.push_back(binfo);
		}


		inline const std::vector<base_info>& bases() const throw() { return m_bases; }
		inline LUABIND_TYPE_INFO type() const throw() { return m_type; }
		inline void set_type(LUABIND_TYPE_INFO t) { m_type = t; }

		inline void add_function(const char* name, const overload_rep& o)
		{

#ifdef LUABIND_DONT_COPY_STRINGS
			detail::method_rep& method = m_methods[name];
			method.name = name;
#else
			m_strings.push_back(dup_string(name));
			detail::method_rep& method = m_methods[m_strings.back()];
			method.name = m_strings.back();
#endif

			method.add_overload(o);
			method.crep = this;
		}

		inline void add_constructor(const detail::construct_rep::overload_t& o)
		{
			m_constructor.overloads.push_back(o);
		}

		inline void add_wrapped_constructor(const detail::construct_rep::overload_t& o)
		{
			m_wrapped_constructor.overloads.push_back(o);
		}

		inline const char* name() const throw()
		{
#ifdef LUABIND_DONT_COPY_STRINGS
			return m_name;
#else
			return m_name.c_str();
#endif
		}

		inline void add_getter(const char* name, const boost::function2<int, lua_State*, int>& g)
		{
			callback c;
			c.func = g;
			c.pointer_offset = 0;
#ifndef LUABIND_DONT_COPY_STRINGS
			m_strings.push_back(dup_string(name));
			m_getters[m_strings.back()] = c;
#else
			m_getters[name] = c;
#endif
		}

		inline void add_setter(const char* name, const boost::function2<int, lua_State*, int>& s)
		{
			callback c;
			c.func = s;
			c.pointer_offset = 0;
#ifndef LUABIND_DONT_COPY_STRINGS
			m_strings.push_back(dup_string(name));
			m_setters[m_strings.back()] = c;
#else
			m_setters[name] = c;
#endif
		}

#ifndef LUABIND_NO_ERROR_CHECKING
		inline void add_operator(lua_State*, int op_id,  int(*func)(lua_State*), int(*matcher)(lua_State*), void(*sig)(lua_State*, std::string&), int arity)
#else
		inline void add_operator(lua_State*, int op_id,  int(*func)(lua_State*), int(*matcher)(lua_State*), int arity)
#endif
		{
			operator_callback o;
			o.set_fun(func);
			o.set_match_fun(matcher);
			o.set_arity(arity);

#ifndef LUABIND_NO_ERROR_CHECKING

			o.set_sig_fun(sig);

#endif
			m_operators[op_id].push_back(o);
		}
		
		// the lua reference to this class_rep
		inline int self_ref() const throw() { return m_self_ref; }

		// the lua reference to the metatable for this class' instances
		inline int metatable_ref() const throw() { return m_instance_metatable; }

		inline int table_ref() const { return m_table_ref; }

		inline void(*destructor() const)(void*) { return m_destructor; }

		inline class_type get_class_type() const { return m_class_type; }

		void add_static_constant(const char* name, int val)
		{
#ifndef LUABIND_DONT_COPY_STRINGS
			m_strings.push_back(dup_string(name));
			m_static_constants[m_strings.back()] = val;
#else
			m_static_constants[name] = val;
#endif
		}

	static inline int super_callback(lua_State* L)
	{
		int args = lua_gettop(L);
		
		object_rep* obj = static_cast<object_rep*>(lua_touserdata(L, lua_upvalueindex(2)));
		class_rep* crep = static_cast<class_rep*>(lua_touserdata(L, lua_upvalueindex(1)));
		class_rep* base = crep->bases()[0].base;

//		std::cout << "__super of " << base->name() << "\n";

		if (base->get_class_type() == class_rep::lua_class)
		{
			if (base->bases().empty())
			{
				obj->set_flags(obj->flags() & ~object_rep::call_super);

				lua_pushstring(L, "super");
				lua_pushnil(L);
				lua_settable(L, LUA_GLOBALSINDEX);
			}
			else
			{
				lua_pushstring(L, "super");
				lua_pushlightuserdata(L, base);
				lua_pushvalue(L, lua_upvalueindex(2));
				lua_pushcclosure(L, super_callback, 2);
				lua_settable(L, LUA_GLOBALSINDEX);
			}

//			std::cout << "  lua_class\n";
			detail::getref(L, base->table_ref());
			lua_pushstring(L, "__init");
			lua_gettable(L, -2);
			lua_insert(L, 1);
			lua_pop(L, 1);

			lua_pushvalue(L, lua_upvalueindex(2));
			lua_insert(L, 2);

			lua_call(L, args + 1, 0);

			// TODO: instead of clearing the global variable "super"
			// store it temporarily in the registry. maybe we should
			// have some kind of warning if the super global is used?
			lua_pushstring(L, "super");
			lua_pushnil(L);
			lua_settable(L, LUA_GLOBALSINDEX);
		}
		else
		{
			obj->set_flags(obj->flags() & ~object_rep::call_super);
//			std::cout << "  cpp_class\n";

			// we need to push some garbage at index 1 to make the construction work
			lua_pushboolean(L, 1);
			lua_insert(L, 1);

			construct_rep* rep = &base->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.begin(), rep->overloads.end(), ambiguous, min_match, match_index, num_params);

#ifdef LUABIND_NO_ERROR_CHECKING

			}

#else
				
			if (!found)
			{
				{
					std::string msg = "no constructor of '";
					msg += base->m_name;
					msg += "' matched the arguments (";
					msg += stack_content_by_name(L, 2);
					msg += ")";
					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}
			else if (ambiguous)
			{
				{
					std::string msg = "call of overloaded constructor '";
					msg += base->m_name;
					msg +=  "(";
					msg += stack_content_by_name(L, 2);
					msg += ")' is ambiguous";
					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}

			// TODO: should this be a warning or something?
/*
			// since the derived class is a lua class
			// it may have reimplemented virtual functions
			// therefore, we have to instantiate the Basewrapper
			// if there is no basewrapper, throw a run-time error
			if (!rep->overloads[match_index].has_wrapped_construct())
			{
				{
					std::string msg = "Cannot derive from C++ class '";
					msg += base->name();
					msg += "'. It does not have a wrapped type";
					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}
*/
#endif

#ifndef LUABIND_NO_EXCEPTIONS

			try
			{

#endif

				if (!rep->overloads[match_index].has_wrapped_construct())
				{
					// if the type doesn't have a wrapped type, use the ordinary constructor
					obj->set_object(rep->overloads[match_index].construct(L));
				}
				else
				{
					// get reference to lua object
					lua_pushvalue(L, lua_upvalueindex(2));
					int ref = detail::ref(L);
					obj->set_object(rep->overloads[match_index].construct_wrapped(L, ref));
				}
				// TODO: is the wrapped type destructed correctly?
				// it should, since the destructor is either the wrapped type's
				// destructor or the base type's destructor, depending on wether
				// the type has a wrapped type or not.
				obj->set_destructor(base->destructor());
				return 0;

#ifndef LUABIND_NO_EXCEPTIONS

			}
			catch(const std::exception& e)
			{
				lua_pushstring(L, e.what());
			}
			catch(const char* s)
			{
				lua_pushstring(L, s);
			}
			catch(...)
			{
				std::string msg = base->m_name;
				msg += "() threw an exception";
				lua_pushstring(L, msg.c_str());
			}
			// can only be reached if an exception was thrown
			lua_error(L);
#endif
		}

		return 0;

	}

	static int lua_settable_dispatcher(lua_State* L)
	{
		class_rep* crep = static_cast<class_rep*>(lua_touserdata(L, 1));
		detail::getref(L, crep->m_table_ref);
		lua_replace(L, 1);
		lua_rawset(L, -3);
		return 0;
	}

	static int construct_lua_class_callback(lua_State* L)
	{
		class_rep* crep = static_cast<class_rep*>(lua_touserdata(L, 1));

		int args = lua_gettop(L);

		// lua stack: crep <arguments>

		lua_newtable(L);
		int ref = detail::ref(L);

		bool has_bases = !crep->bases().empty();
		
		if (has_bases)
		{
			lua_pushstring(L, "super");
			lua_pushvalue(L, 1); // crep
		}

		// lua stack: crep <arguments> "super" crep
		// or
		// lua stack: crep <arguments>


		// if we have a baseclass we set the flag to say that the super has not yet been called
		// we will use this flag later to check that it actually was called from __init()
		int flags = object_rep::lua_class | object_rep::owner | (has_bases ? object_rep::call_super : 0);

		void* obj_ptr = lua_newuserdata(L, sizeof(object_rep));
		new(obj_ptr) object_rep(crep, flags, ref);

		detail::getref(L, crep->metatable_ref());
		lua_setmetatable(L, -2);

		// lua stack: crep <arguments> "super" crep obj_ptr
		// or
		// lua stack: crep <arguments> obj_ptr

		if (has_bases)	lua_pushvalue(L, -1); // obj_ptr
		lua_replace(L, 1); // obj_ptr

		// lua stack: obj_ptr <arguments> "super" crep obj_ptr
		// or
		// lua stack: obj_ptr <arguments>

		if (has_bases)
		{
			lua_pushcclosure(L, super_callback, 2);
			// lua stack: crep <arguments> "super" function
			lua_settable(L, LUA_GLOBALSINDEX);
		}

		// lua stack: crep <arguments>

		lua_pushvalue(L, 1);
		lua_insert(L, 1);

		detail::getref(L, crep->table_ref());
		lua_pushstring(L, "__init");
		lua_gettable(L, -2);

#ifndef LUABIND_NO_ERROR_CHECKING

		// TODO: should this be a run-time error?
		// maybe the default behavior should be to just call
		// the base calss' constructor. We should register
		// the super callback funktion as __init
		if (!lua_isfunction(L, -1))
		{
			{
				std::string msg = crep->name();
				msg += ":__init is not defined";
				lua_pushstring(L, msg.c_str());
			}
			lua_error(L);
		}

#endif

		lua_insert(L, 2); // function first on stack
		lua_pop(L, 1);
		// TODO: lua_call may invoke longjump! make sure we don't have any memory leaks!
		// we don't have any stack objects here
		lua_call(L, args, 0);

#ifndef LUABIND_NO_ERROR_CHECKING

		object_rep* obj = static_cast<object_rep*>(obj_ptr);
		if (obj->flags() & object_rep::call_super)
		{
			lua_pushstring(L, "derived class must call super on base");
			lua_error(L);
		}

#endif

		return 1;
	}

	// called from the metamethod for __index
	// obj is the object pointer
	static int lua_class_gettable(lua_State* L)
	{
		object_rep* obj = static_cast<object_rep*>(lua_touserdata(L, 1));
		class_rep* crep = obj->crep();

#ifndef LUABIND_NO_ERROR_CHECKING

		if (obj->flags() & object_rep::call_super)
		{
			lua_pushstring(L, "derived class must call super on base");
			lua_error(L);
		}

#endif

		detail::getref(L, obj->lua_table_ref());
		lua_pushvalue(L, 2);
		lua_gettable(L, -2);

		if (!lua_isnil(L, -1)) return 1;

		lua_pop(L, 2);

		detail::getref(L, crep->table_ref());
		lua_pushvalue(L, 2);
		lua_gettable(L, -2);

		if (!lua_isnil(L, -1)) return 1;

		lua_pop(L, 2);


		if (lua_isnil(L, 2))
		{
			lua_pushnil(L);
			return 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);

		std::map<const char*, method_rep, ltstr>::iterator i = crep->m_methods.find(key);
		if (i != crep->m_methods.end())
		{
			// the name is a method, return it
			lua_pushlightuserdata(L, &i->second);
			lua_pushcclosure(L, class_rep::function_dispatcher, 1);
			return 1;
		}

		std::map<const char*, class_rep::callback, ltstr>::iterator j = crep->m_getters.find(key);
		if (j != crep->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
	// obj is the object pointer
	static int lua_class_settable(lua_State* L)
	{
		object_rep* obj = static_cast<object_rep*>(lua_touserdata(L, 1));
		class_rep* crep = obj->crep();

#ifndef LUABIND_NO_ERROR_CHECKING

		if (obj->flags() & object_rep::call_super)
		{
			// this block makes sure the std::string is destructed
			// before lua_error is called
			{
				std::string msg = "derived class '";
				msg += crep->name();
				msg += "'must call super on base";
				lua_pushstring(L, msg.c_str());
			}
			lua_error(L);
		}

#endif

		// 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*, class_rep::callback, ltstr>::iterator j = crep->m_setters.find(key);

		if (j == crep->m_setters.end())
		{
			std::map<const char*, class_rep::callback, ltstr>::iterator k = crep->m_getters.find(key);

#ifndef LUABIND_NO_ERROR_CHECKING

			if (k != crep->m_getters.end())
			{
				{
					std::string msg = "cannot set property '";
					msg += crep->name();
					msg += ".";
					msg += key;
					msg += "', because it's read only";
					lua_pushstring(L, msg.c_str());
				}
				lua_error(L);
			}

#endif

			detail::getref(L, obj->lua_table_ref());
			lua_replace(L, 1);
			lua_settable(L, 1);
		}
		else
		{
			// the name is a data member
			j->second.func(L, j->second.pointer_offset);
		}

		return 0;
	}

	static int static_class_gettable(lua_State* L)
	{
		class_rep* crep = static_cast<class_rep*>(lua_touserdata(L, 1));

		if (crep->get_class_type() == class_rep::lua_class)
		{
			detail::getref(L, crep->m_table_ref);
			lua_pushvalue(L, 2);
			lua_gettable(L, -2);
			if (!lua_isnil(L, -1)) return 1;
			else lua_pop(L, 2);
		}

		const char* key = lua_tostring(L, 2);

		std::map<const char*, method_rep, ltstr>::iterator i = crep->m_methods.find(key);
		if (i != crep->m_methods.end())
		{
			// the name is a method, return it
			lua_pushlightuserdata(L, &i->second);
			lua_pushcclosure(L, class_rep::function_dispatcher, 1);
			return 1;
		}

#ifndef LUABIND_NO_ERROR_CHECKING

		std::map<const char*, int, ltstr>::const_iterator j = crep->m_static_constants.find(key);

		if (j != crep->m_static_constants.end())
		{
			lua_pushnumber(L, j->second);
			return 1;
		}

		{
			std::string msg = "no static '";
			msg += key;
			msg += "' in class '";
			msg += crep->name();
			msg += "'";
			lua_pushstring(L, msg.c_str());
		}
		lua_error(L);

#endif

		return 0;
	}

	private:

		// this is a pointer to the type_info structure for
		// this type
		// warning: this may be a problem when using dll:s, since
		// typeid() may actually return different pointers for the same
		// type.
		LUABIND_TYPE_INFO m_type;
		LUABIND_TYPE_INFO m_held_type;

		typedef void*(*extract_ptr_t)(void*);
		extract_ptr_t m_extract_underlying_fun;

		// a list of info for every class this class derives from
		// the information stored here is sufficient to do
		// type casts to the base classes
		std::vector<base_info> m_bases;

		// the class' name (as given when registered to lua with class_)
#ifdef LUABIND_DONT_COPY_STRINGS
		const char* m_name;
#else
		std::string m_name;
#endif

		// contains signatures for all constructors
		construct_rep m_constructor;
		construct_rep m_wrapped_constructor;

		// a reference to this structure itself. Since this struct
		// is kept inside lua (to let lua collect it when lua_close()
		// is called) we need to lock it to prevent collection.
		// the actual reference is not currently used.
		int m_self_ref;

		// a reference to the lua table that represents this class
		// (only used if it is a lua class)
		int m_table_ref;

		// the type of this class.. determines if it's written in c++ or lua
		class_type m_class_type;

		// this is a lua reference that points to the lua table
		// that is to be used as meta table for all instances
		// of this class.
		int m_instance_metatable;

		// ***** the maps below contains all members in this class *****

		// maps method names to a structure with more
		// information about that method.
		// that struct contains the function-signatures
		// for every overload
		std::map<const char*, method_rep, ltstr> m_methods;

#ifndef LUABIND_DONT_COPY_STRINGS
		// this is where the strings that the maps contains
		// pointer to are kept. To make sure they are destructed.
		std::vector<char*> m_strings;
#endif

		struct callback
		{
			boost::function2<int, lua_State*, int> func;
			int pointer_offset;
		};

		// datamembers, some members may be readonly, and
		// only have a getter function
		std::map<const char*, callback, ltstr> m_getters;
		std::map<const char*, callback, ltstr> m_setters;

		struct operator_callback: public overload_rep_base
		{
			inline void set_fun(int (*f)(lua_State*)) { func = f; }
			inline int call(lua_State* L) { return func(L); }
			inline void set_arity(int arity) { m_arity = arity; }

		private:

			int(*func)(lua_State*);
		};

		std::vector<operator_callback> m_operators[number_of_operators]; // the operators in lua

		void(*m_destructor)(void*);

		std::map<const char*, int, ltstr> m_static_constants;
	};


	inline bool is_class_rep(lua_State* L, int index)
	{
		if (lua_getmetatable(L, index) == 0) return false;

		lua_pushstring(L, "__luabind_classrep");
		lua_gettable(L, -2);
		if (lua_toboolean(L, -1))
		{
			lua_pop(L, 2);
			return true;
		}

		lua_pop(L, 2);
		return false;

	}

}}

#endif // LUABIND_CLASS_REP_HPP_INCLUDED

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Engineer
United States United States
Jonathan de Halleux is Civil Engineer in Applied Mathematics. He finished his PhD in 2004 in the rainy country of Belgium. After 2 years in the Common Language Runtime (i.e. .net), he is now working at Microsoft Research on Pex (http://research.microsoft.com/pex).

Comments and Discussions