
christopher diggins wrote:
----- Original Message ----- From: "Jonathan Turkanis" <technews@kangaroologic.com>
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