[C++-sig] Re: Interest in luabind

Daniel Wallin dalwan01 at student.umu.se
Thu Jun 26 14:29:15 CEST 2003


> "Daniel Wallin" <dalwan01 at student.umu.se> writes:
>
> >> "Daniel Wallin" <dalwan01 at student.umu.se> writes:
> >> >> Surely you want to be able to go in both directions,
> >> >> though?  Surely not everyone using lua is interested
> in
> >> >> just speed and not usability?
> >> >
> >> > We probably would like to be able to go in both
> >> > directions.  We also don't want to force the user to
> >> > compile with RTTI turned on
> >>
> >> I'm sure that's a capability some Boost.Python users
> would
> >> appreciate, too.
> >>
> >> > so we currently supply a LUABIND_TYPEID macro to
> >> > overload the typeid calls for a unique id for the
> type.
> >>
> >> This functions something like a specialization?
> >
> > Right. You could do something like:
> >
> > #define LUABIND_TYPE_INFO int
> > #define LUABIND_TYPEID(type) my_type_id<type>::value
>
> I don't see any reason to get macros involved here.  Users
> could just specialize or overload
> boost::luapython::type_id,
> which, in the no-RTTI case, has no no default definition.

Note that we need to store the type_info object, and that's
why we have macros for this. So that the user can change the
type of that object. This wouldn't really work with
specialization.

> >> > This of course causes some problems if we want to
> >> > downcast
> >>
> >> And if you want to support CBD (component based
> >> development, i.e. cross-module conversions), unless you
> >> can somehow get all authors to agree on how to identify
> >> types.  Well, I guess they could use strings and
> manually
> >> supply what std::type_info::name() does.
> >
> > It doesn't seem wrong that the authors need to use the
> > same typeid. If you aren't developing a closed system,
> > don't change typeid system.
>
> I was thinking more of id collisions among extensions
> which
> don't intend to share types.  It becomes less of an issue
> if people use string ids.

But like I said, if you intend to use your module with other
modules; don't change the typeid system. I don't like
forcing the id type, if you want to use int's I think you
should be allowed to.

>
> >> > so we would need to be able to turn downcasting off,
> or
> >> > let the user supply their own RTTI-system somehow.
> >>
> >> That's pretty straightforward, fortunately.
> >>
> >> We need to be careful about what kinds of
> >> reconfigurability is available through macros.
> >> Extensions linked to the same shared library all share
> a
> >> link symbol space and thus are subject to ODR problems.
> >
> > Right. We currently have quite a few configuration
> macros.
> >
> > LUABIND_MAX_ARITY
> > LUABIND_MAX_BASES
>
> It's pretty easy to avoid these causing any real-world ODR
> problems.

Right. They are only used to control the number of template
parameters to a few metaprogramming struct's.

>
> > LUABIND_NO_ERROR_CHECKING
>
> What kind of error checking gets turned off?

Error checking when performing overload matching. In
particular it turns off the pretty error messages.

>
> > LUABIND_DONT_COPY_STRINGS
>
> What kind of string copying gets disabled?

It causes names to be held by const char* instead of
std::string. (class names, function names etc).

> > . and the typeid macros.
> >
> > Most are results of user requests. Massive configuration
> > is quite important to our users, since lua is used alot
> on
> > embedded systems.
>
> Have your users come back after getting these features and
> given you any feedback about how much performance they've
> gained or space they've saved?

No. But some of them is a must-have for alot of developers.
In particular the ability to turn off exception handling.

> >> What's the significance of a pointer-to-auto_ptr?  I'd
> >> understand what you meant if you wrote:
> >>
> >>      void f(auto_ptr<A>);
> >>
> >> instead.  I'm going to assume that's what you meant.
> >
> > Yeah, that's what I meant. I'm lazy and copy-pasted and
> > forgot to remove the *. :)
> >
> >>
> >> > It is very useful when wrapping interfaces which
> expects the
> >> > user to create objects and give up ownership.
> >>
> >> Sure, great.  It's a function-call-oriented thing.
> Before
> >> you object, read on.
> >>
> >> >> Hmm, this is really very specific to calls, because
> it
> >> >> *does* manage arguments and return values.  I really
> >> >> think CallPolicy is better.  In any case I think we
> >> >> should converge on this, one way or another; there
> >> >> will be more languages, and you do want to be able
> to
> >> >> steal my users, right? <wink>.  That'll be a lot
> >> >> easier if they see familiar terminology ;-)
> >> >
> >> > I don't think it's specific to calls, but to all
> >> > conversion of types between the languages. We can use
> >> > policies when fetching values from lua, or when
> calling
> >> > lua functions from c++:
> >> >
> >> > A* stolen_obj =
> object_cast<A*>(get_globals(L)["obj"],
> >> > adopt(result));
> >>
> >> What is result?  A placeholder?
> >
> > Exactly. boost::arg<0> result;
> >
> >>
> >> could be spelled:
> >>
> >>     std::auto_ptr<A> stolen
> >>         = extract<std::auto_ptr<A>
> >> >(get_globals(L)["obj"]);
> >>
> >> in Boost.Python.
> >>
> >> [I assume this means that all of your C++ objects are
> held
> >> within their lua wrappers by pointer.  I went to
> >> considerable lengths to allow them to be held by-value,
> >> though I'm not sure the efficiency gain is worth the
> cost
> >> in flexibility.]
> >
> > Correct. We hold all C++ objects by pointer. I can see
> why
> > it could be interesting to be able to hold objects by
> value
> > though, especially with small objects. I'm not sure it
> would
> > be worth it though, since the user can just provide a
> custom
> > pool allocator if allocation is an issue.
>
> Still costs an extra 4 bytes (gasp!) for the pointer.
> Yeah,
> it was in one of my contract specs, so I had to implement
> it.  Besides, it seemed like fun, but I bet nobody notices
> and I'd be very happy to rip it out and follow your lead
> on
> this.

Need to think about this some more. It's no special case in
BPL though, it's just another type of instance_holder,
correct?

>
> >> > call_function<void>(L, "f", stolen_obj) [ adopt(_1)
> ];
> >>
> >> That's spelled:
> >>
> >>   call_function<void>(L, "f",
> std::auto_ptr<A>(stolen));
> >
> > Wouldn't both the examples you provided require A to be
> held
> > by auto_ptr<A> in python?
>
> Only the A created by call_function for this callback.
> You
> can have As held any number of ways in the same program.

Ah, right. You just instantiate different instance_holder's.
Does this mean there's special handling of auto_ptr's?
Otherwise, how can you tell which type is being held by the
pointer? (In the call_function example).

>
> >> I can begin to see the syntactic convenience of your
> way,
> >> but I worry about the parameterizability.  In the first
> >> case "result" is the only possible appropriate arg and
> in
> >> the 2nd case it's "_1".
> >
> > What exactly do you worry about? That someone would use
> > the wrong placeholder? I think the intended use is quite
> > clear.
>
> I guess I'm just a worrywart.  Do the other policies have
> similar broad applicability?  If so, I'm inclined to
> accept
> that you have the right idea.

Most conversion policies have double direction, and even the
ones that doesn't can still be used in different contexts.
('result' when doing C++ -> lua, and '_N' when doing lua ->
C++).

> >> Are you familiar with the problems of multimethod
> >> dispatching?  Google can help.
> >
> > I wasn't no, and google didn't seem to like me. :)
>
> Did my links help?

A little. But I don't feel even remotly comfortable on the
subject. :)

>
> >> > We just increase the match value by one for every
> >> > casting step that is needed for converting the types.
> >>
> >> That seems to work for all the trivial cases, but the
> >> problem is always phrased in more-complicated terms, I
> >> presume for a reason.  See http://tinyurl.com/f5t6
> >>
> >> One example of a place where it might not work is:
> >>
> >> struct B {}; struct D : B {};
> >>
> >> void f(B*, python::list)
> >> void f(D*, std::vector<int>)
> >>
> >>    >>> f(D(), [1, 2, 3])
> >>
> >> I want this to choose the first overload, since it
> >> requires only lvalue conversions.
> >
> > rvalue conversions could always give much larger errors
> > than lvalue conversions. This would solve some cases,
> but
> > it isn't clear to me what you want. Do you always want
> to
> > choose the first overload, no matter how many lvalue
> > conversions it involves?
> >
> > void f(B*, B*, B*, python::list)
> > void f(D*, D*, D*, std::vector<int>)
> >
> > f(D(), D(), D(), [1, 2, 3])
>
> Yeah, I guess that looks right, but I don't know.  At some
> point things are just ambiguous.

Exactly. Perhaps introducing weights on conversions might
increase the ambiguity for the user, since it's hard to tell
at which point the overload system will turn around and
choose another overload. Does this make any sense?

>
> > To me it's sufficient to have a really simple overload
> > system, and if that doesn't work the user can register
> the
> > overloads with different names.
>
> Hmm.  I like your philosophy.
>
> As a last resort, let me ask someone I know who's done a
> lot
> of multimethod dispatching stuff if there's any reason to
> do
> something more complicated.

Great, do that.

--
Daniel Wallin




More information about the Cplusplus-sig mailing list