Boost logo

Boost :

From: Jonathan Turkanis (technews_at_[hidden])
Date: 2005-01-30 14:28:39


christopher diggins wrote:
> ----- Original Message -----
> From: "Jonathan Turkanis" <technews_at_[hidden]>

>> Cloning must be introduced at some point. I've been trying to find a
>> way to make cloning just another function, but am becoming convinced it's sui
>> generis. Since Thortsen uses cloning for his Smart Containers library, I
think I'll
>> just borrow concepts and machinery from him.
>>
>> I'm not sure what you mean by "clone(interface_value)", though.
>
> I meant Clone() should be a global function instead of a member
> function to avoid namespace clashes.

You mean it should be defined in the global namespace? Defining stuff in the
global namespace *causes* clashes.

> I am not sure I understand what you mean by making cloning as just
> another function.

I'm not sure what I mean either. I guess I was hoping that a natural and elegant
treatment of cloning would appear magically. :-)

> I also don't think Clone is unique. Clone is
> essentially a call to the copy constructor of the object,

This doesn't work for polymorphic classes. For instance, you might be given an
object as a pointer or reference to an abstract class. More generally, you
typically want to call the copy constructor of the *most-derived* class, but you
may be given a pointer or reference to a base class. And you might want to call
a member function clone() even if the static type has an accessible copy
constructor.

As a result, a more general treatment, like Thorsten's, is needed.

The same problem exists for destructors, but there is a built-in language
mechanism to solve the problem: the virtual destructor. So we adopt the
convention that an object of a class without a virtual destructor should never
be passed around as a pointer to a base class.

> So I would propose (renaming Clone to new_copy):

I prefer clone(), since it is an established name.

> AnyInterface i = AnyObject;
> //...
> if (has_copy_ctor(i) && has_dtor(i)) {
> manual_ptr<AnyInterface> p = new_copy(i);
> }

The problem here is that there's no portable way to tell whether a type has an
accessible destructor or copy constructor. With the smart pointer templates, the
burden is on the user: you can't bind a dynamically allocated object to a smart
pointer unless the static type of the object has an accessible destructor or or
unless you're using shared_ptr with a custom deleter.

Similarly, we need a way to put the burden on the user for clonability. Daniel
James suggested having a special clone function which could be declared in an
interface:

     BOOST_IDL_BEGIN(IBar)
          BOOST_IDL_CLONE()
          ...
     BOOST_IDL_END(IBar)

> also:
>
> AnyInterface i = AnyObject;
> //...
> if (has_default_ctor(i) && has_dtor(i)) {
> manual_ptr<AnyInterface> p = new_default_ctor(i);
> }

Here, again, unless you know how to determine whether an object has a trivial
default constructor, we'd have to put the burden on the user. E.g.,

     BOOST_IDL_BEGIN(IBar)
          BOOST_IDL_DEFAULT_CTOR()
          ...
     BOOST_IDL_END(IBar)

This case doesn't seem as useful as clone, IMO.

Also, it's important to note that adding more than a tightly controlled handful
of functions to interface tables can lead to code bloat, even when these
functions are not used.

> Both pieces of code above are extremely useful, especially if we want
> interfaces to have the same expressive power as boost::any.

I don't think Boost.Any ever assumes that user defined types are default
constructible.

> I am
> currently redesigning my Object class in the OOTL, and if I have the
> two above functions, it means that my objects no longer need any
> special macros. (Remember OOTL_DEF_OBJECT?).

Okay, I'm open to suggestions on how to implement this.

>> You could determine this stuff (to the extent the language allows)
>> at the time of binding, and make it available at runtime.
>
> Do you mean by adding functions to the class?

I meant you could determine the results of a bunch of boolean traits, and store
them as flags in the interface table. However, I didn't understand why you
wanted this information.

> I want to automate the
> process somewhat and remove my dependence on my stupid
> DEF_OOTL_OBJECT macro. If you don't provide this functionality within
> interface,

I'll certainly implement cloning, one way or another. I'm not yet convinced of
the value of default construction, though.

> then I would like to know how to create new interface
> types which do that. I would like to be able to create a new
> interface types, which interject new functions into the function tables.
> I think this ability (to inherit from interface types and interject new
> functions into function tables) should be part of the interface functionality.

There are two problems:

- it's hard to see how to allow users to customize the interface infrastructure,
since it's macro-based. There's just no place to stick policies
- some of this functionality can only be made available for a subset of the
classes whose instances a user might want to bind to an interface. Different
user actions trigger different subsets of functionailty. It's hard to see how to
make this into an extensible framework.

The template-based IDL will make this stuff easier.

>> However, most of this stuff is
>> useful only at compile time.
>
> I showed above how the first two are useful at run-time. size_of() is
> useful when writing code which is type-checked at run-time. It is
> important for making dynamically typed algorithms more efficient. The
> other two, I can't think of applications yet.

Could you give an example?

>>>> I'm thinking of adding a template boost::interfaces::any, which has
>>>> (I think boost::interfaces::null might be a better name for
>>>> IAnything.)
>>>
>>> How about IUnknown? It will be especially meaningful when
>>> boost::interfaces allow dynamic introspection.
>>
>> Even when reflection is added, you'll only be able to query the functions of
>> the interface; you won't be able to bind the underlying object to an
>> interface discovered at runtime -- unless this functionality is
>> built into the class of
>> the bound object.
>
> I realize that, but code like:
>
> int x = extract<int>(null);
>
> would surprise the reader that it works.

It wouldn't work. I was suggesting null as the name of a type, not an object.

> The names that I like:
>
> IEmpty
> IAnything
> IUnknown
> INull

I'm not sure the name matters much, because it would usually be left
unspecified:

     template<typename Interface = null>
     class any;

    std::string s = "hello";
    any<> a = s;
    any<iterator> it = s.begin();

Also, while I've been using the microsoft interface naming convention for
example interfaces, it violates the Boost guidelines for library classes.

Jonathan


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk