cppbind: More refactoring and finally getting rid of boost.preprocessor

This commit is contained in:
Thomas Geymayer 2018-01-19 09:07:07 +01:00
parent 5a72a7d9f4
commit 5f8f5a1c33
15 changed files with 267 additions and 214 deletions

View File

@ -189,10 +189,10 @@ namespace canvas
if( hfw.empty() )
return -1;
naContext c = naNewContext();
try
{
return hfw(nasal::to_nasal(c, const_cast<NasalWidget*>(this)), w);
nasal::Context ctx;
return hfw(ctx.to_me(const_cast<NasalWidget*>(this)), w);
}
catch( std::exception const& ex )
{
@ -202,7 +202,6 @@ namespace canvas
"NasalWidget.heightForWidth: callback error: '" << ex.what() << "'"
);
}
naFreeContext(c);
return -1;
}
@ -262,8 +261,8 @@ namespace canvas
try
{
nasal::Context c;
_set_geometry(nasal::to_nasal(c, this), rect);
nasal::Context ctx;
_set_geometry(ctx.to_me(this), rect);
_flags &= ~LAYOUT_DIRTY;
}
catch( std::exception const& ex )

View File

@ -1,10 +1,13 @@
include (SimGearComponent)
set(HEADERS
cppbind_fwd.hxx
Ghost.hxx
NasalCallContext.hxx
NasalContext.hxx
NasalHash.hxx
NasalMe.hxx
NasalMethodHolder.hxx
NasalObject.hxx
NasalObjectHolder.hxx
NasalString.hxx
@ -13,7 +16,6 @@ set(HEADERS
)
set(DETAIL_HEADERS
detail/from_nasal_function_templates.hxx
detail/from_nasal_helper.hxx
detail/nasal_traits.hxx
detail/to_nasal_helper.hxx

View File

@ -56,6 +56,26 @@ namespace nasal
return String(_ctx, str);
}
//----------------------------------------------------------------------------
naRef ContextWrapper::callMethod( Me me,
naRef code,
std::initializer_list<naRef> args )
{
naRef ret = naCallMethodCtx(
_ctx,
code,
me,
args.size(),
const_cast<naRef*>(args.begin()),
naNil() // locals
);
if( const char* error = naGetError(_ctx) )
throw std::runtime_error(error);
return ret;
}
//----------------------------------------------------------------------------
naRef ContextWrapper::newVector(std::initializer_list<naRef> vals)
{

View File

@ -19,13 +19,14 @@
#ifndef SG_NASAL_CONTEXT_HXX_
#define SG_NASAL_CONTEXT_HXX_
#include "from_nasal.hxx"
#include "to_nasal.hxx"
#include "cppbind_fwd.hxx"
#include "NasalMe.hxx"
#include <boost/call_traits.hpp>
#include <initializer_list>
namespace nasal
{
class Hash;
class String;
/**
* Wraps a nasal ::naContext without taking ownership/managing its lifetime
@ -63,12 +64,18 @@ namespace nasal
return nasal::to_nasal(_ctx, arg);
}
template<class T, size_t N>
template<class T, std::size_t N>
naRef to_nasal(const T(&array)[N]) const
{
return nasal::to_nasal(_ctx, array);
}
template<class T>
Me to_me(T arg) const
{
return Me{ to_nasal(arg) };
}
template<class T>
typename from_nasal_ptr<T>::return_type
from_nasal(naRef ref) const
@ -76,6 +83,21 @@ namespace nasal
return (*from_nasal_ptr<T>::get())(_ctx, ref);
}
naRef callMethod(Me me, naRef code, std::initializer_list<naRef> args);
template<class Ret, class... Args>
Ret callMethod( Me me,
naRef code,
typename boost::call_traits<Args>::param_type ... args )
{
// TODO warn if with Ret == void something different to nil is returned?
return from_nasal<Ret>(callMethod(
me,
code,
{ to_nasal<typename boost::call_traits<Args>::param_type>(args)... }
));
}
protected:
naContext _ctx;
@ -100,4 +122,7 @@ namespace nasal
} // namespace nasal
#include "from_nasal.hxx"
#include "to_nasal.hxx"
#endif /* SG_NASAL_CONTEXT_HXX_ */

View File

@ -0,0 +1,44 @@
///@file
//
// Copyright (C) 2018 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_NASAL_ME_HXX_
#define SG_NASAL_ME_HXX_
#include <simgear/nasal/nasal.h>
namespace nasal
{
/**
* Wrap a naRef to indicate it references the self/me object in Nasal method
* calls.
*/
struct Me
{
naRef _ref;
explicit Me(naRef ref = naNil()):
_ref(ref)
{}
operator naRef() { return _ref; }
};
} // namespace nasal
#endif /* SG_NASAL_ME_HXX_ */

View File

@ -0,0 +1,75 @@
///@file
//
// Copyright (C) 2018 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_NASAL_METHOD_HOLDER_HXX_
#define SG_NASAL_METHOD_HOLDER_HXX_
#include "NasalContext.hxx"
#include "NasalObjectHolder.hxx"
namespace nasal
{
/**
* Hold any callable function in Nasal and call it from C++
*/
template<class Ret, class... Args>
class NasalMethodHolder
{
using Holder = ObjectHolder<SGReferenced>;
public:
NasalMethodHolder(naRef code):
_code(Holder::makeShared(code))
{}
/**
* @brief Call the function with the given arguments
*
* If the first argument is nasal::Me it will be passed as 'me' object and
* not as argument.
*/
Ret operator()(Args ... args)
{
return call(args...);
}
private:
Holder::Ref _code;
template<class... CArgs>
Ret call(Me self, CArgs ... args)
{
nasal::Context ctx;
return ctx.callMethod<Ret, CArgs...>(
self,
_code->get_naRef(),
args...
);
}
template<class... CArgs>
Ret call(CArgs ... args)
{
return call(Me{}, args...);
}
};
} // namespace nasal
#endif /* SG_NASAL_METHOD_HOLDER_HXX_ */

View File

@ -52,11 +52,11 @@ namespace nasal
return Ret();
Context ctx;
auto func = get_member<boost::function<Ret (nasal::Me, Args...)>>(
auto func = get_member<boost::function<Ret (Me, Args...)>>(
ctx, _nasal_impl.get_naRef(), name
);
if( func )
return func(nasal::to_nasal(ctx, this), args...);
return func(Me(ctx.to_nasal(this)), args...);
return Ret();
}

View File

@ -42,6 +42,8 @@ namespace nasal
{
public:
using Ref = SGSharedPtr<ObjectHolder<Base>>;
/**
* @param obj Object to save
*/
@ -84,7 +86,7 @@ namespace nasal
*
* @param obj Object to save
*/
static SGSharedPtr<ObjectHolder<Base> > makeShared(naRef obj);
static Ref makeShared(naRef obj);
protected:
naRef _ref;
@ -155,10 +157,10 @@ namespace nasal
//----------------------------------------------------------------------------
template<class Base>
SGSharedPtr<ObjectHolder<Base> >
typename ObjectHolder<Base>::Ref
ObjectHolder<Base>::makeShared(naRef obj)
{
return SGSharedPtr<ObjectHolder<Base> >( new ObjectHolder<Base>(obj) );
return Ref( new ObjectHolder<Base>(obj) );
}
} // namespace nasal

View File

@ -0,0 +1,54 @@
///@file
/// Nasal C++ Bindings forward declarations
///
// Copyright (C) 2018 Thomas Geymayer <tomgey@gmail.com>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef SG_NASAL_CPPBIND_FWD_HXX_
#define SG_NASAL_CPPBIND_FWD_HXX_
#include <simgear/nasal/nasal.h>
#include <cstddef>
namespace nasal
{
class CallContext;
class Context;
class ContextWrapper;
class Hash;
class Me;
class Object;
class String;
template<class, class>
class Ghost;
template<class T>
naRef to_nasal(naContext c, T arg);
template<class T, std::size_t N>
naRef to_nasal(naContext c, const T(&array)[N]);
template<class T>
T from_nasal(naContext c, naRef ref);
template<class Var>
struct from_nasal_ptr;
} // namespace nasal
#endif /* SG_NASAL_CPPBIND_FWD_HXX_ */

View File

@ -1,131 +0,0 @@
#ifndef SG_FROM_NASAL_HELPER_HXX_
# error Nasal cppbind - do not include this file!
#endif
#ifndef SG_DONT_DO_ANYTHING
#define n BOOST_PP_ITERATION()
#ifndef SG_BOOST_FUNCTION_FROM_NASAL_FWD
# define SG_CALL_TRAITS_PARAM(z, n, dummy)\
typename boost::call_traits<A##n>::param_type a##n
# define SG_CALL_ARG(z, n, dummy)\
to_nasal<typename boost::call_traits<A##n>::param_type>(ctx, a##n)
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::disable_if<boost::is_void<Ret>, Ret>::type
callNasalMethod( const ObjectHolder<SGReferenced>* holder,
Me self
BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) )
{
naContext ctx = naNewContext();
#if n
naRef args[n] = {
BOOST_PP_ENUM(n, SG_CALL_ARG, 0)
};
#else
naRef* args = NULL;
#endif
naRef result =
naCallMethodCtx(ctx, holder->get_naRef(), self, n, args, naNil());
const char* error = naGetError(ctx);
std::string error_str(error ? error : "");
Ret r = Ret();
if( !error )
r = from_nasal_helper(ctx, result, static_cast<Ret*>(0));
naFreeContext(ctx);
if( error )
throw std::runtime_error(error_str);
return r;
}
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::enable_if<boost::is_void<Ret>, Ret>::type
callNasalMethod( const ObjectHolder<SGReferenced>* holder,
Me self
BOOST_PP_ENUM_TRAILING(n, SG_CALL_TRAITS_PARAM, 0) )
{
callNasalMethod<
naRef // do not do any conversion and just ignore the return value
// TODO warn if something different to nil is returned?
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_PARAMS(n, A)
>
(
holder,
self
BOOST_PP_ENUM_TRAILING_PARAMS(n, a)
);
}
# undef SG_CALL_TRAITS_PARAM
# undef SG_CALL_ARG
#endif
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::disable_if<
// free function if first argument is not nasal::Me or no argument at all
boost::is_same<BOOST_PP_IF(n, A0, void), Me>,
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
>::type
boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
;
#else
{
return boost::bind
(
&callNasalMethod<Ret BOOST_PP_ENUM_TRAILING_PARAMS(n, A)>,
ObjectHolder<SGReferenced>::makeShared(code),
boost::bind(naNil)
BOOST_PP_COMMA_IF(n)
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
);
}
#endif
#if n > 0
template<
class Ret
BOOST_PP_ENUM_TRAILING_PARAMS(n, class A)
>
typename boost::enable_if<
// method if type of first argument is nasal::Me
boost::is_same<A0, Me>,
boost::function<Ret (BOOST_PP_ENUM_PARAMS(n, A))>
>::type
boostFunctionFromNasal(naRef code, Ret (*)(BOOST_PP_ENUM_PARAMS(n, A)))
#ifdef SG_BOOST_FUNCTION_FROM_NASAL_FWD
;
#else
{
return boost::bind
(
&callNasalMethod<
Ret
BOOST_PP_COMMA_IF(BOOST_PP_DEC(n))
BOOST_PP_ENUM_SHIFTED_PARAMS(n, A)
>,
ObjectHolder<SGReferenced>::makeShared(code),
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(n), _)
);
}
#endif
#endif
#undef n
#endif // SG_DONT_DO_ANYTHING

View File

@ -24,20 +24,15 @@
#include <simgear/math/SGMath.hxx>
#include <simgear/math/SGRect.hxx>
#include <simgear/nasal/nasal.h>
#include <simgear/nasal/cppbind/NasalContext.hxx>
#include <simgear/nasal/cppbind/NasalMe.hxx>
#include <simgear/nasal/cppbind/NasalMethodHolder.hxx>
#include <simgear/nasal/cppbind/NasalObjectHolder.hxx>
#include <simgear/nasal/cppbind/to_nasal.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <boost/bind.hpp>
#include <boost/call_traits.hpp>
#include <boost/function.hpp>
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_trailing.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/repetition/enum_shifted_params.hpp>
#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>
@ -76,27 +71,19 @@ namespace nasal
std::string _msg;
};
/**
* Wrap a naRef to indicate it references the self/me object in Nasal method
* calls.
*/
struct Me
{
naRef _ref;
Me(naRef ref = naNil()):
_ref(ref)
{}
operator naRef() { return _ref; }
};
/**
* Simple pass through for unified handling also of naRef.
*/
inline naRef from_nasal_helper(naContext, naRef ref, const naRef*)
{ return ref; }
/**
* Ignore return value
*/
// TODO show some warning when something is returned but ignored?
inline void from_nasal_helper(naContext, naRef, const void*)
{}
/**
* Convert Nasal string to std::string
*/
@ -125,43 +112,33 @@ namespace nasal
*/
bool from_nasal_helper(naContext c, naRef ref, const bool*);
namespace detail
{
#define SG_BOOST_FUNCTION_FROM_NASAL_FWD
#define BOOST_PP_ITERATION_LIMITS (0, 9)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
#include BOOST_PP_ITERATE()
#undef SG_BOOST_FUNCTION_FROM_NASAL_FWD
}
/**
* Convert a Nasal function to a boost::function with the given signature.
*
* @tparam Sig Signature of returned function (arguments and return value
* are automatically converted using from_nasal/to_nasal)
*/
template<class Sig>
boost::function<Sig>
from_nasal_helper(naContext c, naRef ref, boost::function<Sig>*)
template<class Ret, class... Args>
boost::function<Ret (Args...)>
from_nasal_helper(naContext c, naRef ref, const boost::function<Ret (Args...)>*)
{
if( naIsNil(ref) )
return boost::function<Sig>();
return {};
if( !naIsCode(ref)
&& !naIsCCode(ref)
&& !naIsFunc(ref) )
throw bad_nasal_cast("not a function");
return detail::boostFunctionFromNasal(ref, static_cast<Sig*>(0));
return NasalMethodHolder<Ret, Args...>(ref);
}
template<class Sig>
typename boost::enable_if< boost::is_function<Sig>,
boost::function<Sig>
>::type
from_nasal_helper(naContext c, naRef ref, Sig*)
template<class Ret, class... Args>
boost::function<Ret (Args...)>
from_nasal_helper(naContext c, naRef ref, Ret (*const)(Args...))
{
return from_nasal_helper(c, ref, static_cast<boost::function<Sig>*>(0));
return
from_nasal_helper(c, ref, static_cast<boost::function<Ret (Args...)>*>(0));
}
/**
@ -227,21 +204,6 @@ namespace nasal
return SGRect<T>(vec[0], vec[1], vec[2], vec[3]);
}
// Helpers for wrapping calls to Nasal functions into boost::function
namespace detail
{
// Dummy include to add a build dependency on this file for gcc/CMake/etc.
#define SG_DONT_DO_ANYTHING
# include <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
#undef SG_DONT_DO_ANYTHING
// Now the actual include (we are limited to 8 arguments (+me) here because
// boost::bind has an upper limit of 9)
#define BOOST_PP_ITERATION_LIMITS (0, 8)
#define BOOST_PP_FILENAME_1 <simgear/nasal/cppbind/detail/from_nasal_function_templates.hxx>
#include BOOST_PP_ITERATE()
}
} // namespace nasal
#endif /* SG_FROM_NASAL_HELPER_HXX_ */

View File

@ -21,6 +21,7 @@
#define SG_FROM_NASAL_HXX_
#include <simgear/nasal/cppbind/detail/from_nasal_helper.hxx>
#include <type_traits>
namespace nasal
{
@ -52,8 +53,8 @@ namespace nasal
template<class Var>
struct from_nasal_ptr
{
typedef typename boost::remove_const
< typename boost::remove_reference<Var>::type
typedef typename std::remove_const
< typename std::remove_reference<Var>::type
>::type return_type;
typedef return_type(*type)(naContext, naRef);

View File

@ -42,7 +42,7 @@ class TestContext:
template<class T = naRef>
T exec(const std::string& code)
{
return from_nasal<T>(execImpl(code, {}));
return from_nasal<T>(execImpl(code, nasal::Me{}));
}
template<class T>

View File

@ -218,7 +218,7 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
// passed on to function
typedef boost::function<int (Me, int)> MeIntFunc;
MeIntFunc fmeint = hash.get<MeIntFunc>("func");
BOOST_CHECK_EQUAL(fmeint(naNil(), 5), 5);
BOOST_CHECK_EQUAL(fmeint(Me{}, 5), 5);
//----------------------------------------------------------------------------
// Test exposing classes to Nasal
@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
typedef boost::function<unsigned long (Me)> MemFunc;
MemFunc fGetThis = c.from_nasal<MemFunc>(thisGetter);
BOOST_REQUIRE( fGetThis );
BOOST_CHECK_EQUAL( fGetThis(derived), (unsigned long)d.get() );
BOOST_CHECK_EQUAL( fGetThis(Me{derived}), (unsigned long)d.get() );
BasePtr d2( new DoubleDerived );
derived = c.to_nasal(d2);
@ -369,10 +369,10 @@ BOOST_AUTO_TEST_CASE( cppbind_misc_testing )
BOOST_CHECK_EQUAL( objects[2], d3 );
// Calling fallback setter for unset values
BOOST_CHECK_EQUAL( c.exec<int>("me.test = 3;", derived), 3 );
BOOST_CHECK_EQUAL( c.exec<int>("me.test = 3;", Me{derived}), 3 );
// Calling generic (fallback) getter
BOOST_CHECK_EQUAL( c.exec<std::string>("var a = me.get_test;", derived),
BOOST_CHECK_EQUAL( c.exec<std::string>("var a = me.get_test;", Me{derived}),
"generic-get" );
//----------------------------------------------------------------------------

View File

@ -218,13 +218,13 @@ BOOST_AUTO_TEST_CASE( bind_methods )
TestContext ctx;
auto test = boost::make_shared<TestClass>();
ctx.exec("me.set(1, \"s2\", \"s3\", 4);", ctx.to_nasal(test));
ctx.exec("me.set(1, \"s2\", \"s3\", 4);", ctx.to_me(test));
BOOST_CHECK_EQUAL(test->arg1, 1);
BOOST_CHECK_EQUAL(test->arg2, "s2");
BOOST_CHECK_EQUAL(test->arg3, "s3");
BOOST_CHECK_EQUAL(test->arg4, 4);
ctx.exec("me.setReverse(1, \"s2\", \"s3\", 4);", ctx.to_nasal(test));
ctx.exec("me.setReverse(1, \"s2\", \"s3\", 4);", ctx.to_me(test));
BOOST_CHECK_EQUAL(test->arg1, 4);
BOOST_CHECK_EQUAL(test->arg2, "s3");
BOOST_CHECK_EQUAL(test->arg3, "s2");